Completed
Push — master ( dc1e78...1d86fd )
by Alexey
17:07 queued 02:07
created

PlainArrayHydrator::getHydrator()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 26
rs 8.439
cc 6
eloc 16
nc 2
nop 1
1
<?php
2
/**
3
 * PlainArrayHydrator.php
4
 *
5
 * @date 25.12.2015 15:47:21
6
 */
7
8
namespace Sufir\Hydrator;
9
10
use Closure;
11
use ReflectionObject;
12
use ReflectionClass;
13
use Sufir\Hydrator\Instantiator;
14
use InvalidArgumentException;
15
16
/**
17
 * PlainArrayHydrator
18
 *
19
 * Обеспечивает наполнение и извлечение данных из объекта.
20
 *
21
 * @todo refactoring
22
 * @author Sklyarov Alexey <[email protected]>
23
 * @package Sufir\Hydrator
24
 */
25
class PlainArrayHydrator implements HydratorInterface
26
{
27
    /**
28
     * {@inhereitdoc}
29
     */
30
    public function hydrate(array $data, $object)
31
    {
32
        if (is_string($object)) {
33
            $object = (new Instantiator())->newInstance($object);
34
        }
35
36
        $className = get_class($object);
37
        $selfProperties = [];
38
        $subObjects = [];
39
40
        foreach ($data as $property => $value) {
41
            if (substr($property, 0, 1) !== '_') {
42
                $selfProperties[$property] = $value;
43
            } elseif (substr($property, 0, 2) === '__') {
44
                $sub = explode('_', trim($property, '_'));
45
                if (count($sub) === 2) {
46
                    $subObjects[$sub[0]][$sub[1]] = $value;
47
                }
48
            }
49
        }
50
51
        foreach ($subObjects as $class => $properties) {
52
            $class = $this->parsePropAndClass($class, $className);
53
            $selfProperties[$class['property']] = $this->hydrate($properties, $class['class']);
54
        }
55
56
        $hydrate = $this->getHydrator($className);
57
        $hydrate($object, $selfProperties);
58
59
        return $object;
60
    }
61
62
    /**
63
     * {@inhereitdoc}
64
     */
65
    public function extract($object)
66
    {
67
        $data = [];
68
        $reflection = new ReflectionObject($object);
69
        $class = $reflection->getName();
70
        $propertises = $reflection->getProperties();
71
72
        foreach ($propertises as $prop) {
73
            $prop->setAccessible(true);
74
            $value = $prop->getValue($object);
75
            $propName = $prop->getName();
76
77
            if (!is_object($value)) {
78
                $data[$propName] = $value;
79
            } else {
80
                $fullClassName = get_class($value);
81
                if (strpos($fullClassName, $class) === 0) {
82
                    $className = substr($fullClassName, strrpos($fullClassName, '\\')+1);
83
                } else {
84
                    $className = '\\' . $fullClassName;
85
                }
86
87
                $data['__' . lcfirst($propName) . ':' . $className] = $this->extract($value);
88
            }
89
        }
90
91
        foreach ($data as $key => $value) {
92
            if (substr($key, 0, 2) === '__' && is_array($value)) {
93
                foreach ($value as $subKey => $subVal) {
94
                    $data["{$key}_{$subKey}"] = $subVal;
95
                }
96
                unset($data[$key]);
97
            }
98
        }
99
100
        return $data;
101
    }
102
103
    protected function parsePropAndClass($className, $namespace = null)
104
    {
105
        $parts = explode(":", $className, 2);
106
        if (count($parts) !== 2) {
107
            throw new InvalidArgumentException(
108
                "Wrong property and type definition {$className}, must in format «__propertyName:SubClassName» or «__propertyName:\ClassName»"
109
            );
110
        }
111
112
        $prop = $parts[0];
113
        $class = $parts[1];
114
115
        if (strpos($class, '\\') === 0) {
116
            $namespace = '';
117
        } else {
118
            $namespace = trim($namespace, " \t\n\r\0\x0B\\\/") . '\\';
119
        }
120
121
        return [
122
            'property' => $prop,
123
            'class' => $namespace . $class,
124
        ];
125
    }
126
127
    protected function getHydrator($className)
128
    {
129
        $reflection = new ReflectionClass($className);
130
131
        if($reflection->isUserDefined()) {
132
            return Closure::bind(function ($object, $data) {
133
                foreach ($data as $property => $value) {
134
                    if (property_exists($object, $property)) {
135
                        $object->{$property} = $value;
136
                    }
137
                }
138
            }, null, $className);
139
        } else {
140
            return function ($object, $data) {
141
                $reflection = new ReflectionObject($object);
142
143
                foreach ($data as $propertyName => $value) {
144
                    if ($reflection->hasProperty($propertyName)) {
145
                        $property = $reflection->getProperty($propertyName);
146
                        $property->setAccessible(true);
147
                        $property->setValue($object, $value);
148
                    }
149
                }
150
            };
151
        }
152
    }
153
}
154