Completed
Push — master ( 1826e3...724c6f )
by Rafael
07:29
created

ObjectTypeAnnotationParser   F

Complexity

Total Complexity 84

Size/Duplication

Total Lines 479
Duplicated Lines 0 %

Test Coverage

Coverage 81.78%

Importance

Changes 0
Metric Value
wmc 84
dl 0
loc 479
ccs 184
cts 225
cp 0.8178
rs 1.5789
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A supports() 0 3 2
B parse() 0 33 5
A __construct() 0 3 1
B getFieldDecorators() 0 35 4
A getFieldAnnotation() 0 7 2
C extractInterfaceDefinitions() 0 51 10
A getClassMethods() 0 8 2
A copyFieldsFromInterface() 0 9 3
D resolveDefinitionInterfaces() 0 39 10
F resolveFields() 0 133 31
A loadInheritedProperties() 0 5 2
D isExposed() 0 30 10
A getClassProperties() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like ObjectTypeAnnotationParser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ObjectTypeAnnotationParser, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Definition\Loader\Annotation;
12
13
use Symfony\Component\DependencyInjection\Definition;
14
use Ynlo\GraphQLBundle\Annotation;
15
use Ynlo\GraphQLBundle\Component\TaggedServices\TaggedServices;
16
use Ynlo\GraphQLBundle\Definition\ArgumentDefinition;
17
use Ynlo\GraphQLBundle\Definition\FieldDefinition;
18
use Ynlo\GraphQLBundle\Definition\FieldsAwareDefinitionInterface;
19
use Ynlo\GraphQLBundle\Definition\ImplementorInterface;
20
use Ynlo\GraphQLBundle\Definition\InputObjectDefinition;
21
use Ynlo\GraphQLBundle\Definition\InterfaceDefinition;
22
use Ynlo\GraphQLBundle\Definition\Loader\Annotation\FieldDecorator\FieldDefinitionDecoratorInterface;
23
use Ynlo\GraphQLBundle\Definition\ObjectDefinition;
24
use Ynlo\GraphQLBundle\Definition\ObjectDefinitionInterface;
25
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint;
26
use Ynlo\GraphQLBundle\Resolver\FieldExpressionResolver;
27
use Ynlo\GraphQLBundle\Type\Definition\EndpointAwareInterface;
28
use Ynlo\GraphQLBundle\Util\TypeUtil;
29
30
/**
31
 * Parse the ObjectType annotation to fetch object definitions
32
 */
