Completed
Push — grapqlops ( caf158 )
by Kévin
14:43
created

XmlExtractor::getOperations()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 11
nc 5
nop 2
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
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Metadata\Extractor;
15
16
use ApiPlatform\Core\Exception\InvalidArgumentException;
17
use Symfony\Component\Config\Util\XmlUtils;
18
19
/**
20
 * Extracts an array of metadata from a list of XML files.
21
 *
22
 * @author Kévin Dunglas <[email protected]>
23
 * @author Antoine Bluchet <[email protected]>
24
 * @author Baptiste Meyer <[email protected]>
25
 */
26
final class XmlExtractor extends AbstractExtractor
27
{
28
    const RESOURCE_SCHEMA = __DIR__.'/../schema/metadata.xsd';
29
30
    /**
31
     * {@inheritdoc}
32
     */
33
    protected function extractPath(string $path)
34
    {
35
        try {
36
            $xml = simplexml_import_dom(XmlUtils::loadFile($path, self::RESOURCE_SCHEMA));
37
        } catch (\InvalidArgumentException $e) {
38
            throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
39
        }
40
41
        foreach ($xml->resource as $resource) {
42
            $resourceClass = (string) $resource['class'];
43
44
            $this->resources[$resourceClass] = [
45
                'shortName' => $this->phpize($resource, 'shortName', 'string'),
46
                'description' => $this->phpize($resource, 'description', 'string'),
47
                'iri' => $this->phpize($resource, 'iri', 'string'),
48
                'itemOperations' => $this->getOperations($resource, 'itemOperation'),
49
                'collectionOperations' => $this->getOperations($resource, 'collectionOperation'),
50
                'graphql' => $this->getOperations($resource, 'operation'),
51
                'attributes' => $this->getAttributes($resource, 'attribute') ?: null,
52
                'properties' => $this->getProperties($resource) ?: null,
53
            ];
54
        }
55
    }
56
57
    /**
58
     * Returns the array containing configured operations. Returns NULL if there is no operation configuration.
59
     *
60
     * @return array|null
61
     */
62
    private function getOperations(\SimpleXMLElement $resource, string $operationType)
63
    {
64
        $graphql = 'operation' === $operationType;
65
        if (!$graphql && $legacyOperations = $this->getAttributes($resource, $operationType)) {
66
            @trigger_error(
67
                sprintf('Configuring "%1$s" tags without using a parent "%1$ss" tag is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3', $operationType),
68
                E_USER_DEPRECATED
69
            );
70
71
            return $legacyOperations;
72
        }
73
74
        $operationsParent = $graphql ? 'graphql' : "{$operationType}s";
75
76
        if (!isset($resource->$operationsParent)) {
77
            return null;
78
        }
79
80
        return $this->getAttributes($resource->$operationsParent, $operationType);
81
    }
82
83
    /**
84
     * Recursively transforms an attribute structure into an associative array.
85
     */
86
    private function getAttributes(\SimpleXMLElement $resource, string $elementName): array
87
    {
88
        $attributes = [];
89
        foreach ($resource->$elementName as $attribute) {
90
            $value = isset($attribute->attribute[0]) ? $this->getAttributes($attribute, 'attribute') : XmlUtils::phpize($attribute);
91
            if (isset($attribute['name'])) {
92
                $attributes[(string) $attribute['name']] = $value;
93
            } else {
94
                $attributes[] = $value;
95
            }
96
        }
97
98
        return $attributes;
99
    }
100
101
    /**
102
     * Gets metadata of a property.
103
     */
104
    private function getProperties(\SimpleXMLElement $resource): array
105
    {
106
        $properties = [];
107
        foreach ($resource->property as $property) {
108
            $properties[(string) $property['name']] = [
109
                'description' => $this->phpize($property, 'description', 'string'),
110
                'readable' => $this->phpize($property, 'readable', 'bool'),
111
                'writable' => $this->phpize($property, 'writable', 'bool'),
112
                'readableLink' => $this->phpize($property, 'readableLink', 'bool'),
113
                'writableLink' => $this->phpize($property, 'writableLink', 'bool'),
114
                'required' => $this->phpize($property, 'required', 'bool'),
115
                'identifier' => $this->phpize($property, 'identifier', 'bool'),
116
                'iri' => $this->phpize($property, 'iri', 'string'),
117
                'attributes' => $this->getAttributes($property, 'attribute'),
118
                'subresource' => $property->subresource ? [
119
                    'collection' => $this->phpize($property->subresource, 'collection', 'bool'),
120
                    'resourceClass' => $this->phpize($property->subresource, 'resourceClass', 'string'),
121
                ] : null,
122
            ];
123
        }
124
125
        return $properties;
126
    }
127
128
    /**
129
     * Transforms an XML attribute's value in a PHP value.
130
     *
131
     * @return bool|string|null
132
     */
133
    private function phpize(\SimpleXMLElement $array, string $key, string $type)
134
    {
135
        if (!isset($array[$key])) {
136
            return null;
137
        }
138
139
        switch ($type) {
140
            case 'string':
141
                return (string) $array[$key];
142
            case 'bool':
143
                return (bool) XmlUtils::phpize($array[$key]);
144
        }
145
146
        return null;
147
    }
148
}
149