Completed
Branch master (2d89f6)
by Vladislav
03:27 queued 01:30
created

Hydrator::isMethodExistsAndCallable()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 7
nc 4
nop 2
1
<?php declare(strict_types=1);
2
3
namespace RainbowCat\Hydrator;
4
5
use ReflectionClass;
6
use ReflectionException;
7
use InvalidArgumentException;
8
9
class Hydrator
10
{
11
    /**
12
     * @var string
13
     */
14
    protected $className;
15
    
16
    /**
17
     * @var array
18
     */
19
    protected $mapping;
20
    
21
    /**
22
     * @var bool
23
     */
24
    protected $ignoreSetters;
25
    
26
    /**
27
     * @var bool
28
     */
29
    protected $ignoreGetters;
30
    
31
    /**
32
     * @var array
33
     */
34
    protected $reflectionProperties = [];
35
    
36
    /**
37
     * @var array
38
     */
39
    protected $propertySetters = [];
40
    
41
    /**
42
     * @var array
43
     */
44
    protected $propertyGetters = [];
45
    
46
    /**
47
     * (non-phpdoc)
48
     *
49
     * @param string $className 
50
     * @param array $mapping
51
     * @throws ReflectionException
52
     */
53
    public function __construct(
54
        string $className,
55
        array $mapping,
56
        bool $ignoreSetters = false,
57
        bool $ignoreGetters = false
58
    ) {
59
        $this->className = $className;
60
        
61
        $this->ignoreSetters = $ignoreSetters;
62
        $this->ignoreGetters = $ignoreGetters;
63
        
64
        $this->fixMapping($mapping);
65
        $this->initialize(new ReflectionClass($className));
66
    }
67
    
68
    /**
69
     * (non-phpdoc)
70
     *
71
     * @param object $object 
72
     * @param array $data
73
     * @return object
74
     * @throws InvalidArgumentException
75
     */
76
    public function hydrate($object, array $data)
77
    {
78
        $this->validateObject($object);
79
        
80
        foreach ($this->mapping as $dataKey => $propertyName) {
81
            if (isset($data[$dataKey])) {
82
                if (isset($this->propertySetters[$propertyName])) {
83
                    $object->{$this->propertySetters[$propertyName]}($data[$dataKey]);
84
                } elseif (isset($this->reflectionProperties[$propertyName])) {
85
                    $this->reflectionProperties[$propertyName]->setValue($object, $data[$dataKey]);
86
                } else {
87
                    throw new InvalidArgumentException('Class '. $this->className .' doesn\'t have property $'. $propertyName .'.');
88
                }
89
            }
90
        }
91
        
92
        return $object;
93
    }
94
    
95
    /**
96
     * (non-phpdoc)
97
     *
98
     * @param object $object 
99
     * @return array
100
     */
101
    public function extract($object): array
102
    {
103
        $this->validateObject($object);
104
        
105
        $data = [];
106
        
107
        foreach ($this->mapping as $dataKey => $propertyName) {
108
            if (isset($this->propertyGetters[$propertyName])) {
109
                $data[$dataKey] = $object->{$this->propertyGetters[$propertyName]}();
110
            } elseif (isset($this->reflectionProperties[$propertyName])) {
111
                $data[$dataKey] = $this->reflectionProperties[$propertyName]->getValue($object);
112
            }
113
        }
114
        
115
        return $data;
116
    }
117
    
118
    /**
119
     * (non-phpdoc)
120
     *
121
     * @param object $object 
122
     * @return void
123
     * @throws InvalidArgumentException
124
     */
125
    protected function validateObject($object)
126
    {
127
        if ( ! is_object($object) || $this->className !== get_class($object) ) {
128
            throw new InvalidArgumentException('Argument $object must be an instance of '. $this->className .'.');
129
        }
130
    }
131
    
132
    /**
133
     * (non-phpdoc)
134
     *
135
     * @param array $mapping
136
     * @return void
137
     */
138
    protected function fixMapping(array $mapping)
139
    {
140
        foreach ($mapping as $dataKey => $propertyName) {
141
            if (is_numeric($dataKey)) {
142
                $dataKey = $propertyName;
143
            }
144
            
145
            $this->mapping[$dataKey] = $propertyName;
146
        }
147
    }
148
    
149
    /**
150
     * (non-phpdoc)
151
     *
152
     * @param ReflectionClass $reflectionClass
153
     * @return void
154
     */
155
    protected function initialize(ReflectionClass $reflectionClass)
156
    {
157
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
158
            if ( ! $reflectionProperty->isStatic()) {
159
                $propertyName = $reflectionProperty->getName();
160
                
161
                $reflectionProperty->setAccessible(true);
162
                $this->reflectionProperties[$propertyName] = $reflectionProperty;
163
                
164
                $ucPropertyName = ucfirst($propertyName);
165
                
166
                if ( ! $this->ignoreSetters) {
167
                    $propertySetterName = 'set'. $ucPropertyName;
168
                    
169
                    if ($this->isMethodExistsAndCallable($reflectionClass, $propertySetterName)) {
170
                        $this->propertySetters[$propertyName] = $propertySetterName;
171
                    }
172
                }
173
                
174
                if ( ! $this->ignoreGetters) {
175
                    $propertyGetterName = 'get'. $ucPropertyName;
176
                    
177
                    if ($this->isMethodExistsAndCallable($reflectionClass, $propertyGetterName)) {
178
                        $this->propertyGetters[$propertyName] = $propertyGetterName;
179
                    }
180
                }
181
            }
182
        }
183
    }
184
    
185
    /**
186
     * (non-phpdoc)
187
     *
188
     * @param ReflectionClass $reflectionClass
189
     * @param string $methodName
190
     * @return void
191
     */
192
    protected function isMethodExistsAndCallable(ReflectionClass $reflectionClass, string $methodName): bool
193
    {
194
        if ($reflectionClass->hasMethod($methodName)) {
195
            $reflectionMethod = $reflectionClass->getMethod($methodName);
196
            
197
            $notAbstractOrStatic = ! $reflectionMethod->isAbstract() && ! $reflectionMethod->isStatic();
198
            
199
            if ($reflectionMethod->isPublic() && $notAbstractOrStatic) {
200
                return true;
201
            }
202
        }
203
        
204
        return false;
205
    }
206
    
207
}
208