Completed
Push — master ( 2f25cf...59cc7f )
by Marco
43:54 queued 18:55
created

Properties::onlyNullableProperties()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
c 0
b 0
f 0
ccs 3
cts 3
cp 1
rs 9.9
cc 2
nc 1
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProxyManager\ProxyGenerator\Util;
6
7
use ReflectionClass;
8
use ReflectionProperty;
9
use ReflectionType;
10
use function array_filter;
11
use function array_flip;
12
use function array_key_exists;
13
use function array_keys;
14
use function array_map;
15
use function array_merge;
16
use function array_values;
17
18
/**
19
 * DTO containing the list of all non-static proxy properties and utility methods to access them
20
 * in various formats/collections
21
 */
22
final class Properties
23
{
24
    /** @var array|ReflectionProperty[] */
25
    private $properties;
26
27 15
    /**
28
     * @param ReflectionProperty[] $properties
29 15
     */
30 15
    private function __construct(array $properties)
31
    {
32 15
        $this->properties = $properties;
33
    }
34 15
35 15
    public static function fromReflectionClass(ReflectionClass $reflection) : self
36
    {
37
        $class         = $reflection;
38 15
        $parentClasses = [];
39
40 15
        do {
41 15
            $parentClasses[] = $class;
42
43 15
            $class = $class->getParentClass();
44
        } while ($class);
45 15
46 15
        return new self(array_merge(
47
            ...array_map(static function (ReflectionClass $class) : array {
48 13
                return array_values(array_filter(
49 13
                    $class->getProperties(),
50 15
                    static function (ReflectionProperty $property) use ($class) : bool {
51
                        return $class->getName() === $property->getDeclaringClass()->getName()
52 15
                            && ! $property->isStatic();
53
                    }
54
                ));
55
            }, $parentClasses)
56
        ));
57
    }
58
59 5
    /**
60
     * @param string[] $excludedProperties
61 5
     */
62
    public function filter(array $excludedProperties) : self
63 5
    {
64 5
        $properties = $this->getInstanceProperties();
65
66
        foreach ($excludedProperties as $propertyName) {
67 5
            unset($properties[$propertyName]);
68
        }
69
70
        return new self($properties);
71
    }
72
73 9
    public function onlyNonReferenceableProperties() : self
74
    {
75 9
        return new self(array_filter($this->properties, static function (ReflectionProperty $property) : bool {
76
            if (! $property->hasType()) {
77 9
                return false;
78 8
            }
79 8
80
            return ! array_key_exists(
81
                $property->getName(),
82 7
                // https://bugs.php.net/bug.php?id=77673
83
                array_flip(array_keys($property->getDeclaringClass()->getDefaultProperties()))
84
            );
85 9
        }));
86
    }
87
88
    public function onlyPropertiesThatCanBeUnset() : self
89
    {
90
        return new self(array_filter($this->properties, static function (ReflectionProperty $property) : bool {
91 9
            if (! $property->hasType()) {
92
                return true;
93 9
            }
94
95 9
            return array_key_exists(
96 8
                $property->getName(),
97 8
                // https://bugs.php.net/bug.php?id=77673
98
                array_flip(array_keys($property->getDeclaringClass()->getDefaultProperties()))
99
            );
100 7
        }));
101
    }
102
103 9
    /**
104
     * Properties that cannot be referenced are non-nullable typed properties that aren't initialised
105
     */
106
    public function withoutNonReferenceableProperties() : self
107
    {
108
        return new self(array_filter($this->properties, static function (ReflectionProperty $property) : bool {
109 10
            if (! $property->hasType()) {
110
                return true;
111 10
            }
112
113 10
            /** @var ReflectionType $type */
114 10
            $type = $property->getType();
115 7
116
            if ($type->allowsNull()) {
117
                return true;
118 10
            }
119
120 10
            return array_key_exists(
121
                $property->getName(),
122
                // https://bugs.php.net/bug.php?id=77673
123 10
                array_flip(array_keys($property->getDeclaringClass()->getDefaultProperties()))
124
            );
125
        }));
126
    }
127
128
    public function onlyNullableProperties() : self
129 7
    {
130
        return new self(array_filter(
131 7
            $this->properties,
132
            static function (ReflectionProperty $property) : bool {
133
                $type = $property->getType();
134
135
                return $type === null || $type->allowsNull();
136
            }
137 2
        ));
138
    }
139 2
140
    public function onlyInstanceProperties() : self
141 2
    {
142 2
        return new self(array_values(array_merge($this->getAccessibleProperties(), $this->getPrivateProperties())));
143
    }
144 2
145
    public function empty() : bool
146
    {
147 2
        return $this->properties === [];
148
    }
149
150
    /**
151
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name
152
     */
153 6
    public function getPublicProperties() : array
154
    {
155 6
        $publicProperties = [];
156
157
        foreach ($this->properties as $property) {
158
            if (! $property->isPublic()) {
159
                continue;
160
            }
161
162
            $publicProperties[$property->getName()] = $property;
163
        }
164
165
        return $publicProperties;
166
    }
167
168
    /**
169
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
170
     */
171
    public function getProtectedProperties() : array
172
    {
173
        $protectedProperties = [];
174
175
        foreach ($this->properties as $property) {
176
            if (! $property->isProtected()) {
177
                continue;
178
            }
179
180
            $protectedProperties["\0*\0" . $property->getName()] = $property;
181
        }
182
183
        return $protectedProperties;
184
    }
185
186
    /**
187
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0ClassName\0propertyName)
188
     */
189
    public function getPrivateProperties() : array
190
    {
191
        $privateProperties = [];
192
193
        foreach ($this->properties as $property) {
194
            if (! $property->isPrivate()) {
195
                continue;
196
            }
197
198
            $declaringClass = $property->getDeclaringClass()->getName();
199
200
            $privateProperties["\0" . $declaringClass . "\0" . $property->getName()] = $property;
201
        }
202
203
        return $privateProperties;
204
    }
205
206
    /**
207
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
208
     */
209
    public function getAccessibleProperties() : array
210
    {
211
        return array_merge($this->getPublicProperties(), $this->getProtectedProperties());
212
    }
213
214
    /**
215
     * @return ReflectionProperty[][] indexed by class name and property name
216
     */
217
    public function getGroupedPrivateProperties() : array
218
    {
219
        $propertiesMap = [];
220
221
        foreach ($this->getPrivateProperties() as $property) {
222
            $class = &$propertiesMap[$property->getDeclaringClass()->getName()];
223
224
            $class[$property->getName()] = $property;
225
        }
226
227
        return $propertiesMap;
228
    }
229
230
    /**
231
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
232
     */
233
    public function getInstanceProperties() : array
234
    {
235
        return array_merge($this->getAccessibleProperties(), $this->getPrivateProperties());
236
    }
237
}
238