Passed
Pull Request — master (#3)
by Wojciech
01:42
created

SmartEntity::set()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6.0131

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 27
ccs 13
cts 14
cp 0.9286
rs 9.2222
cc 6
nc 5
nop 2
crap 6.0131
1
<?php
2
3
/*
4
 * This file is part of the Bushido\Foundation package.
5
 *
6
 * (c) Wojciech Nowicki <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.md
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Bushido\Foundation\SmartEntity;
13
14
use Bushido\Foundation\Contracts\Makeable;
15
use Bushido\Foundation\Exception;
16
use Bushido\Foundation\Exceptions\InvalidArgumentException;
17
18
/**
19
 * @todo clone implementation
20
 * @todo add method
21
 * @todo support for some common classes
22
 * @todo limited internal type arrays
23
 */
24
abstract class SmartEntity extends FlexEntity
25
{
26
    protected $properties = [];
27
28 15
    protected function set(string $propertyName, array $arguments)
29
    {
30 15
        $value = $this->fetchValue($arguments, $propertyName);
31
32 15
        if (!$this->isPropertySet($propertyName)) {
33 3
            throw new \RuntimeException('Property [' . $propertyName . '] not set');
34
        }
35
36 12
        $type = $this->properties[$propertyName];
37
38 12
        if ($this->isInternalType($type)) {
39 6
            if ($this->validateInternalType($value, $type)) {
40 3
                parent::set($propertyName, [$value]);
41
42 3
                return $this;
43
            }
44
45 3
            throw new InvalidArgumentException(
46 3
                'Expected value to be type of [' . $this->propertyInternalTypes()[$type] . '] different type was given'
47
            );
48
        }
49
50 9
        if ($this->isTypeArrayOfObjects($type) && is_array($value)) {
51
            return $this->processArrayOfObj($value, $type);
52
        }
53
54 9
        return parent::set($propertyName, [$this->processObjectType($value, $type)]);
55
    }
56
57 12
    private function isInternalType($type)
58
    {
59 12
        return is_numeric($type) && array_key_exists($type, $this->propertyInternalTypes());
60
    }
61
62 15
    private function isPropertySet($propertyName): bool
63
    {
64 15
        return array_key_exists($propertyName, $this->properties);
65
    }
66
67 9
    private function processObjectType($value, $class)
68
    {
69 9
        $this->isClassOrInterface($class);
70
71 9
        if (is_array($value) && is_subclass_of($class, Makeable::class)) {
72 6
            return $class::make($value);
73
        }
74
75 6
        if (is_a($value, $class)) {
76 3
            return $value;
77
        }
78
79 3
        throw new InvalidArgumentException(
80 3
            'Expected value to be object of [' . $class . '] type different type was given'
81
        );
82
    }
83
84 9
    private function isClassOrInterface($class): bool
85
    {
86 9
        if (class_exists($class) || interface_exists($class)) {
87 9
            return true;
88
        }
89
        throw new Exception('Non existing class or interface [' . $class . ']');
90
    }
91
92 9
    private function isTypeArrayOfObjects($type): bool
93
    {
94 9
        return strpos($type, '[]') !== false;
95
    }
96
97
    private function processArrayOfObj(array $value, $type)
98
    {
99
        $ar = [];
100
        $class = $this->getClassNameFromType($type);
101
102
        foreach ($value as $obj) {
103
            $ar[] = $this->processObjectType($obj, $class);
104
        }
105
106
        return $ar;
107
    }
108
109
    private function getClassNameFromType($type)
110
    {
111
        return (string) str_replace('[]', '', $type);
112
    }
113
114 6
    private function validateInternalType($value, $type): bool
115
    {
116
        switch ($type) {
117 6
            case self::TYPE_ARRAY:
118 3
                return is_array($value);
119 6
            case self::TYPE_INT:
120 3
                return is_int($value);
121 6
            case self::TYPE_STRING:
122 3
                return is_string($value);
123 6
            case self::TYPE_BOOL:
124 6
                return is_bool($value);
125 3
            case self::TYPE_FLOAT:
126 3
                return is_float($value);
127 3
            case self::TYPE_NUMERIC:
128 3
                return is_numeric($value);
129
        }
130
131
        return false;
132
    }
133
134 6
    private function propertyInternalTypes()
135
    {
136
        return [
137 6
            self::TYPE_ARRAY => 'array',
138 6
            self::TYPE_INT => 'int',
139 6
            self::TYPE_STRING => 'string',
140 6
            self::TYPE_BOOL => 'bool',
141 6
            self::TYPE_FLOAT => 'float',
142 6
            self::TYPE_NUMERIC => 'numeric',
143
        ];
144
    }
145
}
146