Completed
Push — master ( 8afd90...4bf010 )
by Kirill
03:48
created

ValueTypeResolver   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 136
ccs 0
cts 59
cp 0
rs 10
c 0
b 0
f 0
wmc 16
lcom 1
cbo 3

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A breakpoint() 0 6 1
A load() 0 4 1
A castTo() 0 17 4
A resolveType() 0 14 4
A getFilter() 0 6 1
A shouldBreak() 0 4 1
A getFilteredChildrenInheritance() 0 12 3
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\SDL\Compiler\Builder\Common;
11
12
use Railt\Reflection\Contracts\Definition\Behaviour\ProvidesTypeIndication;
13
use Railt\Reflection\Contracts\Definition\ScalarDefinition;
14
use Railt\Reflection\Contracts\Definition\TypeDefinition;
15
use Railt\Reflection\Contracts\Dictionary;
16
use Railt\SDL\Exception\TypeConflictException;
17
18
/**
19
 * The class serves to dynamically output a type from values
20
 */
21
class ValueTypeResolver
22
{
23
    /**
24
     * @var callable[]
25
     */
26
    private const DEFAULT_BREAKPOINTS = [
27
        'String'  => '\\is_string',
28
        'Boolean' => '\\is_bool',
29
        'Float'   => '\\is_float',
30
        'Int'     => '\\is_int',
31
    ];
32
33
    /**
34
     * @var Dictionary
35
     */
36
    private $dictionary;
37
38
    /**
39
     * @var callable[]
40
     */
41
    private $breakpoints = self::DEFAULT_BREAKPOINTS;
42
43
    /**
44
     * ValueTypeResolver constructor.
45
     * @param Dictionary $dictionary
46
     */
47
    public function __construct(Dictionary $dictionary)
48
    {
49
        $this->dictionary = $dictionary;
50
    }
51
52
    /**
53
     * @param string $type
54
     * @param callable $filter
55
     * @return ValueTypeResolver
56
     */
57
    public function breakpoint(string $type, callable $filter): ValueTypeResolver
58
    {
59
        $this->breakpoints[$type] = $filter;
60
61
        return $this;
62
    }
63
64
    /**
65
     * @param string $type
66
     * @return TypeDefinition
67
     * @throws \Railt\Reflection\Exception\TypeNotFoundException
68
     */
69
    private function load(string $type): TypeDefinition
70
    {
71
        return $this->dictionary->get($type);
72
    }
73
74
    /**
75
     * @param TypeDefinition $type
76
     * @param mixed $value
77
     * @param string $renderedValue
78
     * @return mixed
79
     * @throws TypeConflictException
80
     * @throws \Railt\Reflection\Exception\TypeNotFoundException
81
     */
82
    public function castTo(TypeDefinition $type, $value, string $renderedValue = null)
83
    {
84
        foreach ($this->resolveType($value) as $haystack) {
85
            if ($haystack->instanceOf($type)) {
86
                if ($type instanceof ScalarDefinition) {
87
                    return $type->parse($value);
0 ignored issues
show
Bug introduced by
The method parse() does not seem to exist on object<Railt\Reflection\...ition\ScalarDefinition>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
88
                }
89
90
                return $value;
91
            }
92
        }
93
94
        $error = 'Could not cast %s to %s';
95
        $error = \sprintf($error, $renderedValue, $type);
96
97
        throw new TypeConflictException($error);
98
    }
99
100
    /**
101
     * @param mixed $value
102
     * @return iterable|TypeDefinition[]
103
     * @throws \Railt\Reflection\Exception\TypeNotFoundException
104
     */
105
    public function resolveType($value): iterable
106
    {
107
        foreach ($this->breakpoints as $name => $filter) {
108
            if ($filter($value)) {
109
                $inheritance = $this->getFilteredChildrenInheritance($this->load($name), $this->getFilter());
110
111
                foreach ($inheritance as $child) {
112
                    yield $child;
113
                }
114
            }
115
        }
116
117
        yield $this->load('Any');
118
    }
119
120
    /**
121
     * @return \Closure
122
     */
123
    private function getFilter(): \Closure
124
    {
125
        return function (TypeDefinition $resolved): bool {
126
            return $this->shouldBreak($resolved);
127
        };
128
    }
129
130
    /**
131
     * @param TypeDefinition $resolved
132
     * @return bool
133
     */
134
    private function shouldBreak(TypeDefinition $resolved): bool
135
    {
136
        return ! isset($this->breakpoints[$resolved->getName()]);
137
    }
138
139
    /**
140
     * @param TypeDefinition $type
141
     * @param \Closure $filter
142
     * @return \Generator|TypeDefinition[]
143
     */
144
    private function getFilteredChildrenInheritance(TypeDefinition $type, \Closure $filter): \Traversable
145
    {
146
        yield $type;
147
148
        foreach ($type->getChildrenInheritance() as $child) {
0 ignored issues
show
Bug introduced by
The method getChildrenInheritance() does not seem to exist on object<Railt\Reflection\...inition\TypeDefinition>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
149
            if (! $filter($child)) {
150
                continue;
151
            }
152
153
            yield from $this->getFilteredChildrenInheritance($child, $filter);
154
        }
155
    }
156
}
157