33
class ObjectTypeAnnotationParser implements AnnotationParserInterface
34
{
35
    use AnnotationReaderAwareTrait;
36
37
    /**
38
     * @var TaggedServices
39
     */
40
    protected $taggedServices;
41
    /**
42
     * @var Endpoint
43
     */
44
    protected $endpoint;
45
46
    /**
47
     * ObjectTypeAnnotationParser constructor.
48
     *
49
     * @param TaggedServices $taggedServices
50
     */
51 1
    public function __construct(TaggedServices $taggedServices)
52
    {
53 1
        $this->taggedServices = $taggedServices;
54 1
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59 1
    public function supports($annotation): bool
60
    {
61 1
        return $annotation instanceof Annotation\ObjectType || $annotation instanceof Annotation\InputObjectType;
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 1
    public function parse($annotation, \ReflectionClass $refClass, Endpoint $endpoint)
68
    {
69 1
        $this->endpoint = $endpoint;
70
71 1
        if ($annotation instanceof Annotation\ObjectType) {
72 1
            $objectDefinition = new ObjectDefinition();
73
        } else {
74 1
            $objectDefinition = new InputObjectDefinition();
75
        }
76
77 1
        $objectDefinition->setName($annotation->name);
78 1
        $objectDefinition->setExclusionPolicy($annotation->exclusionPolicy);
79 1
        $objectDefinition->setClass($refClass->name);
80 1
        $objectDefinition->setMetas($annotation->options);
81
82 1
        if (!$objectDefinition->getName()) {
83 1
            preg_match('/\w+$/', $refClass->getName(), $matches);
84 1
            $objectDefinition->setName($matches[0] ?? '');
85
        }
86
87 1
        if ($endpoint->hasType($objectDefinition->getName())) {
88
            return;
89
        }
90
91 1
        $objectDefinition->setDescription($annotation->description);
92
93 1
        if ($objectDefinition instanceof ImplementorInterface) {
94 1
            $this->resolveDefinitionInterfaces($refClass, $objectDefinition, $endpoint, $annotation);
95
        }
96
97 1
        $this->loadInheritedProperties($refClass, $objectDefinition);
98 1
        $this->resolveFields($refClass, $objectDefinition);
99 1
        $endpoint->addType($objectDefinition);
100 1
    }
101
102
    /**
103
     * @param \ReflectionClass      $refClass
104
     * @param ImplementorInterface  $implementor
105
     * @param Endpoint              $endpoint
106
     * @param Annotation\ObjectType $annotation
107 1
     */
108
    protected function resolveDefinitionInterfaces(\ReflectionClass $refClass, ImplementorInterface $implementor, Endpoint $endpoint, Annotation\ObjectType $annotation)
109 1
    {
110 1
        $interfaceDefinitions = $this->extractInterfaceDefinitions($refClass);
111 1
        foreach ($interfaceDefinitions as $interfaceDefinition) {
112
            if (in_array($interfaceDefinition->getName(), $annotation->ignoreInterface)) {
113 1
                continue;
114 1
            }
115
116 1
            $implementor->addInterface($interfaceDefinition->getName());
117
118
            if (!$endpoint->hasType($interfaceDefinition->getName())) {
119 1
                $endpoint->addType($interfaceDefinition);
120 1
            } else {
121
                $interfaceDefinition = $endpoint->getType($interfaceDefinition->getName());
122
            }
123
124
            $interfaceDefinition->addImplementor($implementor->getName());
0 ignored issues
show
Bug introduced by
The method addImplementor() does not exist on Ynlo\GraphQLBundle\Definition\DefinitionInterface. It seems like you code against a sub-type of Ynlo\GraphQLBundle\Definition\DefinitionInterface such as Ynlo\GraphQLBundle\Definition\InterfaceDefinition or Ynlo\GraphQLBundle\Definition\InterfaceDefinition. ( Ignorable by Annotation )

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

124
            $interfaceDefinition->/** @scrutinizer ignore-call */ 
125
                                  addImplementor($implementor->getName());
Loading history...
125
            $this->copyFieldsFromInterface($interfaceDefinition, $implementor);
126
        }
127 1
128 1
        //support interface inheritance
129 1
        //Interface inheritance is not implemented in GraphQL
130 1
        //@see https://github.com/facebook/graphql/issues/295
131 1
        //BUT, GraphQLBundle use this feature in some places like extensions etc.
132 1
        foreach ($interfaceDefinitions as $interfaceDefinition) {
133 1
            if ($interfaceDefinition->getClass()) {
134 1
                $childInterface = new \ReflectionClass($interfaceDefinition->getClass());
135 1
                /** @var Annotation\InterfaceType $interface */
136
                $interface = $this->reader->getClassAnnotation($childInterface, Annotation\InterfaceType::class);
137
                $parentDefinitions = $this->extractInterfaceDefinitions($childInterface);
138
                foreach ($parentDefinitions as $parentDefinition) {
139
                    if (in_array($parentDefinition->getName(), $interface->ignoreParent)) {
140
                        continue;
141 1
                    }
142
                    $this->copyFieldsFromInterface($parentDefinition, $interfaceDefinition);
143
                    if ($endpoint->hasType($parentDefinition->getName())) {
144
                        $existentParentDefinition = $endpoint->getType($parentDefinition->getName());
145
                        if ($existentParentDefinition instanceof InterfaceDefinition) {
146
                            $existentParentDefinition->addImplementor($interfaceDefinition->getName());
147
                        }
148 1
                    }
149
                }
150 1
            }
151
        }
152
    }
153 1
154 1
    /**
155 1
     * @param \ReflectionClass $refClass
156 1
     *
157
     * @return InterfaceDefinition[]
158 1
     */
159
    protected function extractInterfaceDefinitions(\ReflectionClass $refClass)
160
    {
161 1
        $int = $refClass->getInterfaces();
162 1
163
        //get recursively all parent abstract classes to use as interfaces
164 1
        $currentClass = $refClass;
165 1
        while ($currentClass->getParentClass()) {
166 1
            if ($currentClass->getParentClass()->isAbstract()) {
167
                $int[] = $currentClass->getParentClass();
168
            }
169 1
            $currentClass = $currentClass->getParentClass();
170 1
        }
171 1
172 1
        //current class can be a object and interface at the same time,
173 1
        //When use different object types using discriminator map
174 1
        if ($this->reader->getClassAnnotation($refClass, Annotation\InterfaceType::class)) {
175
            $int[] = $refClass;
176
        }
177
178 1
        $definitions = [];
179 1
        foreach ($int as $intRef) {
180
            /** @var Annotation\InterfaceType $intAnnot */
181
            $intAnnot = $this->reader->getClassAnnotation(
182 1
                $intRef,
183 1
                Annotation\InterfaceType::class
184 1
            );
185
186
            if ($intAnnot) {
187 1
                $intDef = new InterfaceDefinition();
188
                $intDef->setName($intAnnot->name);
189
                $intDef->setClass($intRef->getName());
190
191 1
                if (!$intDef->getName() && preg_match('/\w+$/', $intRef->getName(), $matches)) {
192
                    $intDef->setName(preg_replace('/Interface$/', null, $matches[0]));
193
                }
194
195
                $intDef->setMetas($intAnnot->options);
196
                $intDef->setDescription($intAnnot->description);
197
                $intDef->setDiscriminatorMap($intAnnot->discriminatorMap);
198 1
                $intDef->setDiscriminatorProperty($intAnnot->discriminatorProperty);
199
                $intDef->setExclusionPolicy($intAnnot->exclusionPolicy);
200 1
                $this->resolveFields($intRef, $intDef);
201 1
                if (!$intDef->getName() && preg_match('/\w+$/', $intRef->getName(), $matches)) {
202 1
                    $intDef->setName(preg_replace('/Interface$/', null, $matches[0]));
203
                }
204 1
205
                $definitions[] = $intDef;
206
            }
207
        }
208
209
        return $definitions;
210
    }
211
212 1
    /**
213
     * @param \ReflectionClass          $refClass
214 1
     * @param ObjectDefinitionInterface $objectDefinition
215 1
     */
216 1
    protected function loadInheritedProperties(\ReflectionClass $refClass, ObjectDefinitionInterface $objectDefinition)
217 1
    {
218 1
        while ($parent = $refClass->getParentClass()) {
219
            $this->resolveFields($refClass, $objectDefinition);
220 1
            $refClass = $parent;
221
        }
222
    }
223 1
224
    /**
225
     * Copy all fields from interface to given object implementor
226
     *
227
     * @param InterfaceDefinition            $intDef
228
     * @param FieldsAwareDefinitionInterface $fieldsAwareDefinition
229
     */
230
    protected function copyFieldsFromInterface(InterfaceDefinition $intDef, FieldsAwareDefinitionInterface $fieldsAwareDefinition)
231 1
    {
232
        foreach ($intDef->getFields() as $field) {
233 1
            if (!$fieldsAwareDefinition->hasField($field->getName())) {
234 1
                $newField = clone $field;
235 1
                $newField->addInheritedFrom($intDef->getName());
236 1
                $fieldsAwareDefinition->addField($newField);
237 1
            } else {
238
                $fieldsAwareDefinition->getField($field->getName())->addInheritedFrom($intDef->getName());
239
            }
240 1
        }
241 1
    }
242 1
243
    /**
244
     * Extract all fields for given definition
245 1
     *
246 1
     * @param \ReflectionClass          $refClass
247
     * @param ObjectDefinitionInterface $objectDefinition
248 1
     */
249
    protected function resolveFields(\ReflectionClass $refClass, ObjectDefinitionInterface $objectDefinition)
250
    {
251 1
        $props = array_merge($this->getClassProperties($refClass), $this->getClassMethods($refClass));
252 1
        $fieldDecorators = $this->getFieldDecorators();
253
        foreach ($props as $prop) {
254
            if (!$this->isExposed($objectDefinition, $prop)) {
255 1
                continue;
256 1
            }
257 1
258 1
            $field = new FieldDefinition();
259
            foreach ($fieldDecorators as $fieldDecorator) {
260
                $fieldDecorator->decorateFieldDefinition($prop, $field, $objectDefinition);
261 1
            }
262 1
263 1
            if ($objectDefinition->hasField($field->getName())) {
264 1
                $field = $objectDefinition->getField($field->getName());
265 1
            } else {
266 1
                $objectDefinition->addField($field);
267 1
            }
268 1
269 1
            $field->setOriginName($prop->name);
270 1
            $field->setOriginType(\get_class($prop));
271
272
            //resolve field arguments
273
            if ($prop instanceof \ReflectionMethod) {
274
                foreach ($this->reader->getMethodAnnotations($prop) as $argAnnotation) {
275
                    if (!$argAnnotation instanceof Annotation\Argument) {
276 1
                        continue;
277 1
                    }
278 1
279
                    $arg = new ArgumentDefinition();
280
                    $arg->setName($argAnnotation->name);
281 1
                    $arg->setDescription($argAnnotation->description);
282
                    $arg->setInternalName($argAnnotation->internalName);
283
                    $arg->setDefaultValue($argAnnotation->defaultValue);
284
                    $arg->setType(TypeUtil::normalize($argAnnotation->type));
285
                    $arg->setList(TypeUtil::isTypeList($argAnnotation->type));
286
                    $arg->setNonNullList(TypeUtil::isTypeNonNullList($argAnnotation->type));
287
                    $arg->setNonNull(TypeUtil::isTypeNonNull($argAnnotation->type));
288
                    $field->addArgument($arg);
289 1
                }
290
            }
291
        }
292
293
        //load overrides
294
        foreach ($this->reader->getClassAnnotations($refClass) as $annotation) {
295 1
            if (!$annotation instanceof Annotation\OverrideField) {
296
                continue;
297 1
            }
298 1
299
            if ($annotation->in && !\in_array($objectDefinition->getName(), $annotation->in)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $annotation->in of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
300 1
                continue;
301
            }
302
303
            if ($annotation->notIn && \in_array($objectDefinition->getName(), $annotation->notIn)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $annotation->notIn of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
304
                continue;
305 1
            }
306 1
307
            if (!$objectDefinition->hasField($annotation->name)) {
308 1
                throw new \InvalidArgumentException(sprintf(
309
                    'The object definition "%s" does not have any field called "%s" in any of its parents definitions.',
310
                    $objectDefinition->getName(),
311 1
                    $annotation->name
312
                ));
313
            }
314 1
315 1
            if (true === $annotation->hidden) {
316
                $objectDefinition->removeField($annotation->name);
317
318
                continue;
319
            }
320 1
321 1
            $fieldDefinition = $objectDefinition->getField($annotation->name);
322 1
323
            if ($annotation->type) {
324
                $fieldDefinition->setType($annotation->type);
325
            }
326
            if ($annotation->alias) {
327
                $fieldDefinition->setName($annotation->alias);
328
                $objectDefinition->removeField($fieldDefinition->getName());
329
                $objectDefinition->addField($fieldDefinition);
330
            }
331
            if ($annotation->description) {
332
                $fieldDefinition->setDescription($annotation->description);
333
            }
334
            if ($annotation->deprecationReason || false === $annotation->deprecationReason) {
335
                $fieldDefinition->setDeprecationReason($annotation->deprecationReason);
336
            }
337
            if ($annotation->complexity) {
338
                $fieldDefinition->setComplexity($annotation->complexity);
339 1
            }
340 1
            if ($annotation->roles) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $annotation->roles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
341 1
                $fieldDefinition->setRoles($annotation->roles);
342
            }
343
        }
344
345
        //load virtual fields
346
        $annotations = $this->reader->getClassAnnotations($refClass);
347
        foreach ($annotations as $annotation) {
348
            if ($annotation instanceof Annotation\VirtualField) {
349
                if ($annotation->in && !\in_array($objectDefinition->getName(), $annotation->in)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $annotation->in of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
350
                    continue;
351
                }
352
353
                if ($annotation->notIn && \in_array($objectDefinition->getName(), $annotation->notIn)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $annotation->notIn of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
354
                    continue;
355
                }
356
357
                if (!$objectDefinition->hasField($annotation->name)) {
358
                    $fieldDefinition = new FieldDefinition();
359
                    $fieldDefinition->setName($annotation->name);
360
                    $fieldDefinition->setDescription($annotation->description);
361
                    $fieldDefinition->setDeprecationReason($annotation->deprecationReason);
362
                    $fieldDefinition->setType(TypeUtil::normalize($annotation->type));
363
                    $fieldDefinition->setNonNull(TypeUtil::isTypeNonNull($annotation->type));
364
                    $fieldDefinition->setNonNullList(TypeUtil::isTypeNonNullList($annotation->type));
365
                    $fieldDefinition->setList(TypeUtil::isTypeList($annotation->type));
366 1
                    $fieldDefinition->setMeta('expression', $annotation->expression);
367
                    $fieldDefinition->setResolver(FieldExpressionResolver::class);
368
                    $fieldDefinition->setComplexity($annotation->complexity);
369
                    $fieldDefinition->setRoles((array) $annotation->roles);
370 1
                    $objectDefinition->addField($fieldDefinition);
371
                } else {
372
                    $fieldDefinition = $objectDefinition->getField($annotation->name);
373
                    if ($fieldDefinition->getResolver() === FieldExpressionResolver::class) {
374
                        continue;
375 1
                    }
376
                    $error = sprintf(
377
                        'The object definition "%s" already has a field called "%s".',
378 1
                        $objectDefinition->getName(),
379 1
                        $annotation->name
380
                    );
381 1
                    throw new \InvalidArgumentException($error);
382 1
                }
383 1
            }
384 1
        }
385 1
    }
386 1
387
    /**
388
     * @return array|FieldDefinitionDecoratorInterface[]
389 1
     */
390
    protected function getFieldDecorators(): array
391 1
    {
392
        /** @var Definition $resolversServiceDefinition */
393
        $decoratorsDef = $this->taggedServices
394
            ->findTaggedServices('graphql.field_definition_decorator');
395 1
396
        $decorators = [];
397
        foreach ($decoratorsDef as $decoratorDef) {
398
            $attr = $decoratorDef->getAttributes();
399 1
            $priority = 0;
400 1
            if (isset($attr['priority'])) {
401 1
                $priority = $attr['priority'];
402 1
            }
403 1
404
            $decorator = $decoratorDef->getService();
405 1
406 1
            if ($decorator instanceof EndpointAwareInterface) {
407
                $decorator->setEndpoint($this->endpoint);
408
            }
409 1
410
            $decorators[] = [$priority, $decorator];
411
        }
412
413
        //sort by priority
414
        usort(
415
            $decorators,
416
            function ($service1, $service2) {
417
                list($priority1) = $service1;
418
                list($priority2) = $service2;
419
420 1
                return version_compare($priority2, $priority1);
421
            }
422 1
        );
423
424 1
        return array_column($decorators, 1);
425 1
    }
426
427
    /**
428 1
     * Verify if a given property for given definition is exposed or not
429 1
     *
430
     * @param ObjectDefinitionInterface             $definition
431
     * @param \ReflectionMethod|\ReflectionProperty $prop
432
     *
433 1
     * @return boolean
434 1
     */
435 1
    protected function isExposed(ObjectDefinitionInterface $definition, $prop): bool
436 1
    {
437
        $exposed = $definition->getExclusionPolicy() === ObjectDefinitionInterface::EXCLUDE_NONE;
438
439 1
        if ($prop instanceof \ReflectionMethod) {
440
            $exposed = false;
441
442 1
            //implicit inclusion
443 1
            if ($this->getFieldAnnotation($prop, Annotation\Field::class)) {
444 1
                $exposed = true;
445 1
            }
446
        }
447
448
        if ($exposed && $this->getFieldAnnotation($prop, Annotation\Exclude::class)) {
449 1
            $exposed = false;
450
        } elseif (!$exposed && $this->getFieldAnnotation($prop, Annotation\Expose::class)) {
451
            $exposed = true;
452 1
        }
453
454
        /** @var Annotation\Field $fieldAnnotation */
455
        if ($fieldAnnotation = $this->getFieldAnnotation($prop, Annotation\Field::class)) {
456
            $exposed = true;
457
            if ($fieldAnnotation->in) {
458
                $exposed = \in_array($definition->getName(), $fieldAnnotation->in);
459
            } elseif (($fieldAnnotation->notIn)) {
460
                $exposed = !\in_array($definition->getName(), $fieldAnnotation->notIn);
461
            }
462
        }
463 1
464
        return $exposed;
465 1
    }
466 1
467
    /**
468
     * Get field specific annotation matching given implementor
469 1
     *
470
     * @param \ReflectionMethod|\ReflectionProperty $prop
471
     * @param string                                $annotationClass
472
     *
473
     * @return mixed
474
     */
475
    protected function getFieldAnnotation($prop, string $annotationClass)
476
    {
477 1
        if ($prop instanceof \ReflectionProperty) {
478
            return $this->reader->getPropertyAnnotation($prop, $annotationClass);
479 1
        }
480 1
481 1
        return $this->reader->getMethodAnnotation($prop, $annotationClass);
482
    }
483
484 1
    /**
485
     * @param \ReflectionClass $refClass
486
     *
487
     * @return array
488
     */
489
    protected function getClassProperties(\ReflectionClass $refClass)
490
    {
491
        $props = [];
492 1
        foreach ($refClass->getProperties() as $prop) {
493
            $props[$prop->name] = $prop;
494 1
        }
495 1
496 1
        return $props;
497
    }
498
499 1
    /**
500
     * @param \ReflectionClass $refClass
501
     *
502
     * @return array
503
     */
504
    protected function getClassMethods(\ReflectionClass $refClass)
505
    {
506
        $methods = [];
507
        foreach ($refClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
508
            $methods[$method->name] = $method;
509
        }
510
511
        return $methods;
512
    }
513
}
514