Skip to content

Commit 20a360e

Browse files
committed
Merge XML and YAML factories
1 parent 02c7676 commit 20a360e

29 files changed

+987
-1297
lines changed

src/Bridge/Symfony/Bundle/Resources/config/metadata.xml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
<!-- Extractor -->
1010

11-
<service id="api_platform.metadata.extractor.yaml" class="ApiPlatform\Core\Metadata\YamlExtractor" public="false" />
12-
<service id="api_platform.metadata.extractor.xml" class="ApiPlatform\Core\Metadata\XmlExtractor" public="false" />
11+
<service id="api_platform.metadata.extractor.yaml" class="ApiPlatform\Core\Metadata\Extractor\YamlExtractor" public="false" />
12+
<service id="api_platform.metadata.extractor.xml" class="ApiPlatform\Core\Metadata\Extractor\XmlExtractor" public="false" />
1313

1414
<!-- Resource name collection -->
1515

@@ -24,12 +24,12 @@
2424
<argument type="service" id="api_platform.metadata.resource.name_collection_factory.cached.inner" />
2525
</service>
2626

27-
<service id="api_platform.metadata.resource.name_collection_factory.yaml" decorates="api_platform.metadata.resource.name_collection_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\YamlResourceNameCollectionFactory" public="false">
27+
<service id="api_platform.metadata.resource.name_collection_factory.yaml" decorates="api_platform.metadata.resource.name_collection_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\ExtractorResourceNameCollectionFactory" public="false">
2828
<argument type="service" id="api_platform.metadata.extractor.yaml" />
2929
<argument type="service" id="api_platform.metadata.resource.name_collection_factory.yaml.inner" />
3030
</service>
3131

32-
<service id="api_platform.metadata.resource.name_collection_factory.xml" decorates="api_platform.metadata.resource.name_collection_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\XmlResourceNameCollectionFactory" public="false">
32+
<service id="api_platform.metadata.resource.name_collection_factory.xml" decorates="api_platform.metadata.resource.name_collection_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\ExtractorResourceNameCollectionFactory" public="false">
3333
<argument type="service" id="api_platform.metadata.extractor.xml" />
3434
<argument type="service" id="api_platform.metadata.resource.name_collection_factory.xml.inner" />
3535
</service>
@@ -46,12 +46,12 @@
4646
<argument type="service" id="api_platform.metadata.resource.metadata_factory.php_doc.inner" />
4747
</service>
4848

49-
<service id="api_platform.metadata.resource.metadata_factory.yaml" decorates="api_platform.metadata.resource.metadata_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\YamlResourceMetadataFactory" decoration-priority="40" public="false">
49+
<service id="api_platform.metadata.resource.metadata_factory.yaml" decorates="api_platform.metadata.resource.metadata_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\ExtractorResourceMetadataFactory" decoration-priority="40" public="false">
5050
<argument type="service" id="api_platform.metadata.extractor.yaml" />
5151
<argument type="service" id="api_platform.metadata.resource.metadata_factory.yaml.inner" />
5252
</service>
5353

54-
<service id="api_platform.metadata.resource.metadata_factory.xml" decorates="api_platform.metadata.resource.metadata_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\XmlResourceMetadataFactory" decoration-priority="40" public="false">
54+
<service id="api_platform.metadata.resource.metadata_factory.xml" decorates="api_platform.metadata.resource.metadata_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\ExtractorResourceMetadataFactory" decoration-priority="40" public="false">
5555
<argument type="service" id="api_platform.metadata.extractor.xml" />
5656
<argument type="service" id="api_platform.metadata.resource.metadata_factory.xml.inner" />
5757
</service>
@@ -95,12 +95,12 @@
9595
<argument type="service" id="api_platform.metadata.property.name_collection_factory.cached.inner" />
9696
</service>
9797

98-
<service id="api_platform.metadata.property.name_collection_factory.yaml" class="ApiPlatform\Core\Metadata\Property\Factory\YamlPropertyNameCollectionFactory" decorates="api_platform.metadata.property.name_collection_factory" public="false">
98+
<service id="api_platform.metadata.property.name_collection_factory.yaml" class="ApiPlatform\Core\Metadata\Property\Factory\ExtractorPropertyNameCollectionFactory" decorates="api_platform.metadata.property.name_collection_factory" public="false">
9999
<argument type="service" id="api_platform.metadata.extractor.yaml" />
100100
<argument type="service" id="api_platform.metadata.property.name_collection_factory.yaml.inner" />
101101
</service>
102102

