Issues (18)

src/Metadata/Driver/YamlDriver.php (3 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace CCT\Component\ODMElasticsearch\Metadata\Driver;
6
7
use CCT\Component\ODMElasticsearch\Metadata\ClassMetadata;
8
use CCT\Component\ODMElasticsearch\Metadata\Exception\InvalidArgumentException;
9
use CCT\Component\ODMElasticsearch\Metadata\PropertyMetadataInterface;
10
use CCT\Component\ODMElasticsearch\Metadata\VirtualPropertyMetadata;
11
use Metadata\Driver\AbstractFileDriver;
12
13
use CCT\Component\ODMElasticsearch\Metadata\PropertyMetadata;
14
use Symfony\Component\Yaml\Yaml;
15
16
class YamlDriver extends AbstractFileDriver
17
{
18
    /**
19
     * Load metadata from config file
20
     *
21
     * @param \ReflectionClass $class
22
     * @param string $file
23
     *
24
     * @return ClassMetadata|\Metadata\ClassMetadata|null
25
     * @throws \ReflectionException
26
     */
27 14
    protected function loadMetadataFromFile(\ReflectionClass $class, $file)
28
    {
29 14
        $config = Yaml::parse(file_get_contents($file));
30
31 14
        $className = $class->name;
32
33 14
        if (!isset($config[$className])) {
34
            throw new \RuntimeException(
35
                sprintf('Expected metadata for class %s to be defined in %s.', $class->name, $file)
36
            );
37
        }
38
39 14
        $classConfig = $config[$className];
40 14
        $metadata = new ClassMetadata($className);
41
42 14
        if (array_key_exists('index', $classConfig)) {
43 13
            $metadata->setIndex($classConfig['index']);
44
        }
45
46 14
        if (array_key_exists('customRepositoryName', $classConfig)) {
47 13
            $metadata->setCustomRepositoryName($classConfig['customRepositoryName']);
48
        }
49
50 14
        $this->processProperties($class, $metadata, $classConfig);
51
52 14
        $this->processVirtualProperties($class, $metadata, $classConfig);
53
54 14
        return $metadata;
55
    }
56
57
    /**
58
     * File extension type to look for in config folder. All other extensions will be ignored
59
     *
60
     * @return string
61
     */
62 16
    protected function getExtension()
63
    {
64 16
        return 'yaml';
65
    }
66
67
    /**
68
     * Process properties
69
     *
70
     * @param \ReflectionClass $class
71
     * @param \Metadata\ClassMetadata $metadata
72
     * @param array $classConfig
73
     *
74
     * @throws \ReflectionException
75
     */
76 14
    protected function processProperties(
77
        \ReflectionClass $class,
78
        \Metadata\ClassMetadata $metadata,
79
        array $classConfig
80
    ): void {
81 14
        $className = $class->name;
82 14
        $exposeAll = $classConfig['exposeAll'] ?? true;
83
84 14
        $classParent = null;
85 14
        if ($class->getParentClass()) {
86
            $classParent = $class->getParentClass()->name;
87
        }
88
89 14
        foreach ($class->getProperties() as $property) {
90 14
            if (($property->class !== $className && $property->class !== $classParent)
91 14
                || (isset($property->info) && $property->info['class'] !== $className)
92
            ) {
93
                continue;
94
            }
95
96 14
            $propertyName = $property->getName();
97 14
            $propertyConfig = $classConfig['properties'][$propertyName] ?? null;
98
99 14
            if (null === $propertyConfig && false === $exposeAll) {
100 10
                continue;
101
            }
102
103 14
            $propertyConfig['use_default_accessors'] = $classConfig['use_default_accessors'] ?? false;
104
105 14
            if (isset($propertyConfig['expose']) && false === $propertyConfig['expose']) {
106 1
                continue;
107
            }
108
109 14
            $propertyMetadata = new PropertyMetadata($className, $propertyName);
110
111 14
            $this->applyPropertyConfigToPropertyMetadata($propertyMetadata, $propertyConfig);
112
113
            //Fall back to get property type from annotation @var
114 14
            if (null === $propertyMetadata->getType()) {
115 8
                $type = $this->getPropertyTypeFromAnnotation($property);
116 8
                $propertyMetadata->setType($type);
117
            }
118
119 14
            $metadata->addPropertyMetadata($propertyMetadata);
120
        }
121 14
    }
122
123
    /**
124
     * Process virtual property metadata
125
     *
126
     * @param \ReflectionClass $class
127
     * @param \Metadata\ClassMetadata $metadata
128
     * @param array $classConfig
129
     *
130
     * @throws \ReflectionException
131
     */
132 14
    protected function processVirtualProperties(
133
        \ReflectionClass $class,
134
        \Metadata\ClassMetadata $metadata,
135
        array $classConfig
136
    ): void {
137 14
        if (!array_key_exists('virtual_properties', $classConfig)) {
138 14
            return;
139
        }
140
        $className = $class->name;
141
142
        foreach ($classConfig['virtual_properties'] as $methodName => $virtualPropertyConfig) {
143
            if (!$class->hasMethod($methodName)) {
144
                throw new InvalidArgumentException('The method ' . $methodName . ' not found in class ' . $className);
145
            }
146
147
            $virtualPropertyConfig['use_default_accessors'] = false;
148
            $virtualPropertyMetadata = new VirtualPropertyMetadata($className, $methodName);
149
150
            $this->applyPropertyConfigToPropertyMetadata($virtualPropertyMetadata, $virtualPropertyConfig);
151
152
            $metadata->addPropertyMetadata($virtualPropertyMetadata);
153
        }
154
    }
155
156
    /**
157
     * Apply config to a property metadata
158
     *
159
     * @param PropertyMetadataInterface|PropertyMetadata $propertyMetadata
160
     * @param array $propertyConfig
161
     *
162
     * @throws \ReflectionException
163
     */
164 14
    protected function applyPropertyConfigToPropertyMetadata(
165
        PropertyMetadataInterface $propertyMetadata,
166
        array $propertyConfig
167
    ): void {
168
        // Set index name
169 14
        if (isset($propertyConfig['field_name'])) {
170
            $propertyMetadata->setFieldName($propertyConfig['field_name']);
171
        }
172
173
        // Set property type
174 14
        if (isset($propertyConfig['type'])) {
175 12
            $propertyMetadata->setType((string)$propertyConfig['type']);
176
177 12
            if (isset($propertyConfig['type_class'])) {
178 5
                $propertyMetadata->setTypeClass((string)$propertyConfig['type_class']);
179
            }
180
        }
181
182
        // Set accessors
183 14
        if (true === $propertyConfig['use_default_accessors']) {
184
            $propertyMetadata->setDefaultGetterAccessor();
0 ignored issues
show
The method setDefaultGetterAccessor() does not exist on CCT\Component\ODMElastic...opertyMetadataInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to CCT\Component\ODMElastic...opertyMetadataInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

184
            $propertyMetadata->/** @scrutinizer ignore-call */ 
185
                               setDefaultGetterAccessor();
Loading history...
185
            $propertyMetadata->setDefaultSetterAccessor();
0 ignored issues
show
The method setDefaultSetterAccessor() does not exist on CCT\Component\ODMElastic...opertyMetadataInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to CCT\Component\ODMElastic...opertyMetadataInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

185
            $propertyMetadata->/** @scrutinizer ignore-call */ 
186
                               setDefaultSetterAccessor();
Loading history...
186
        }
187
188 14
        if (isset($propertyConfig['accessor']['getter'])) {
189
            $propertyMetadata->setGetterAccessor($propertyConfig['accessor']['getter']);
190
        }
191
192 14
        if (isset($propertyConfig['accessor']['setter'])) {
193
            $propertyMetadata->setSetterAccessor($propertyConfig['accessor']['setter']);
194
        }
195
196
        // Set Elastic Search Mapping
197 14
        if (isset($propertyConfig['mapping'])) {
198
            $propertyMetadata->setMapping($propertyConfig['mapping']);
199
        }
200 14
    }
201
202
    /**
203
     * Try get the property type from annotation
204
     *
205
     * @param \ReflectionProperty $property
206
     *
207
     * @return string|null
208
     */
209 8
    protected function getPropertyTypeFromAnnotation(\ReflectionProperty $property): ?string
210
    {
211 8
        if (false === $property->getDocComment()) {
212 3
            return null;
213
        }
214
215 5
        if (preg_match('/@var\s+([^\s]+)/', $property->getDocComment(), $matches)) {
0 ignored issues
show
It seems like $property->getDocComment() can also be of type true; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

215
        if (preg_match('/@var\s+([^\s]+)/', /** @scrutinizer ignore-type */ $property->getDocComment(), $matches)) {
Loading history...
216 5
            [, $type] = $matches;
217
218 5
            if (null === $type || $type === 'resource') {
219
                return null;
220
            }
221
222 5
            if (\in_array($type, ['string', 'integer', 'int', 'float', 'boolean', 'bool', 'array'])) {
223 5
                return $type;
224
            }
225
226
            return 'object';
227
        }
228
229
        return null;
230
    }
231
}
232