Failed Conditions
Push — develop ( ba9041...24f682 )
by Guilherme
64:30
created

ComponentMetadata::hasProperty()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 3
nop 1
1
<?php
2
3
4
declare(strict_types=1);
5
6
namespace Doctrine\ORM\Mapping;
7
use Doctrine\ORM\Reflection\RuntimePublicReflectionProperty;
8
9
/**
10
 * A <tt>ComponentMetadata</tt> instance holds object-relational property mapping.
11
 *
12
 * @package Doctrine\ORM\Mapping
13
 * @since 3.0
14
 *
15
 * @author Guilherme Blanco <[email protected]>
16
 */
17
abstract class ComponentMetadata
18
{
19
    /**
20
     * @var string
21
     */
22
    protected $className;
23
24
    /**
25
     * @var ComponentMetadata|null
26
     */
27
    protected $parent;
28
29
    /**
30
     * The ReflectionClass instance of the component class.
31
     *
32
     * @var \ReflectionClass|null
33
     */
34
    protected $reflectionClass;
35
36
    /**
37
     * @var CacheMetadata|null
38
     */
39
    protected $cache;
40
41
    /**
42
     * @var array<string, Property>
43
     */
44
    protected $declaredProperties = [];
45
46
    /**
47
     * ComponentMetadata constructor.
48
     *
49
     * @param string                       $className
50
     * @param ClassMetadataBuildingContext $metadataBuildingContext
51
     */
52
    public function __construct(string $className, ClassMetadataBuildingContext $metadataBuildingContext)
53
    {
54
        $reflectionService = $metadataBuildingContext->getReflectionService();
55
56
        $this->reflectionClass = $reflectionService->getClass($className);
57
        $this->className       = $this->reflectionClass ? $this->reflectionClass->getName() : $className;
0 ignored issues
show
Bug introduced by
Consider using $this->reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
58
    }
59
60
    /**
61
     * @return string
62
     */
63
    public function getClassName() : string
64
    {
65
        return $this->className;
66
    }
67
68
    /**
69
     * @param ComponentMetadata $parent
70
     */
71
    public function setParent(ComponentMetadata $parent) : void
72
    {
73
        $this->parent = $parent;
74
    }
75
76
    /**
77
     * @return ComponentMetadata|null
78
     */
79
    public function getParent() : ?ComponentMetadata
80
    {
81
        return $this->parent;
82
    }
83
84
    /**
85
     * @return \ReflectionClass|null
86
     */
87
    public function getReflectionClass() : ?\ReflectionClass
88
    {
89
        return $this->reflectionClass;
90
    }
91
92
    /**
93
     * @param CacheMetadata|null $cache
94
     *
95
     * @return void
96
     */
97
    public function setCache(?CacheMetadata $cache = null) : void
98
    {
99
        $this->cache = $cache;
100
    }
101
102
    /**
103
     * @return CacheMetadata|null
104
     */
105
    public function getCache(): ?CacheMetadata
106
    {
107
        return $this->cache;
108
    }
109
110
    /**
111
     * @return \ArrayIterator
112
     */
113
    public function getDeclaredPropertiesIterator() : \ArrayIterator
114
    {
115
        return new \ArrayIterator($this->declaredProperties);
116
    }
117
118
    /**
119
     * @param Property $property
120
     *
121
     * @throws \ReflectionException
122
     * @throws MappingException
123
     */
124
    public function addDeclaredProperty(Property $property) : void
125
    {
126
        $className    = $this->getClassName();
127
        $propertyName = $property->getName();
128
129
        // @todo guilhermeblanco Switch to hasProperty once inherited properties are not being mapped on child classes
130
        if ($this->hasDeclaredProperty($propertyName)) {
131
            throw MappingException::duplicateProperty($className, $this->getProperty($propertyName));
0 ignored issues
show
Bug introduced by
It seems like $this->getProperty($propertyName) can be null; however, duplicateProperty() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
132
        }
133
134
        $property->setDeclaringClass($this);
135
136
        if ($this->reflectionClass) {
137
            $reflectionProperty = new \ReflectionProperty($className, $propertyName);
138
139
            if ($reflectionProperty->isPublic()) {
140
                $reflectionProperty = new RuntimePublicReflectionProperty($className, $propertyName);
141
            }
142
143
            $reflectionProperty->setAccessible(true);
144
145
            $property->setReflectionProperty($reflectionProperty);
146
        }
147
148
        $this->declaredProperties[$propertyName] = $property;
149
    }
150
151
    /**
152
     * @param string $propertyName
153
     *
154
     * @return bool
155
     */
156
    public function hasDeclaredProperty(string $propertyName) : bool
157
    {
158
        return isset($this->declaredProperties[$propertyName]);
159
    }
160
161
    /**
162
     * @return \Iterator
163
     */
164
    public function getPropertiesIterator() : \Iterator
165
    {
166
        $declaredPropertiesIterator = $this->getDeclaredPropertiesIterator();
167
168
        if (! $this->parent) {
169
            return $declaredPropertiesIterator;
170
        }
171
172
        $iterator = new \AppendIterator();
173
174
        $iterator->append($this->parent->getPropertiesIterator());
175
        $iterator->append($declaredPropertiesIterator);
176
177
        return $iterator;
178
    }
179
180
    /**
181
     * @param string $propertyName
182
     *
183
     * @return null|Property
184
     */
185
    public function getProperty(string $propertyName) : ?Property
186
    {
187
        if (isset($this->declaredProperties[$propertyName])) {
188
            return $this->declaredProperties[$propertyName];
189
        }
190
191
        if ($this->parent) {
192
            return $this->parent->getProperty($propertyName);
193
        }
194
195
        return null;
196
    }
197
198
    /**
199
     * @param string $propertyName
200
     *
201
     * @return bool
202
     */
203
    public function hasProperty(string $propertyName) : bool
204
    {
205
        if (isset($this->declaredProperties[$propertyName])) {
206
            return true;
207
        }
208
209
        return $this->parent && $this->parent->hasProperty($propertyName);
210
    }
211
212
    /**
213
     * @return \ArrayIterator
214
     */
215
    public function getColumnsIterator() : \ArrayIterator
216
    {
217
        $iterator = new \ArrayIterator();
218
219
        // @todo guilhermeblanco Must be switched to getPropertiesIterator once class only has its declared properties
220
        foreach ($this->getDeclaredPropertiesIterator() as $property) {
221
            switch (true) {
222
                case ($property instanceof FieldMetadata):
223
                    $iterator->offsetSet($property->getColumnName(), $property);
224
                    break;
225
226
                case ($property instanceof ToOneAssociationMetadata && $property->isOwningSide()):
227
                    foreach ($property->getJoinColumns() as $joinColumn) {
228
                        /** @var JoinColumnMetadata $joinColumn */
229
                        $iterator->offsetSet($joinColumn->getColumnName(), $joinColumn);
230
                    }
231
232
                    break;
233
            }
234
        }
235
236
        return $iterator;
237
    }
238
239
    /**
240
     * @param string|null $className
241
     *
242
     * @return string|null null if the input value is null
243
     */
244
    public function fullyQualifiedClassName(?string $className) : ?string
245
    {
246
        if ($className === null || ! $this->reflectionClass) {
247
            return $className;
248
        }
249
250
        $namespaceName  = $this->reflectionClass->getNamespaceName();
251
        $finalClassName = ($className !== null && strpos($className, '\\') === false && $namespaceName)
252
            ? sprintf('%s\\%s', $namespaceName, $className)
253
            : $className
254
        ;
255
256
        return ltrim($finalClassName, '\\');
257
    }
258
}
259