103-
<service id="api_platform.metadata.property.name_collection_factory.xml" class="ApiPlatform\Core\Metadata\Property\Factory\XmlPropertyNameCollectionFactory" decorates="api_platform.metadata.property.name_collection_factory" public="false">
103+
<service id="api_platform.metadata.property.name_collection_factory.xml" class="ApiPlatform\Core\Metadata\Property\Factory\ExtractorPropertyNameCollectionFactory" decorates="api_platform.metadata.property.name_collection_factory" public="false">
104104
<argument type="service" id="api_platform.metadata.extractor.xml" />
105105
<argument type="service" id="api_platform.metadata.property.name_collection_factory.xml.inner" />
106106
</service>
@@ -124,12 +124,12 @@
124124
<argument type="service" id="api_platform.metadata.property.metadata_factory.inherited.inner" />
125125
</service>
126126

127-
<service id="api_platform.metadata.property.metadata_factory.yaml" class="ApiPlatform\Core\Metadata\Property\Factory\YamlPropertyMetadataFactory" decorates="api_platform.metadata.property.metadata_factory" decoration-priority="40" public="false">
127+
<service id="api_platform.metadata.property.metadata_factory.yaml" class="ApiPlatform\Core\Metadata\Property\Factory\ExtractorPropertyMetadataFactory" decorates="api_platform.metadata.property.metadata_factory" decoration-priority="40" public="false">
128128
<argument type="service" id="api_platform.metadata.extractor.yaml" />
129129
<argument type="service" id="api_platform.metadata.property.metadata_factory.yaml.inner" />
130130
</service>
131131

