Completed
Push — master ( a4b03a...366fbf )
by Rafael
02:33
created

Array2Object::populate()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 24
rs 6.7272
cc 7
eloc 14
nc 6
nop 2
1
<?php
2
3
/**
4
 * LICENSE: This file is subject to the terms and conditions defined in
5
 * file 'LICENSE', which is part of this source code package.
6
 *
7
 * @copyright 2016 Copyright(c) - All rights reserved.
8
 */
9
10
namespace Rafrsr\LibArray2Object;
11
12
use Rafrsr\LibArray2Object\Parser\ValueParserInterface;
13
14
/**
15
 * Using the property names and the common property annotations
16
 * populate a object instance with the values of the array recursively
17
 */
18
class Array2Object
19
{
20
    /**
21
     * @var Array2ObjectContext
22
     */
23
    private $context;
24
25
    /**
26
     * Array2Object constructor.
27
     */
28
    public function __construct(Array2ObjectContext $context)
29
    {
30
        $this->context = $context;
31
    }
32
33
    /**
34
     * @return Array2ObjectContext
35
     */
36
    public function getContext()
37
    {
38
        return $this->context;
39
    }
40
41
    /**
42
     * @param Array2ObjectContext $context
43
     *
44
     * @return $this
45
     */
46
    public function setContext(Array2ObjectContext $context)
47
    {
48
        $this->context = $context;
49
50
        return $this;
51
    }
52
53
    /**
54
     * createObject
55
     *
56
     * @param string $class class to create object or instance
57
     * @param array  $data  array of data
58
     *
59
     * @return mixed
60
     *
61
     * @throws \InvalidArgumentException
62
     */
63
    public function createObject($class, array $data)
64
    {
65
        if (is_string($class) && class_exists($class)) {
66
            $object = new $class;
67
        } else {
68
            throw new \InvalidArgumentException('The first argument should be a valid class, can use ::populate with objects');
69
        }
70
71
        $this->populate($object, $data);
72
73
        return $object;
74
    }
75
76
    /**
77
     * @param object $object object instance to populate
78
     * @param array  $data   array of data to apply
79
     *
80
     * @throws \InvalidArgumentException
81
     */
82
    public function populate($object, array $data)
83
    {
84
        if (!is_object($object)) {
85
            throw new \InvalidArgumentException('The first param should be a object.');
86
        }
87
88
        if ($object instanceof Array2ObjectInterface) {
89
            $object->__populate($this, $data);
90
        } else {
91
            $reflClass = new \ReflectionClass($object);
92
93
            foreach ($this->getClassProperties($reflClass) as $property) {
94
                foreach ($data as $key => $value) {
95
                    if ($this->context->getMatcher()->match($property, $key)
96
                        && $this->context->getWriter()->isWritable($object, $property->getName())
97
                    ) {
98
                        $types = $this->getPropertyTypes($property);
99
                        $value = $this->parseValue($value, $types, $property, $object);
0 ignored issues
show
Bug introduced by
It seems like $object can also be of type array; however, Rafrsr\LibArray2Object\Array2Object::parseValue() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
100
                        $this->context->getWriter()->setValue($object, $property->getName(), $value);
101
                    }
102
                }
103
            }
104
        }
105
    }
106
107
    /**
108
     * Get array of class properties including parents private properties
109
     *
110
     * @param \ReflectionClass $refClass
111
     *
112
     * @return array|\ReflectionProperty[]
113
     */
114
    private function getClassProperties(\ReflectionClass $refClass)
115
    {
116
        $props = $refClass->getProperties();
117
        $props_arr = [];
118
        foreach ($props as $prop) {
119
            $f = $prop->getName();
120
121
            $props_arr[$f] = $prop;
122
        }
123
        if ($parentClass = $refClass->getParentClass()) {
124
            $parent_props_arr = $this->getClassProperties($parentClass);//RECURSION
125
            if (count($parent_props_arr) > 0) {
126
                $props_arr = array_merge($parent_props_arr, $props_arr);
127
            }
128
        }
129
130
        return $props_arr;
131
    }
132
133
    /**
134
     * Parse a value using given types
135
     *
136
     * @param mixed               $value
137
     * @param array               $types
138
     * @param \ReflectionProperty $property
139
     * @param object              $object
140
     *
141
     * @return array|bool|float|int|string
142
     */
143
    private function parseValue($value, $types, \ReflectionProperty $property, $object)
144
    {
145
        foreach ($types as $type) {
146
147
            foreach ($this->context->getParsers() as $parser) {
148
                if ($parser instanceof ValueParserInterface) {
149
                    if (is_array($value) && strpos($type, '[]') !== false) {
150
                        foreach ($value as $key => &$arrayValue) {
151
                            $arrayValue = $parser->parseValue($arrayValue, str_replace('[]', null, $type), $property, $object);
152
                        }
153
                    } else {
154
                        $value = $parser->parseValue($value, $type, $property, $object);
155
                    }
156
                } else {
157
                    throw new \InvalidArgumentException(sprintf("%s is not a valid parser.", get_class($parser)));
158
                }
159
            }
160
        }
161
162
        return $value;
163
    }
164
165
    /**
166
     * @param \ReflectionProperty $property
167
     *
168
     * @return array
169
     */
170
    private function getPropertyTypes(\ReflectionProperty $property)
171
    {
172
        $doc = $property->getDocComment();
173
        preg_match('/@var\s([\w\\\|\[\]]+)/', $doc, $matches);
174
        $types = [];
175
        if (isset($matches[1])) {
176
            $types = explode('|', $matches[1]);
177
        }
178
179
        return $types;
180
    }
181
}