Completed
Push — master ( 0eab77...b2f729 )
by Paul
05:35
created

AnnotationDriver::loadBusinessProperties()   C

Complexity

Conditions 11
Paths 22

Size

Total Lines 47
Code Lines 27

Duplication

Lines 14
Ratio 29.79 %

Importance

Changes 0
Metric Value
dl 14
loc 47
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 27
nc 22
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Victoire\Bundle\BusinessEntityBundle\Annotation;
4
5
use Doctrine\Common\Annotations\AnnotationException;
6
use Doctrine\Common\Annotations\Reader;
7
use Doctrine\ORM\Mapping\Column;
8
use Doctrine\ORM\Mapping\Driver\AnnotationDriver as DoctrineAnnotationDriver;
9
use Doctrine\ORM\Mapping\MappingException;
10
use Symfony\Component\Config\Definition\Exception\Exception;
11
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
12
use Symfony\Component\Validator\Constraints\NotBlank;
13
use Symfony\Component\Validator\Constraints\NotNull;
14
use Victoire\Bundle\BusinessEntityBundle\Entity\ReceiverProperty;
15
use Victoire\Bundle\CoreBundle\Annotations\ReceiverProperty as ReceiverPropertyAnnotation;
16
use Victoire\Bundle\WidgetBundle\Event\WidgetAnnotationEvent;
17
use Victoire\Bundle\WidgetBundle\Helper\WidgetHelper;
18
19
/**
20
 * Parse all files to get BusinessClasses.
21
 **/
22
class AnnotationDriver extends DoctrineAnnotationDriver
23
{
24
    public $reader;
25
    protected $eventDispatcher;
26
    protected $widgetHelper;
27
    protected $paths;
28
29
    /**
30
     * construct.
31
     *
32
     * @param Reader                   $reader
33
     * @param EventDispatcherInterface $eventDispatcher
34
     * @param WidgetHelper             $widgetHelper
35
     * @param array                    $paths           The paths where to search about Entities
36
     */
37
    public function __construct(Reader $reader, EventDispatcherInterface $eventDispatcher, $widgetHelper, $paths)
38
    {
39
        $this->reader = $reader;
40
        $this->eventDispatcher = $eventDispatcher;
41
        $this->widgetHelper = $widgetHelper;
42
        $this->paths = $paths;
43
    }
44
45
    /**
46
     * Get all class names.
47
     *
48
     * @return string[]
49
     */
50
    public function getAllClassNames()
51
    {
52
        if (!$this->paths) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->paths 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...
53
            throw MappingException::pathRequired();
54
        }
55
        $classes = [];
56
        $includedFiles = [];
57
        foreach ($this->paths as $path) {
58
            if (!is_dir($path)) {
59
                throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
60
            }
61
            $iterator = new \RegexIterator(
62
                new \RecursiveIteratorIterator(
63
                    new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
64
                    \RecursiveIteratorIterator::LEAVES_ONLY
65
                ),
66
                '/^.+\/Entity\/.+\.php$/i',
67
                \RecursiveRegexIterator::GET_MATCH
68
            );
69
            foreach ($iterator as $file) {
70
                $sourceFile = realpath($file[0]);
71
                require_once $sourceFile;
72
                $includedFiles[] = $sourceFile;
73
            }
74
        }
75
        $declared = get_declared_classes();
76
        foreach ($declared as $className) {
77
            $rc = new \ReflectionClass($className);
78
            $sourceFile = $rc->getFileName();
79
            if (in_array($sourceFile, $includedFiles) && !$this->isTransient($className)) {
80
                $classes[] = $className;
81
            }
82
        }
83
84
        return $classes;
85
    }
86
87
    /**
88
     * Parse the given Class to find some annotations related to BusinessEntities.
89
     */
90
    public function parse(\ReflectionClass $class)
