Skip to content

Commit b68d3b8

Browse files
committed
added support for manipulating and writing schema files
fixes #18
1 parent 83cec26 commit b68d3b8

File tree

8 files changed

+184
-14
lines changed

8 files changed

+184
-14
lines changed

README.md

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# php-openapi
22

3-
READ [OpenAPI](https://www.openapis.org/) 3.0.x YAML and JSON files and make the content accessible in PHP objects.
3+
Read and write [OpenAPI](https://www.openapis.org/) 3.0.x YAML and JSON files and make the content accessible in PHP objects.
44

55
It also provides a CLI tool for validating and converting OpenAPI 3.0.x YAML and JSON files.
66

@@ -19,7 +19,7 @@ It also provides a CLI tool for validating and converting OpenAPI 3.0.x YAML and
1919

2020
## Used by
2121

22-
This library provides a low level API for reading OpenAPI files. It is used by higher level tools to
22+
This library provides a low level API for reading and writing OpenAPI files. It is used by higher level tools to
2323
do awesome work:
2424

2525
- https://github.com/cebe/yii2-openapi Code Generator for REST API from OpenAPI spec, includes fake data generator.
@@ -105,6 +105,44 @@ foreach($openapi->paths as $path => $definition) {
105105
Object properties are exactly like in the [OpenAPI specification](https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#openapi-specification).
106106
You may also access additional properties added by specification extensions.
107107

108+
### Writing Specification files
109+
110+
```php
111+
// create base description
112+
$openapi = new \cebe\openapi\spec\OpenApi([
113+
'openapi' => '3.0.2',
114+
'info' => [
115+
'title' => 'Test API',
116+
'version' => '1.0.0',
117+
],
118+
'paths' => [],
119+
]);
120+
// manipulate description as needed
121+
$openapi->paths['/test'] = new \cebe\openapi\spec\PathItem([
122+
'description' => 'something'
123+
]);
124+
// ...
125+
126+
$json = \cebe\openapi\Writer::writeToJson($openapi);
127+
```
128+
129+
results in the following JSON data:
130+
131+
```json
132+
{
133+
"openapi": "3.0.0",
134+
"info": {
135+
"title": "Test API",
136+
"version": "1.0.0"
137+
},
138+
"paths": {
139+
"/test": {
140+
"description": "something"
141+
}
142+
}
143+
}
144+
```
145+
108146
### Reading Specification Files and Resolving References
109147

110148
In the above we have passed the raw JSON or YAML data to the Reader. In order to be able to resolve

src/SpecBaseObject.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public function __get($name)
265265

266266
public function __set($name, $value)
267267
{
268-
throw new ReadonlyPropertyException('Setting read-only property: ' . \get_class($this) . '::' . $name);
268+
$this->_properties[$name] = $value;
269269
}
270270

271271
public function __isset($name)
@@ -279,7 +279,7 @@ public function __isset($name)
279279

280280
public function __unset($name)
281281
{
282-
throw new ReadonlyPropertyException('Unsetting read-only property: ' . \get_class($this) . '::' . $name);
282+
unset($this->_properties[$name]);
283283
}
284284

285285
/**

src/spec/Callback.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,30 @@ public function getUrl()
5858
return $this->_url;
5959
}
6060

61+
/**
62+
* @param string $url
63+
*/
64+
public function setUrl(string $url): void
65+
{
66+
$this->_url = $url;
67+
}
68+
6169
/**
6270
* @return PathItem
6371
*/
64-
public function getRequest()
72+
public function getRequest(): ?PathItem
6573
{
6674
return $this->_pathItem;
6775
}
6876

77+
/**
78+
* @param PathItem $request
79+
*/
80+
public function setRequest(?PathItem $request): void
81+
{
82+
$this->_pathItem = $request;
83+
}
84+
6985
/**
7086
* Validate object data according to OpenAPI spec.
7187
* @return bool whether the loaded data is valid according to OpenAPI spec

src/spec/Encoding.php

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ class Encoding extends SpecBaseObject
3030
protected function attributes(): array
3131
{
3232
return [
33-
// TODO implement default values for contentType
34-
// https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#encodingObject
3533
'contentType' => Type::STRING,
3634
'headers' => [Type::STRING, Header::class],
3735
// TODO implement default values for style
@@ -42,16 +40,49 @@ protected function attributes(): array
4240
];
4341
}
4442

43+
private $_attributeDefaults = [];
44+
45+
/**
46+
* @return array array of attributes default values.
47+
*/
48+
protected function attributeDefaults(): array
49+
{
50+
return $this->_attributeDefaults;
51+
}
52+
4553
/**
4654
* Create an object from spec data.
4755
* @param array $data spec data read from YAML or JSON
4856
* @throws TypeErrorException in case invalid data is supplied.
4957
*/
50-
public function __construct(array $data)
58+
public function __construct(array $data, ?Schema $schema = null)
5159
{
52-
if (!isset($data['explode']) && isset($data['style'])) {
60+
if (isset($data['style'])) {
5361
// Spec: When style is form, the default value is true.
54-
$data['explode'] = ($data['style'] === 'form');
62+
$this->_attributeDefaults['explode'] = ($data['style'] === 'form');
63+
}
64+
if ($schema !== null) {
65+
// Spec: Default value depends on the property type:
66+
// for string with format being binary – application/octet-stream;
67+
// for other primitive types – text/plain;
68+
// for object - application/json;
69+
// for array – the default is defined based on the inner type.
70+
switch ($schema->type === 'array' ? ($schema->items->type ?? 'array') : $schema->type) {
71+
case Type::STRING:
72+
if ($schema->format === 'binary') {
73+
$this->_attributeDefaults['contentType'] = 'application/octet-stream';
74+
break;
75+
}
76+
// no break here
77+
case Type::BOOLEAN:
78+
case Type::INTEGER:
79+
case Type::NUMBER:
80+
$this->_attributeDefaults['contentType'] = 'text/plain';
81+
break;
82+
case 'object':
83+
$this->_attributeDefaults['contentType'] = 'application/json';
84+
break;
85+
}
5586
}
5687
parent::__construct($data);
5788
}

src/spec/MediaType.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace cebe\openapi\spec;
99

10+
use cebe\openapi\exceptions\TypeErrorException;
1011
use cebe\openapi\SpecBaseObject;
1112

1213
/**
@@ -34,6 +35,27 @@ protected function attributes(): array
3435
];
3536
}
3637

38+
/**
39+
* Create an object from spec data.
40+
* @param array $data spec data read from YAML or JSON
41+
* @throws TypeErrorException in case invalid data is supplied.
42+
*/
43+
public function __construct(array $data)
44+
{
45+
// instantiate Encoding by passing the schema for extracting default values
46+
$encoding = $data['encoding'] ?? null;
47+
unset($data['encoding']);
48+
49+
parent::__construct($data);
50+
51+
if (!empty($encoding)) {
52+
foreach($encoding as $property => $encodingData) {
53+
$encoding[$property] = new Encoding($encodingData, $this->schema->properties[$property] ?? null);
54+
}
55+
$this->encoding = $encoding;
56+
}
57+
}
58+
3759
/**
3860
* Perform validation on this object, check data against OpenAPI Specification rules.
3961
*/

src/spec/Paths.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,23 @@ public function getPath(string $name): ?PathItem
8484
return $this->_paths[$name] ?? null;
8585
}
8686

87+
/**
88+
* @param string $name path name
89+
* @param PathItem $pathItem the path item to add
90+
*/
91+
public function addPath(string $name, PathItem $pathItem): void
92+
{
93+
$this->_paths[$name] = $pathItem;
94+
}
95+
96+
/**
97+
* @param string $name path name
98+
*/
99+
public function removePath(string $name): void
100+
{
101+
unset($this->_paths[$name]);
102+
}
103+
87104
/**
88105
* @return PathItem[]
89106
*/
@@ -163,7 +180,7 @@ public function offsetGet($offset)
163180
*/
164181
public function offsetSet($offset, $value)
165182
{
166-
throw new ReadonlyPropertyException('Setting read-only property: ' . \get_class($this) . '::' . $offset);
183+
$this->addPath($offset, $value);
167184
}
168185

169186
/**
@@ -174,7 +191,7 @@ public function offsetSet($offset, $value)
174191
*/
175192
public function offsetUnset($offset)
176193
{
177-
throw new ReadonlyPropertyException('Unsetting read-only property: ' . \get_class($this) . '::' . $offset);
194+
$this->removePath($offset);
178195
}
179196

180197
/**

src/spec/Responses.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,23 @@ public function getResponse($statusCode)
8484
return $this->_responses[$statusCode] ?? null;
8585
}
8686

87+
/**
88+
* @param string $statusCode HTTP status code
89+
* @param Response|Reference $response
90+
*/
91+
public function addResponse($statusCode, $response): void
92+
{
93+
$this->_responses[$statusCode] = $response;
94+
}
95+
96+
/**
97+
* @param string $statusCode HTTP status code
98+
*/
99+
public function removeResponse($statusCode)
100+
{
101+
unset($this->_responses[$statusCode]);
102+
}
103+
87104
/**
88105
* @return Response[]|Reference[]
89106
*/
@@ -159,7 +176,7 @@ public function offsetGet($offset)
159176
*/
160177
public function offsetSet($offset, $value)
161178
{
162-
throw new ReadonlyPropertyException('Setting read-only property: ' . \get_class($this) . '::' . $offset);
179+
$this->addResponse($offset, $value);
163180
}
164181

165182
/**
@@ -170,7 +187,7 @@ public function offsetSet($offset, $value)
170187
*/
171188
public function offsetUnset($offset)
172189
{
173-
throw new ReadonlyPropertyException('Unsetting read-only property: ' . \get_class($this) . '::' . $offset);
190+
$this->removeResponse($offset);
174191
}
175192

176193
/**

tests/WriterTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,35 @@ public function testWriteJson()
3535
);
3636
}
3737

38+
public function testWriteJsonMofify()
39+
{
40+
$openapi = $this->createOpenAPI();
41+
42+
$openapi->paths['/test'] = new \cebe\openapi\spec\PathItem([
43+
'description' => 'something'
44+
]);
45+
46+
$json = \cebe\openapi\Writer::writeToJson($openapi);
47+
48+
$this->assertEquals(preg_replace('~\R~', "\n", <<<JSON
49+
{
50+
"openapi": "3.0.0",
51+
"info": {
52+
"title": "Test API",
53+
"version": "1.0.0"
54+
},
55+
"paths": {
56+
"\/test": {
57+
"description": "something"
58+
}
59+
}
60+
}
61+
JSON
62+
),
63+
$json
64+
);
65+
}
66+
3867
public function testWriteYaml()
3968
{
4069
$openapi = $this->createOpenAPI();

0 commit comments

Comments
 (0)