Completed
Push — master ( 646a79...3fa8e1 )
by Iqbal
08:33
created

SerializerTrait   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 6
Bugs 1 Features 1
Metric Value
wmc 26
c 6
b 1
f 1
lcom 1
cbo 3
dl 0
loc 177
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A deserialize() 0 13 2
A buildParameters() 0 9 2
A buildValue() 0 14 2
D buildObjectParameter() 0 26 9
A serialize() 0 21 4
A normalize() 0 12 3
A resolveFlat() 0 14 3
A isInternal() 0 4 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 ($class->hasMethod('__toString')) {
104
            return (string) $value;
105
        }
106
107
        if (in_array('Borobudur\Cqrs\IdentifierInterface', class_implements($className))) {
108
            if (is_array($value) && isset($value[$name]) && is_string($value[$name])) {
109
                return $className::{'fromString'}($value[$name]);
110
            }
111
        }
112
113
        if (in_array('Borobudur\Cqrs\Serializer\SerializableInterface', class_implements($className))) {
114
            return $className::{'deserialize'}(new ParameterBag($value));
115
        }
116
117
        if ('DateTime' === $className && isset($value[$name])) {
118
            $value[$name] = (array) $value[$name];
119
120
            return new DateTime($value[$name]['date'], new DateTimeZone($value[$name]['timezone']));
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
        return $value;
169
    }
170
171
    /**
172
     * @param array $data
173
     *
174
     * @return array
175
     */
176
    protected function resolveFlat(array $data)
177
    {
178
        $flat = array();
179
        foreach ($data as $index => $value) {
180
            if (is_array($value)) {
181
                $flat = array_merge($flat, $this->resolveFlat($value));
182
                continue;
183
            }
184
185
            $flat[$index] = $value;
186
        }
187
188
        return $flat;
189
    }
190
191
    /**
192
     * Check if property is internal use (@internal annotation).
193
     *
194
     * @param ReflectionProperty $property
195
     *
196
     * @return bool
197
     */
198
    protected function isInternal(ReflectionProperty $property)
199
    {
200
        return (bool) preg_match('#@internal\n#s', $property->getDocComment());
201
    }
202
}
203