Completed
Push — master ( 161f33...20f2fb )
by Sébastien
09:38
created

AccessorGuesser   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 46
c 2
b 1
f 0
dl 0
loc 177
rs 10
wmc 22

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getPropertyAccessor() 0 14 3
A useClosureAccessor() 0 3 1
A getPropertyOwner() 0 9 3
A guessSetter() 0 9 2
A guessGetter() 0 13 3
B getMethodAccessor() 0 34 8
A guessAccessor() 0 8 2
1
<?php
2
3
namespace Bdf\Serializer\Util;
4
5
use Bdf\Serializer\PropertyAccessor\ClosureAccessor;
6
use Bdf\Serializer\PropertyAccessor\PropertyAccessorInterface;
7
use Bdf\Serializer\PropertyAccessor\DelegateAccessor;
8
use Bdf\Serializer\PropertyAccessor\PublicAccessor;
9
use Bdf\Serializer\PropertyAccessor\MethodAccessor;
10
use Bdf\Serializer\PropertyAccessor\ReflectionAccessor;
11
use ReflectionClass;
12
13
/**
14
 * AccessorGuesser
15
 */
16
class AccessorGuesser
17
{
18
    /**
19
     * Allows closure accessor
20
     *
21
     * @var bool
22
     */
23
    private static $useClosure = false;
24
25
    /**
26
     * Guess which property accessor suit the property
27
     *
28
     * @param ReflectionClass $reflection
29
     * @param string $property
30
     * @param array $options
31
     *
32
     * @return PropertyAccessorInterface
33
     *
34
     * @deprecated Use getMethodAccessor or getPropertyAccessor instead of
35
     */
36
    public static function guessAccessor(ReflectionClass $reflection, string $property, array $options = null): PropertyAccessorInterface
37
    {
38
        // use reflection accessor if not set. Guess if property is public to use tue public accessor
39
        if ($options === null) {
40
            return self::getPropertyAccessor($reflection, $property);
41
        }
42
43
        return self::getMethodAccessor($reflection, $property, $options['reader'] ?? null, $options['writer'] ?? null, $options['readOnly'] ?? false);
44
    }
45
46
    /**
47
     * Guess which method accessor suit the property
48
     *
49
     * @param ReflectionClass $reflection
50
     * @param string $property
51
     * @param PropertyAccessorInterface|string|null $getter
52
     * @param PropertyAccessorInterface|string|null $setter
53
     *
54
     * @return PropertyAccessorInterface
55
     */
56
    public static function getMethodAccessor(ReflectionClass $reflection, $property, $getter, $setter, bool $readOnly = false): PropertyAccessorInterface
57
    {
58
        // If accessor is an array, it should have reader and writer keys.
59
        // The reader and the writer are methods, the method accessor will be used
60
        // Otherwise, the delegate accessor will be built with defined accessors.
61
        if (is_string($getter) && is_string($setter)) {
62
            return new MethodAccessor($reflection->name, $property, $getter, $setter);
63
        }
64
65
        if ($getter !== null) {
66
            if ($getter instanceof PropertyAccessorInterface) {
67
                $reader = $getter;
68
            } else {
69
                $reader = new MethodAccessor($reflection->name, $property, $getter);
70
            }
71
        } else {
72
            $reader = static::getPropertyAccessor($reflection, $property);
73
        }
74
75
        if ($readOnly) {
76
            return $reader;
77
        }
78
79
        if ($setter !== null) {
80
            if ($setter instanceof PropertyAccessorInterface) {
81
                $writter = $setter;
82
            } else {
83
                $writter = new MethodAccessor($reflection->name, $property, null, $setter);
84
            }
85
        } else {
86
            $writter = static::getPropertyAccessor($reflection, $property);
87
        }
88
89
        return new DelegateAccessor($reader, $writter);
90
    }
91
92
    /**
93
     * Guess which property accessor suit the property
94
     *
95
     * @param ReflectionClass $reflection
96
     * @param string $property
97
     *
98
     * @return PropertyAccessorInterface
99
     *
100
     * @throws \ReflectionException
101
     */
102
    public static function getPropertyAccessor(ReflectionClass $reflection, string $property): PropertyAccessorInterface
103
    {
104
        $reflection = self::getPropertyOwner($reflection, $property);
105
106
        // use reflection accessor if not set. Guess if property is public to use tue public accessor
107
        if ($reflection->getProperty($property)->isPublic()) {
108
            return new PublicAccessor($reflection->name, $property);
109
        }
110
111
        if (self::$useClosure) {
112
            return new ClosureAccessor($reflection->name, $property);
113
        }
114
115
        return new ReflectionAccessor($reflection->name, $property);
116
    }
117
118
    /**
119
     * Try to guess the setter method
120
     *
121
     * @param string $class
122
     * @param string $property
123
     *
124
     * @return null|string
125
     *
126
     * @todo Manage magic method __set
127
     */
128
    public static function guessSetter(string $class, string $property): ?string
129
    {
130
        $method = 'set'.ucfirst($property);
131
132
        if (method_exists($class, $method)) {
133
            return $method;
134
        }
135
136
        return null;
137
    }
138
139
    /**
140
     * Try to guess the getter method
141
     *
142
     * @param string $class
143
     * @param string $property
144
     *
145
     * @return null|string
146
     *
147
     * @todo Manage magic method __get
148
     */
149
    public static function guessGetter(string $class, string $property): ?string
150
    {
151
        if (method_exists($class, $property)) {
152
            return $property;
153
        }
154
155
        $method = 'get'.ucfirst($property);
156
157
        if (method_exists($class, $method)) {
158
            return $method;
159
        }
160
161
        return null;
162
    }
163
164
    /**
165
     * Enable/disable the closure accessor
166
     *
167
     * @param bool $flag
168
     */
169
    public static function useClosureAccessor($flag): void
170
    {
171
        self::$useClosure = (bool)$flag;
172
    }
173
174
    /**
175
     * Get the reflection that ownes the property
176
     *
177
     * @param ReflectionClass $reflection
178
     * @param string $property
179
     *
180
     * @return ReflectionClass
181
     *
182
     * @throws \LogicException if property does not belongs to this hierarchy
183
     */
184
    private static function getPropertyOwner($reflection, $property): ReflectionClass
185
    {
186
        do {
187
            if ($reflection->hasProperty($property)) {
188
                return $reflection;
189
            }
190
        } while ($reflection = $reflection->getParentClass());
191
192
        throw new \LogicException('No reflection found for property "'.$property.'"');
193
    }
194
}