Completed
Branch feature/split-orm (60a911)
by Anton
03:15
created

ReflectionEntity::getFillable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
namespace Spiral\Models\Reflections;
9
10
use Spiral\Models\Prototypes\AbstractEntity;
11
12
/**
13
 * Provides ability to generate entity schema based on given entity class and default property
14
 * values, support value inheritance!
15
 *
16
 * @method bool isAbstract()
17
 * @method string getName()
18
 * @method string getShortName()
19
 * @method bool isSubclassOf($class)
20
 * @method bool hasConstant($name)
21
 * @method mixed getConstant($name)
22
 * @method \ReflectionMethod[] getMethods()
23
 * @method \ReflectionClass|null getParentClass()
24
 */
25
class ReflectionEntity
26
{
27
    /**
28
     * Required to validly merge parent and children attributes.
29
     */
30
    const BASE_CLASS = AbstractEntity::class;
31
32
    /**
33
     * Properties cache.
34
     *
35
     * @invisible
36
     *
37
     * @var array
38
     */
39
    private $cache = [];
40
41
    /**
42
     * @var \ReflectionClass
43
     */
44
    private $reflection = null;
45
46
    /**
47
     * Only support SchematicEntity classes!
48
     *
49
     * @param string $class
50
     */
51
    public function __construct(string $class)
52
    {
53
        $this->reflection = new \ReflectionClass($class);
54
    }
55
56
    /**
57
     * @return \ReflectionClass
58
     */
59
    public function getReflection(): \ReflectionClass
60
    {
61
        return $this->reflection;
62
    }
63
64
    /**
65
     * @return array|string
66
     */
67
    public function getSecured()
68
    {
69
        if ($this->getProperty('secured', true) === '*') {
70
            return $this->getProperty('secured', true);
71
        }
72
73
        return array_unique((array)$this->getProperty('secured', true));
74
    }
75
76
    /**
77
     * @return array
78
     */
79
    public function getFillable(): array
80
    {
81
        return array_unique((array)$this->getProperty('fillable', true));
82
    }
83
84
    /**
85
     * @return array
86
     */
87
    public function getHidden(): array
88
    {
89
        return array_unique((array)$this->getProperty('hidden', true));
90
    }
91
92
    /**
93
     * @return array
94
     */
95
    public function getSetters(): array
96
    {
97
        return $this->getMutators()[AbstractEntity::MUTATOR_SETTER];
98
    }
99
100
    /**
101
     * @return array
102
     */
103
    public function getGetters(): array
104
    {
105
        return $this->getMutators()[AbstractEntity::MUTATOR_GETTER];
106
    }
107
108
    /**
109
     * @return array
110
     */
111
    public function getAccessors(): array
112
    {
113
        return $this->getMutators()[AbstractEntity::MUTATOR_ACCESSOR];
114
    }
115
116
    /**
117
     * Get methods declared in current class and exclude methods declared in parents.
118
     *
119
     * @return \ReflectionMethod[]
120
     */
121
    public function declareMethods(): array
122
    {
123
        $methods = [];
124
        foreach ($this->getMethods() as $method) {
125
            if ($method->getDeclaringClass()->getName() != $this->getName()) {
126
                continue;
127
            }
128
129
            $methods[] = $method;
130
        }
131
132
        return $methods;
133
    }
134
135
    /**
136
     * Fields associated with their type.
137
     *
138
     * @return array
139
     */
140
    public function getFields(): array
141
    {
142
        //Default property to store schema
143
        return (array)$this->getProperty('schema', true);
144
    }
145
146
    /**
147
     * Model mutators grouped by their type.
148
     *
149
     * @return array
150
     */
151
    public function getMutators(): array
152
    {
153
        $mutators = [
154
            AbstractEntity::MUTATOR_GETTER   => [],
155
            AbstractEntity::MUTATOR_SETTER   => [],
156
            AbstractEntity::MUTATOR_ACCESSOR => [],
157
        ];
158
159 View Code Duplication
        foreach ((array)$this->getProperty('getters', true) as $field => $filter) {
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...
160
            $mutators[AbstractEntity::MUTATOR_GETTER][$field] = $filter;
161
        }
162
163 View Code Duplication
        foreach ((array)$this->getProperty('setters', true) as $field => $filter) {
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...
164
            $mutators[AbstractEntity::MUTATOR_SETTER][$field] = $filter;
165
        }
166
167 View Code Duplication
        foreach ((array)$this->getProperty('accessors', true) as $field => $filter) {
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...
168
            $mutators[AbstractEntity::MUTATOR_ACCESSOR][$field] = $filter;
169
        }
170
171
        return $mutators;
172
    }
173
174
    /**
175
     * Read default model property value, will read "protected" and "private" properties. Method
176
     * raises entity event "describe" to allow it traits modify needed values.
177
     *
178
     * @param string $property Property name.
179
     * @param bool   $merge    If true value will be merged with all parent declarations.
180
     *
181
     * @return mixed
182
     */
183
    public function getProperty(string $property, bool $merge = false)
184
    {
185
        if (isset($this->cache[$property])) {
186
            //Property merging and trait events are pretty slow
187
            return $this->cache[$property];
188
        }
189
190
        $properties = $this->reflection->getDefaultProperties();
191
        $constants = $this->reflection->getConstants();
192
193
        if (isset($properties[$property])) {
194
            //Read from default value
195
            $value = $properties[$property];
196
        } elseif (isset($constants[strtoupper($property)])) {
197
            //Read from a constant
198
            $value = $constants[strtoupper($property)];
199
        } else {
200
            return null;
201
        }
202
203
        //Merge with parent value requested
204
        if ($merge && is_array($value) && !empty($parent = $this->parentReflection())) {
205
            $parentValue = $parent->getProperty($property, $merge);
206
207
            if (is_array($parentValue)) {
208
                //Class values prior to parent values
209
                $value = array_merge($parentValue, $value);
210
            }
211
        }
212
213
        //To let traits apply schema changes
214
        return $this->cache[$property] = call_user_func(
215
            [$this->getName(), 'describeProperty'], $this, $property, $value
216
        );
217
    }
218
219
    /**
220
     * Parent entity schema/
221
     *
222
     * @return ReflectionEntity|null
223
     */
224
    public function parentReflection()
225
    {
226
        $parentClass = $this->reflection->getParentClass();
227
228
        if (!empty($parentClass) && $parentClass->getName() != static::BASE_CLASS) {
229
            $parent = clone $this;
230
            $parent->reflection = $this->getParentClass();
231
232
            return $parent;
233
        }
234
235
        return null;
236
    }
237
238
    /**
239
     * Bypassing call to reflection.
240
     *
241
     * @param string $name
242
     * @param array  $arguments
243
     *
244
     * @return mixed
245
     */
246
    public function __call(string $name, array $arguments)
247
    {
248
        return call_user_func_array([$this->reflection, $name], $arguments);
249
    }
250
251
    /**
252
     * @return string
253
     */
254
    public function __toString(): string
255
    {
256
        return $this->getName();
257
    }
258
259
    /**
260
     * Cloning and flushing cache.
261
     */
262
    public function __clone()
263
    {
264
        $this->cache = [];
265
    }
266
}