Skip to content

Commit ebb36c2

Browse files
committed
implement Schema
1 parent a306a1c commit ebb36c2

10 files changed

Lines changed: 246 additions & 13 deletions

File tree

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ all:
44
style:
55
vendor/bin/indent --tabs composer.json
66

7+
test:
8+
vendor/bin/phpunit
9+

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,12 @@ This library is currently work in progress, the following list tracks completene
7272
- [ ] Header Object
7373
- [ ] Tag Object
7474
- [ ] Reference Object
75-
- [ ] Schema Object
76-
- [ ] Discriminator Object
77-
- [ ] XML Object
75+
- [x] Schema Object
76+
- [x] load/read
77+
- [ ] `additionalProperties` field
78+
- [ ] validation
79+
- [x] Discriminator Object
80+
- [x] XML Object
7881
- [ ] Security Scheme Object
7982
- [ ] OAuth Flows Object
8083
- [ ] OAuth Flow Object

src/SpecBaseObject.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,14 @@ public function __construct(array $data)
4040
continue;
4141
}
4242

43-
if ($type === 'string') {
43+
if ($type === 'string' || $type === 'any') {
4444
$this->_properties[$property] = $data[$property];
45+
} elseif ($type === 'boolean') {
46+
if (!is_bool($data[$property])) {
47+
$this->_errors[] = "property '$property' must be boolean, but " . gettype($data[$property]) . " given.";
48+
continue;
49+
}
50+
$this->_properties[$property] = (bool) $data[$property];
4551
} elseif (is_array($type)) {
4652
if (!is_array($data[$property])) {
4753
$this->_errors[] = "property '$property' must be array, but " . gettype($data[$property]) . " given.";

src/spec/Components.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ protected function performValidation()
5555
// All the fixed fields declared above are objects that MUST use keys that match the regular expression: ^[a-zA-Z0-9\.\-_]+$.
5656
foreach (array_keys($this->attributes()) as $attribute) {
5757
foreach($this->$attribute as $k => $v) {
58-
if (!preg_match('^[a-zA-Z0-9\.\-_]+$', $k)) {
58+
if (!preg_match('~^[a-zA-Z0-9\.\-_]+$~', $k)) {
5959
$this->addError("Invalid key '$k' used in Components Object for attribute '$attribute', does not match ^[a-zA-Z0-9\.\-_]+\$.");
6060
}
6161
}

src/spec/Discriminator.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace cebe\openapi\spec;
4+
5+
use cebe\openapi\SpecBaseObject;
6+
7+
/**
8+
* When request bodies or response payloads may be one of a number of different schemas, a discriminator object can be used to aid in serialization, deserialization, and validation.
9+
*
10+
* @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#discriminatorObject
11+
*
12+
* @property-read string $propertyName
13+
* @property-read string[] $ mapping
14+
*
15+
* @author Carsten Brandt <mail@cebe.cc>
16+
*/
17+
class Discriminator extends SpecBaseObject
18+
{
19+
/**
20+
* @return array array of attributes available in this object.
21+
*/
22+
protected function attributes(): array
23+
{
24+
return [
25+
'propertyName' => 'string',
26+
' mapping' => ['string', 'string'],
27+
];
28+
}
29+
30+
/**
31+
* Perform validation on this object, check data against OpenAPI Specification rules.
32+
*
33+
* Call `addError()` in case of validation errors.
34+
*/
35+
protected function performValidation()
36+
{
37+
$this->requireProperties(['propertyName']);
38+
}
39+
}

src/spec/Schema.php

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,83 @@
1717
*
1818
* @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#schemaObject
1919
*
20+
* @property-read string $title
21+
* @property-read int|float $multipleOf
22+
* @property-read int|float $maximum
23+
* @property-read int|float $exclusiveMaximum
24+
* @property-read int|float $minimum
25+
* @property-read int|float $exclusiveMinimum
26+
* @property-read int $maxLength
27+
* @property-read int $minLength
28+
* @property-read string $pattern (This string SHOULD be a valid regular expression, according to the [ECMA 262 regular expression dialect](https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.5))
29+
* @property-read int $maxItems
30+
* @property-read int $minItems
31+
* @property-read bool $uniqueItems
32+
* @property-read int $maxProperties
33+
* @property-read int $minProperties
34+
* @property-read string[] $required list of required properties
35+
* @property-read array $enum
36+
*
37+
* @property-read string $type
38+
* @property-read Schema[] $allOf
39+
* @property-read Schema[] $oneOf
40+
* @property-read Schema[] $anyOf
41+
* @property-read Schema|null $not
42+
* @property-read Schema|null $items
43+
* @property-read Schema[] $properties
44+
* @property-read string $description
45+
* @property-read string $format
46+
* @property-read mixed $default
47+
*
48+
* @property-read bool $nullable
49+
* @property-read Discriminator|null $discriminator
50+
* @property-read bool $readOnly
51+
* @property-read bool $writeOnly
52+
* @property-read Xml|null $xml
53+
* @property-read ExternalDocumentation|null $externalDocs
54+
* @property-read mixed $example
55+
* @property-read bool $deprecated
56+
*
2057
* @author Carsten Brandt <mail@cebe.cc>
2158
*/
22-
class Schema
59+
class Schema extends SpecBaseObject
2360
{
24-
// TODO implement
61+
/**
62+
* @return array array of attributes available in this object.
63+
*/
64+
protected function attributes(): array
65+
{
66+
return [
67+
'type' => 'string',
68+
'allOf' => [Schema::class], // TODO allow reference
69+
'oneOf' => [Schema::class],
70+
'anyOf' => [Schema::class],
71+
'not' => Schema::class,
72+
'items' => Schema::class,
73+
'properties' => ['string', Schema::class],
74+
//'additionalProperties' => 'boolean' | ['string', Schema::class], // TODO can be bool?
75+
'description' => 'string',
76+
'format' => 'string',
77+
'default' => 'any',
78+
79+
'nullable' => 'boolean',
80+
'discriminator' => Discriminator::class,
81+
'readOnly' => 'boolean',
82+
'writeOnly' => 'boolean',
83+
'xml' => Xml::class,
84+
'externalDocs' => ExternalDocumentation::class,
85+
'example' => 'any',
86+
'deprecated' => 'boolean',
87+
];
88+
}
2589

90+
/**
91+
* Perform validation on this object, check data against OpenAPI Specification rules.
92+
*
93+
* Call `addError()` in case of validation errors.
94+
*/
95+
protected function performValidation()
96+
{
97+
// TODO: Implement performValidation() method.
98+
}
2699
}

src/spec/Xml.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace cebe\openapi\spec;
4+
5+
use cebe\openapi\SpecBaseObject;
6+
7+
/**
8+
* A metadata object that allows for more fine-tuned XML model definitions.
9+
*
10+
* @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#xmlObject
11+
*
12+
* @author Carsten Brandt <mail@cebe.cc>
13+
*/
14+
class Xml extends SpecBaseObject
15+
{
16+
/**
17+
* @return array array of attributes available in this object.
18+
*/
19+
protected function attributes(): array
20+
{
21+
return [
22+
'name' => 'string',
23+
'namespace' => 'string',
24+
'prefix' => 'string',
25+
'attribute' => 'boolean',
26+
'wrapped' => 'boolean',
27+
];
28+
}
29+
30+
/**
31+
* Perform validation on this object, check data against OpenAPI Specification rules.
32+
*
33+
* Call `addError()` in case of validation errors.
34+
*/
35+
protected function performValidation()
36+
{
37+
// TODO: Implement performValidation() method.
38+
}
39+
}

tests/spec/OpenApiTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
use cebe\openapi\spec\OpenApi;
34
use Symfony\Component\Yaml\Yaml;
45

56
/**
@@ -11,7 +12,7 @@ class OpenApiTest extends \PHPUnit\Framework\TestCase
1112
{
1213
public function testEmpty()
1314
{
14-
$openapi = new \cebe\openapi\spec\OpenApi([]);
15+
$openapi = new OpenApi([]);
1516

1617
$this->assertFalse($openapi->validate());
1718
$this->assertEquals([
@@ -26,7 +27,7 @@ public function testReadPetStore()
2627
$openApiFile = __DIR__ . '/../../vendor/oai/openapi-specification/examples/v3.0/petstore.yaml';
2728

2829
$yaml = Yaml::parse(file_get_contents($openApiFile));
29-
$openapi = new \cebe\openapi\spec\OpenApi($yaml);
30+
$openapi = new OpenApi($yaml);
3031

3132
$result = $openapi->validate();
3233
$this->assertEquals([], $openapi->getErrors());

tests/spec/SchemaTest.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
use cebe\openapi\Reader;
4+
5+
6+
/**
7+
*
8+
*
9+
* @author Carsten Brandt <mail@cebe.cc>
10+
*/
11+
class SchemaTest extends \PHPUnit\Framework\TestCase
12+
{
13+
public function testRead()
14+
{
15+
/** @var $schema \cebe\openapi\spec\Schema */
16+
$schema = Reader::readFromJson(<<<JSON
17+
{
18+
"type": "string",
19+
"format": "email"
20+
}
21+
JSON
22+
, \cebe\openapi\spec\Schema::class);
23+
24+
$result = $schema->validate();
25+
$this->assertEquals([], $schema->getErrors());
26+
$this->assertTrue($result);
27+
28+
$this->assertEquals("string", $schema->type);
29+
$this->assertEquals("email", $schema->format);
30+
}
31+
32+
public function testReadObject()
33+
{
34+
/** @var $schema \cebe\openapi\spec\Schema */
35+
$schema = Reader::readFromJson(<<<'JSON'
36+
{
37+
"type": "object",
38+
"required": [
39+
"name"
40+
],
41+
"properties": {
42+
"name": {
43+
"type": "string"
44+
},
45+
"address": {
46+
"$ref": "#/components/schemas/Address"
47+
},
48+
"age": {
49+
"type": "integer",
50+
"format": "int32",
51+
"minimum": 0
52+
}
53+
}
54+
}
55+
JSON
56+
, \cebe\openapi\spec\Schema::class);
57+
58+
$result = $schema->validate();
59+
$this->assertEquals([], $schema->getErrors());
60+
$this->assertTrue($result);
61+
62+
$this->assertEquals("object", $schema->type);
63+
$this->assertEquals(['name'], $schema->required);
64+
$this->assertEquals('integer', $schema->properties['age']->type);
65+
$this->assertEquals(0, $schema->properties['age']->minimum);
66+
}
67+
68+
}

tests/spec/ServerTest.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

3-
use Symfony\Component\Yaml\Yaml;
3+
use cebe\openapi\Reader;
4+
use cebe\openapi\spec\Server;
45

56
/**
67
*
@@ -11,8 +12,8 @@ class ServerTest extends \PHPUnit\Framework\TestCase
1112
{
1213
public function testRead()
1314
{
14-
/** @var $server \cebe\openapi\spec\Server */
15-
$server = \cebe\openapi\Reader::readFromJson(<<<JSON
15+
/** @var $server Server */
16+
$server = Reader::readFromJson(<<<JSON
1617
{
1718
"url": "https://{username}.gigantic-server.com:{port}/{basePath}",
1819
"description": "The production API server",
@@ -34,7 +35,7 @@ public function testRead()
3435
}
3536
}
3637
JSON
37-
, \cebe\openapi\spec\Server::class);
38+
, Server::class);
3839

3940
$result = $server->validate();
4041
$this->assertEquals([], $server->getErrors());

0 commit comments

Comments
 (0)