Failed Conditions
Pull Request — develop (#6719)
by Marco
65:21
created

ComponentMetadata::addDeclaredProperty()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.2
c 0
b 0
f 0
cc 3
eloc 11
nc 3
nop 1
1
<?php
2
3
4
declare(strict_types=1);
5
6
namespace Doctrine\ORM\Mapping;
7
8
/**
9
 * A <tt>ComponentMetadata</tt> instance holds object-relational property mapping.
10
 *
11
 * @package Doctrine\ORM\Mapping
12
 * @since 3.0
13
 *
14
 * @author Guilherme Blanco <[email protected]>
15
 */
16
abstract class ComponentMetadata
17
{
18
    /**
19
     * @var string
20
     */
21
    protected $className;
22
23
    /**
24
     * @var ComponentMetadata|null
25
     */
26
    protected $parent;
27
28
    /**
29
     * The ReflectionClass instance of the component class.
30
     *
31
     * @var \ReflectionClass|null
32
     */
33
    protected $reflectionClass;
34
35
    /**
36
     * @var CacheMetadata|null
37
     */
38
    protected $cache;
39
40
    /**
41
     * @var array<string, Property>
42
     */
43
    protected $declaredProperties = [];
44
45
    /**
46
     * ComponentMetadata constructor.
47
     *
48
     * @param string                       $className
49
     * @param ClassMetadataBuildingContext $metadataBuildingContext
50
     */
51
    public function __construct(string $className, ClassMetadataBuildingContext $metadataBuildingContext)
52
    {
53
        $reflectionService = $metadataBuildingContext->getReflectionService();
54
55
        $this->reflectionClass = $reflectionService->getClass($className);
56
        $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...
57
    }
58
59
    /**
60
     * @return string
61
     */
62
    public function getClassName() : string
63
    {
64
        return $this->className;
65
    }
66
67
    /**
68
     * @param ComponentMetadata $parent
69
     */
70
    public function setParent(ComponentMetadata $parent) : void
71
    {
72
        $this->parent = $parent;
73
    }
74
75
    /**
76
     * @return ComponentMetadata|null
77
     */
78
    public function getParent() : ?ComponentMetadata
79
    {
80
        return $this->parent;
81
    }
82
83
    /**
84
     * @return \ReflectionClass|null
85
     */
86
    public function getReflectionClass() : ?\ReflectionClass
87
    {
88
        return $this->reflectionClass;
89
    }
90
91
    /**
92
     * @param CacheMetadata|null $cache
93
     *
94
     * @return void
95
     */
96
    public function setCache(?CacheMetadata $cache = null) : void
97
    {
98
        $this->cache = $cache;
99
    }
100
101
    /**
102
     * @return CacheMetadata|null
103
     */
104
    public function getCache(): ?CacheMetadata
105
    {
106
        return $this->cache;
107
    }
108
109
    /**
110
     * @return iterable
111
     */
112
    public function getDeclaredPropertiesIterator() : iterable
113
    {
114
        foreach ($this->declaredProperties as $name => $property) {
115
            yield $name => $property;
116
        }
117
    }
118
119
    /**
120
     * @param Property $property
121
     *
122
     * @throws \ReflectionException
123
     * @throws MappingException
124
     */
125
    public function addDeclaredProperty(Property $property) : void
126
    {
127
        $className    = $this->getClassName();
128
        $propertyName = $property->getName();
129
130
        // @todo guilhermeblanco Switch to hasProperty once inherited properties are not being mapped on child classes
131
        if ($this->hasDeclaredProperty($propertyName)) {
132
            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...
133
        }
134
135
        $property->setDeclaringClass($this);
136
137
        if ($this->reflectionClass) {
138
            $reflectionProperty = new \ReflectionProperty($className, $propertyName);
139
140
            $reflectionProperty->setAccessible(true);
141
142
            $property->setReflectionProperty($reflectionProperty);
143
        }
144
145
        $this->declaredProperties[$propertyName] = $property;
146
    }
147
148
    /**
149
     * @param string $propertyName
150
     *
151
     * @return bool
152
     */
153
    public function hasDeclaredProperty(string $propertyName) : bool
154
    {
155
        return isset($this->declaredProperties[$propertyName]);
156
    }
157
158
    /**
159
     * @return iterable
160
     */
161
    public function getPropertiesIterator() : iterable
162
    {
163
        if ($this->parent) {
164
            yield from $this->parent->getPropertiesIterator();
165
        }
166
167
        yield from $this->getDeclaredPropertiesIterator();
168
    }
169
170
    /**
171
     * @param string $propertyName
172
     *
173
     * @return null|Property
174
     */
175
    public function getProperty(string $propertyName) : ?Property
176
    {
177
        if (isset($this->declaredProperties[$propertyName])) {
178
            return $this->declaredProperties[$propertyName];
179
        }
180
181
        if ($this->parent) {
182
            return $this->parent->getProperty($propertyName);
183
        }
184
185
        return null;
186
    }
187
188
    /**
189
     * @param string $propertyName
190
     *
191
     * @return bool
192
     */
193
    public function hasProperty(string $propertyName) : bool
194
    {
195
        if (isset($this->declaredProperties[$propertyName])) {
196
            return true;
197
        }
198
199
        return $this->parent && $this->parent->hasProperty($propertyName);
200
    }
201
202
    /**
203
     * @return \ArrayIterator
204
     */
205
    public function getColumnsIterator() : \ArrayIterator
206
    {
207
        $iterator = new \ArrayIterator();
208
209
        // @todo guilhermeblanco Must be switched to getPropertiesIterator once class only has its declared properties
210
        foreach ($this->getDeclaredPropertiesIterator() as $property) {
211
            switch (true) {
212
                case ($property instanceof FieldMetadata):
213
                    $iterator->offsetSet($property->getColumnName(), $property);
214
                    break;
215
216
                case ($property instanceof ToOneAssociationMetadata && $property->isOwningSide()):
217
                    foreach ($property->getJoinColumns() as $joinColumn) {
218
                        /** @var JoinColumnMetadata $joinColumn */
219
                        $iterator->offsetSet($joinColumn->getColumnName(), $joinColumn);
220
                    }
221
222
                    break;
223
            }
224
        }
225
226
        return $iterator;
227
    }
228
229
    /**
230
     * @param string|null $className
231
     *
232
     * @return string|null null if the input value is null
233
     */
234
    public function fullyQualifiedClassName(?string $className) : ?string
235
    {
236
        if ($className === null || ! $this->reflectionClass) {
237
            return $className;
238
        }
239
240
        $namespaceName  = $this->reflectionClass->getNamespaceName();
241
        $finalClassName = ($className !== null && strpos($className, '\\') === false && $namespaceName)
242
            ? sprintf('%s\\%s', $namespaceName, $className)
243
            : $className
244
        ;
245
246
        return ltrim($finalClassName, '\\');
247
    }
248
}
249