91
    {
92
        $classPath = dirname($class->getFileName());
93
        $inPaths = false;
94
        foreach ($this->paths as $key => $_path) {
95
            //Check the entity path is in watching paths
96
            if (strpos($classPath, realpath($_path)) === 0) {
97
                $inPaths = true;
98
            }
99
        }
100
        if ($inPaths) {
101
            $classAnnotations = $this->reader->getClassAnnotations($class);
102 View Code Duplication
            if (!empty($classAnnotations)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
103
                foreach ($classAnnotations as $key => $annot) {
104
                    if (!is_numeric($key)) {
105
                        continue;
106
                    }
107
                    $classAnnotations[get_class($annot)] = $annot;
108
                }
109
            }
110
111
            //check if the entity is a widget (extends (in depth) widget class)
112
            $parentClass = $class->getParentClass();
113
            $isWidget = false;
114
            while ($parentClass && ($parentClass = $parentClass->getParentClass()) && !$isWidget && $parentClass->name != null) {
115
                $isWidget = $parentClass->name === 'Victoire\\Bundle\\WidgetBundle\\Model\\Widget';
116
            }
117
            if ($isWidget) {
118
                if ($this->widgetHelper->isEnabled(new $class->name())) {
119
                    $event = new WidgetAnnotationEvent(
120
                        $this->widgetHelper->getWidgetName(new $class->name()),
121
                        $this->loadReceiverProperties($class)
122
                    );
123
                    //dispatch victoire.widget_annotation_load to save receiverProperties in cache
124
                    $this->eventDispatcher->dispatch('victoire.widget_annotation_load', $event);
125
                } else {
126
                    error_log(sprintf('Widget name not found for widget %s. Is this widget declared in AppKernel ?', $class->name));
127
                }
128
            }
129
        }
130
    }
131
132
    /**
133
     * Load receiver properties and NotBlank constraints from ReflectionClass.
134
     *
135
     * @param \ReflectionClass $class
136
     *
137
     * @throws AnnotationException
138
     *
139
     * @return array
140
     */
141
    protected function loadReceiverProperties(\ReflectionClass $class)
142
    {
143
        $receiverPropertiesTypes = [];
144
        $properties = $class->getProperties();
145
        //Store receiver properties
146
        foreach ($properties as $property) {
147
            $annotations = $this->reader->getPropertyAnnotations($property);
148
            foreach ($annotations as $key => $annotationObj) {
149
                if ($annotationObj instanceof ReceiverPropertyAnnotation && !in_array($class, $receiverPropertiesTypes)) {
150
                    if (!$annotations[$key]->getTypes()) {
151
                        $message = $class->name.':$'.$property->name.'" field';
152
                        throw AnnotationException::requiredError('type', 'ReceiverProperty annotation', $message, 'array or string');
153
                    }
154
                    foreach ($annotations[$key]->getTypes() as $type) {
155
                        $receiverProperty = new ReceiverProperty();
156
                        $receiverProperty->setFieldName($property->name);
157
                        $receiverPropertiesTypes[$type][] = $receiverProperty;
158
                    }
159
                }
160
            }
161
        }
162
        //Set receiver properties as required if necessary
163
        foreach ($receiverPropertiesTypes as $type => $receiverProperties) {
164
            /* @var ReceiverProperty[] $receiverProperties */
165
            foreach ($receiverProperties as $receiverProperty) {
166
                $receiverPropertyName = $receiverProperty->getFieldName();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $receiverPropertyName is correct as $receiverProperty->getFieldName() (which targets Victoire\Bundle\Business...roperty::getFieldName()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
167
                $refProperty = $class->getProperty($receiverPropertyName);
168
                $annotations = $this->reader->getPropertyAnnotations($refProperty);
169
                foreach ($annotations as $key => $annotationObj) {
170
                    if ($annotationObj instanceof Column && $annotationObj->nullable === false) {
171
                        throw new Exception(sprintf(
172
                            'Property "%s" in class "%s" has a @ReceiverProperty annotation and by consequence must have "nullable=true" for ORM\Column annotation',
173
                            $refProperty->name,
174
                            $refProperty->class
175
                        ));
176
                    } elseif ($annotationObj instanceof NotBlank) {
177
                        throw new Exception(sprintf(
178
                            'Property "%s" in class "%s" has a @ReceiverProperty annotation and by consequence can not use NotBlank annotation',
179
                            $refProperty->name,
180
                            $refProperty->class
181
                        ));
182
                    } elseif ($annotationObj instanceof NotNull) {
183
                        throw new Exception(sprintf(
184
                            'Property "%s" in class "%s" has a @ReceiverProperty annotation and by consequence can not use NotNull annotation',
185
                            $refProperty->name,
186
                            $refProperty->class
187
                        ));
188
                    } elseif ($annotationObj instanceof ReceiverPropertyAnnotation && $annotationObj->isRequired()) {
189
                        $receiverProperty->setRequired(true);
190
                    }
191
                }
192
            }
193
        }
194
195
        return $receiverPropertiesTypes;
196
    }
197
}
198