Passed
Pull Request — master (#3)
by Wojciech
02:51
created

SmartEntity::validateInternalType()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7.0178

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
dl 0
loc 18
ccs 13
cts 14
cp 0.9286
rs 8.8333
cc 7
nc 7
nop 2
crap 7.0178
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\Exceptions\InvalidArgumentException;
16
17
/**
18
 * @todo clone implementation
19
 * @todo add method
20
 * @todo support for some common classes
21
 * @todo limited internal type arrays
22
 */
23
abstract class SmartEntity extends FlexEntity
24
{
25
    protected $properties = [];
26
27 3
    protected function set(string $propertyName, array $arguments)
28
    {
29 3
        $value = $this->fetchValue($arguments, $propertyName);
30
31 3
        if (!$this->isPropertySet($propertyName)) {
32
            throw new \RuntimeException();
33
        }
34
35 3
        $type = $this->properties[$propertyName];
36
37 3
        if ($this->isInternalType($type)) {
38 3
            if ($this->validateInternalType($value, $type)) {
39 3
                parent::set($propertyName, [$value]);
40
41 3
                return $this;
42
            }
43
44
            throw new InvalidArgumentException(
45
                'Expected value to be type of [' . $this->propertyInternalTypes()[$type] . '] different type was given'
46
            );
47
        }
48
49
        if ($this->isTypeArrayOfObjects($type) && is_array($value)) {
50
            return $this->processArrayOfObj($value, $type);
51
        }
52
53
        parent::set($propertyName, [$this->processObjectType($value, $type)]);
54
    }
55
56 3
    private function isInternalType($type)
57
    {
58 3
        return is_numeric($type) && array_key_exists($type, $this->propertyInternalTypes());
59
    }
60
61 3
    private function isPropertySet($propertyName): bool
62
    {
63 3
        return count($this->properties) == 0 || array_key_exists($propertyName, $this->properties);
64
    }
65
66
    private function processObjectType($value, $class)
67
    {
68
        $this->isClassOrInterface($class);
69
70
        if (is_array($value) && is_subclass_of($class, Makeable::class)) {
71
            return $class::make($value);
72
        }
73
74
        if (is_a($value, $class)) {
75
            return $value;
76
        }
77
78
        throw new InvalidArgumentException(
79
            'Expected value to be object of [' . $class . '] type ' . $this->checkType($value) . '] was given'
0 ignored issues
show
Bug introduced by
The method checkType() does not exist on Bushido\Foundation\SmartEntity\SmartEntity. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

79
            'Expected value to be object of [' . $class . '] type ' . $this->/** @scrutinizer ignore-call */ checkType($value) . '] was given'
Loading history...
80
        );
81
    }
82
83
    private function isClassOrInterface($class): bool
84
    {
85
        if (class_exists($class) || interface_exists($class)) {
86
            return true;
87
        }
88
        throw new Exception('Non existing class or interface [' . $class . ']');
0 ignored issues
show
Bug introduced by
The type Bushido\Foundation\SmartEntity\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
89
    }
90
91
    private function isTypeArrayOfObjects($type): bool
92
    {
93
        return strpos($type, '[]') !== false;
94
    }
95
96
    private function processArrayOfObj(array $value, $type)
97
    {
98
        $ar = [];
99
        $class = $this->getClassNameFromType($type);
100
101
        foreach ($value as $obj) {
102
            $ar[] = $this->processObjectType($obj, $class);
103
        }
104
105
        return $ar;
106
    }
107
108
    private function getClassNameFromType($type)
109
    {
110
        return (string) str_replace('[]', '', $type);
111
    }
112
113 3
    private function validateInternalType($value, $type): bool
114
    {
115
        switch ($type) {
116 3
            case self::TYPE_ARRAY:
117 3
                return is_array($value);
118 3
            case self::TYPE_INT:
119 3
                return is_int($value);
120 3
            case self::TYPE_STRING:
121 3
                return is_string($value);
122 3
            case self::TYPE_BOOL:
123 3
                return is_bool($value);
124 3
            case self::TYPE_FLOAT:
125 3
                return is_float($value);
126 3
            case self::TYPE_NUMERIC:
127 3
                return is_numeric($value);
128
        }
129
130
        return false;
131
    }
132
133 3
    private function propertyInternalTypes()
134
    {
135
        return [
136 3
            self::TYPE_ARRAY => 'array',
137 3
            self::TYPE_INT => 'int',
138 3
            self::TYPE_STRING => 'string',
139 3
            self::TYPE_BOOL => 'bool',
140 3
            self::TYPE_FLOAT => 'float',
141 3
            self::TYPE_NUMERIC => 'numeric',
142
        ];
143
    }
144
}
145