Completed
Push — master ( af263b...6a40ff )
by Iqbal
17:45 queued 05:19
created

SerializerTrait::normalize()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 16
rs 8.8571
cc 5
eloc 8
nc 4
nop 1
1
<?php
2
/*
3
 * This file is part of the Borobudur-Cqrs package.
4
 *
5
 * (c) Hexacodelabs <http://hexacodelabs.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Borobudur\Cqrs\Serializer;
12
13
use Borobudur\Cqrs\IdentifierInterface;
14
use Borobudur\Cqrs\ParameterBag;
15
use DateTime;
16
use DateTimeZone;
17
use ReflectionClass;
18
use ReflectionObject;
19
use ReflectionParameter;
20
use ReflectionProperty;
21
22
/**
23
 * @author      Iqbal Maulana <[email protected]>
24
 * @created     8/18/15
25
 */
26
trait SerializerTrait
27
{
28
    use DataTypeCasterTrait;
29
30
    /**
31
     * {@inheritdoc}
32
     *
33
     * @return static
34
     */
35
    public static function deserialize(ParameterBag $data)
36
    {
37
        $class = get_called_class();
38
        $reflection = new ReflectionClass($class);
39
        $constructor = $reflection->getConstructor();
40
        $parameters = $constructor->getParameters();
41
42
        if (!empty($parameters)) {
43
            return $reflection->newInstanceArgs(self::buildParameters($parameters, $data));
44
        }
45
46
        return $reflection->newInstance();
47
    }
48
49
    /**
50
     * Build parameters.
51
     *
52
     * @param ReflectionParameter[] $parameters
53
     * @param ParameterBag          $data
54
     *
55
     * @return array
56
     */
57
    protected static function buildParameters(array $parameters, ParameterBag $data)
58
    {
59
        $built = array();
60
        foreach ($parameters as $param) {
61
            $built[] = self::buildValue($param, $data);
62
        }
63
64
        return $built;
65
    }
66
67
    /**
68
     * Build value.
69
     *
70
     * @param ReflectionParameter $parameter
71
     * @param ParameterBag        $data
72
     *
73
     * @return array|mixed|null
74
     */
75
    protected static function buildValue(ReflectionParameter $parameter, ParameterBag $data)
76
    {
77
        $class = $parameter->getClass();
78
        $name = $parameter->getName();
79
        $value = $data->get($name);
80
81
        if (null === $class) {
82
            return self::castType($value, $value, '');
83
        }
84
85
        $value = array_merge($data->all(), (array) $value);
86
87
        return self::buildObjectParameter($class, $value, $name);
88
    }
89
90
    /**
91
     * Build object parameter.
92
     *
93
     * @param ReflectionClass $class
94
     * @param mixed           $value
95
     * @param string          $name
96
     *
97
     * @return mixed|null
98
     */
99
    protected static function buildObjectParameter(ReflectionClass $class, $value, $name)
100
    {
101
        $className = $class->getName();
102
103
        if (in_array('Borobudur\Cqrs\IdentifierInterface', class_implements($className))) {
104
            if (is_array($value) && isset($value[$name]) && is_string($value[$name])) {
105
                return $className::{'fromString'}($value[$name]);
106
            }
107
        }
108
109
        if (in_array('Borobudur\Cqrs\Serializer\SerializableInterface', class_implements($className))) {
110
            return $className::{'deserialize'}(new ParameterBag($value));
111
        }
112
113
        if ('DateTime' === $className && isset($value[$name])) {
114
            $value[$name] = (array) $value[$name];
115
116
            return new DateTime($value[$name]['date'], new DateTimeZone($value[$name]['timezone']));
117
        }
118
119
        if ($class->hasMethod('__toString')) {
120
            return new $className($value[$name]);
121
        }
122
123
        return null;
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function serialize()
130
    {
131
        $reflection = new ReflectionObject($this);
132
        $properties = $reflection->getProperties();
133
        $data = array();
134
135
        foreach ($properties as $property) {
136
            if ($this->isInternal($property)) {
137
                continue;
138
            }
139
140
            $property->setAccessible(true);
141
            $data[$property->getName()] = $this->normalize($property->getValue($this));
142
        }
143
144
        if ($this instanceof FlatSerializerInterface) {
145
            return $this->resolveFlat($data);
146
        }
147
148
        return $data;
149
    }
150
151
    /**
152
     * Normalize value.
153
     *
154
     * @param $value
155
     *
156
     * @return array|string
157
     */
158
    protected function normalize($value)
159
    {
160
        if ($value instanceof IdentifierInterface) {
161
            return (string) $value;
162
        }
163
164
        if ($value instanceof SerializableInterface) {
165
            return $value->serialize();
166
        }
167
168
        if (is_object($value) && method_exists($value, '__toString')) {
169
            return (string) $value;
170
        }
171
172
        return $value;
173
    }
174
175
    /**
176
     * @param array $data
177
     *
178
     * @return array
179
     */
180
    protected function resolveFlat(array $data)
181
    {
182
        $flat = array();
183
        foreach ($data as $index => $value) {
184
            if (is_array($value)) {
185
                $flat = array_merge($flat, $this->resolveFlat($value));
186
                continue;
187
            }
188
189
            $flat[$index] = $value;
190
        }
191
192
        return $flat;
193
    }
194
195
    /**
196
     * Check if property is internal use (@internal annotation).
197
     *
198
     * @param ReflectionProperty $property
199
     *
200
     * @return bool
201
     */
202
    protected function isInternal(ReflectionProperty $property)
203
    {
204
        return (bool) preg_match('#@internal\n#s', $property->getDocComment());
205
    }
206
}
207