132-
<service id="api_platform.metadata.property.metadata_factory.xml" class="ApiPlatform\Core\Metadata\Property\Factory\XmlPropertyMetadataFactory" decorates="api_platform.metadata.property.metadata_factory" decoration-priority="40" public="false">
132+
<service id="api_platform.metadata.property.metadata_factory.xml" class="ApiPlatform\Core\Metadata\Property\Factory\ExtractorPropertyMetadataFactory" decorates="api_platform.metadata.property.metadata_factory" decoration-priority="40" public="false">
133133
<argument type="service" id="api_platform.metadata.extractor.xml" />
134134
<argument type="service" id="api_platform.metadata.property.metadata_factory.xml.inner" />
135135
</service>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace ApiPlatform\Core\Metadata\Extractor;
13+
14+
/**
15+
* Base file extractor.
16+
*
17+
* @author Kévin Dunglas <[email protected]>
18+
*/
19+
abstract class AbstractExtractor implements ExtractorInterface
20+
{
21+
protected $paths;
22+
protected $resources;
23+
24+
/**
25+
* @param string[] $paths
26+
*/
27+
public function __construct(array $paths)
28+
{
29+
$this->paths = $paths;
30+
}
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function getResources(): array
36+
{
37+
if (null !== $this->resources) {
38+
return $this->resources;
39+
}
40+
41+
$this->resources = [];
42+
foreach ($this->paths as $path) {
43+
$this->extractPath($path);
44+
}
45+
46+
return $this->resources;
47+
}
48+
49+
/**
50+
* Extracts metadata from a given path.
51+
*
52+
* @param string $path
53+
*/
54+
abstract protected function extractPath(string $path);
55+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace ApiPlatform\Core\Metadata\Extractor;
13+
14+
use ApiPlatform\Core\Exception\InvalidArgumentException;
15+
16+
/**
17+
* Extracts an array of metadata from a file or a list of files.
18+
*
19+
* @author Kévin Dunglas <[email protected]>
20+
*/
21+
interface ExtractorInterface
22+
{
23+
/**
24+
* Parses all metadata files and convert them in an array.
25+
*
26+
* @throws InvalidArgumentException
27+
*
28+
* @return array
29+
*/
30+
public function getResources(): array;
31+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace ApiPlatform\Core\Metadata\Extractor;
13+
14+
use ApiPlatform\Core\Exception\InvalidArgumentException;
15+
use Symfony\Component\Config\Util\XmlUtils;
16+
17+
/**
18+
* Extracts an array of metadata from a list of XML files.
19+
*
20+
* @author Kévin Dunglas <[email protected]>
21+
* @author Antoine Bluchet <[email protected]>
22+
* @author Baptiste Meyer <[email protected]>
23+
*/
24+
final class XmlExtractor extends AbstractExtractor
25+
{
26+
const RESOURCE_SCHEMA = __DIR__.'/../schema/metadata.xsd';
27+
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
protected function extractPath(string $path)
32+
{
33+
try {
34+
$xml = simplexml_import_dom(XmlUtils::loadFile($path, self::RESOURCE_SCHEMA));
35+
} catch (\InvalidArgumentException $e) {
36+
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
37+
}
38+
39+
foreach ($xml->resource as $resource) {
40+
$resourceClass = (string) $resource['class'];
41+
42+
$this->resources[$resourceClass] = [
43+
'shortName' => $this->phpize($resource, 'shortName', 'string'),
44+
'description' => $this->phpize($resource, 'description', 'string'),
45+
'iri' => $this->phpize($resource, 'iri', 'string'),
46+
'itemOperations' => $this->getAttributes($resource, 'itemOperation') ?: null,
47+
'collectionOperations' => $this->getAttributes($resource, 'collectionOperation') ?: null,
48+
'attributes' => $this->getAttributes($resource, 'attribute') ?: null,
49+
'properties' => $this->getProperties($resource) ?: null,
50+
];
51+
}
52+
}
53+
54+
/**
55+
* Recursively transforms an attribute structure into an associative array.
56+
*
57+
* @param \SimpleXMLElement $resource
58+
* @param string $elementName
59+
*
60+
* @return array
61+
*/
62+
private function getAttributes(\SimpleXMLElement $resource, string $elementName): array
63+
{
64+
$attributes = [];
65+
foreach ($resource->$elementName as $attribute) {
66+
if (isset($attribute->attribute[0])) {
67+
$value = $this->getAttributes($attribute, 'attribute');
68+
} else {
69+
$value = (string) $attribute;
70+
}
71+
72+
if (isset($attribute['name'])) {
73+
$attributes[(string) $attribute['name']] = $value;
74+
} else {
75+
$attributes[] = $value;
76+
}
77+
}
78+
79+
return $attributes;
80+
}
81+
82+
/**
83+
* Gets metadata of a property.
84+
*
85+
* @param \SimpleXMLElement $resource
86+
*
87+
* @return array
88+
*/
89+
private function getProperties(\SimpleXMLElement $resource): array
90+
{
91+
$properties = [];
92+
foreach ($resource->property as $property) {
93+
$properties[(string) $property['name']] = [
94+
'description' => $this->phpize($property, 'description', 'string'),
95+
'readable' => $this->phpize($property, 'readable', 'bool'),
96+
'writable' => $this->phpize($property, 'writable', 'bool'),
97+
'readableLink' => $this->phpize($property, 'readableLink', 'bool'),
98+
'writableLink' => $this->phpize($property, 'writableLink', 'bool'),
99+
'required' => $this->phpize($property, 'required', 'bool'),
100+
'identifier' => $this->phpize($property, 'identifier', 'bool'),
101+
'iri' => $this->phpize($property, 'iri', 'string'),
102+
'attributes' => $this->getAttributes($property, 'attribute'),
103+
];
104+
}
105+
106+
return $properties;
107+
}
108+
109+
/**
110+
* Transforms an XML attribute's value in a PHP value.
111+
*
112+
* @param \SimpleXMLElement $array
113+
* @param string $key
114+
* @param string $type
115+
*
116+
* @return bool|string|null
117+
*/
118+
private function phpize(\SimpleXMLElement $array, string $key, string $type)
119+
{
120+
if (!isset($array[$key])) {
121+
return;
122+
}
123+
124+
switch ($type) {
125+
case 'string':
126+
return (string) $array[$key];
127+
128+
case 'bool':
129+
return (bool) XmlUtils::phpize($array[$key]);
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)