Completed
Push — master ( 0866c4...69f81a )
by Alexey
22:16 queued 07:16
created

PlainArrayHydrator::parsePropAndClass()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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