Completed
Push — master ( 1bc299...b12741 )
by Kirill
03:33
created

Builder::coroutine()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
/**
3
 * This file is part of Properties 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 Serafim\Properties;
11
12
use Railt\Parser\Ast\RuleInterface;
13
use Serafim\Properties\Attribute\AttributeInterface;
14
use Serafim\Properties\Parser\Parser;
15
use Serafim\Properties\Reducers;
16
use Serafim\Properties\Reducers\ReducerInterface;
17
18
/**
19
 * Class Builder
20
 */
21
class Builder
22
{
23
    /**
24
     * @var string[]
25
     */
26
    private const DEFAULT_REDUCERS = [
27
        Reducers\DocBlockReducer::class,
28
        Reducers\DocTitleReducer::class,
29
        Reducers\VariableReducer::class,
30
        Reducers\TypeHintReducer::class,
31
        Reducers\TypeNameReducer::class,
32
        Reducers\TypeHint\ScalarHintReducer::class,
33
        Reducers\TypeHint\ArrayHintReducer::class,
34
        Reducers\TypeHint\OrHintReducer::class,
35
        Reducers\TypeHint\AndHintReducer::class,
36
    ];
37
38
    /**
39
     * @var Parser
40
     */
41
    private $parser;
42
43
    /**
44
     * @var array|ReducerInterface[]
45
     */
46
    private $reducers = [];
47
48
    /**
49
     * Builder constructor.
50
     */
51
    public function __construct()
52
    {
53
        $this->parser = new Parser();
54
55
        $this->bootReducers();
56
    }
57
58
    /**
59
     * @return void
60
     */
61
    private function bootReducers(): void
62
    {
63
        foreach (self::DEFAULT_REDUCERS as $reducer) {
64
            $this->reducers[] = new $reducer;
65
        }
66
    }
67
68
    /**
69
     * @param string $class
70
     * @return iterable|AttributeInterface[]
71
     * @throws \RuntimeException
72
     */
73
    public function buildClass(string $class): iterable
74
    {
75
        $ast = $this->parser->parseClassDocComment($class);
76
77
        if ($ast instanceof RuleInterface) {
78
            foreach ($ast->getChildren() as $child) {
79
                yield $this->build($child);
80
            }
81
        }
82
    }
83
84
    /**
85
     * @param RuleInterface $rule
86
     * @return mixed
87
     * @throws \RuntimeException
88
     */
89
    private function build(RuleInterface $rule)
90
    {
91
        foreach ($this->reducers as $reducer) {
92
            if ($reducer->match($rule)) {
93
                $result = $reducer->reduce($rule);
94
95
                return $result instanceof \Generator ? $this->coroutine($result) : $result;
96
            }
97
        }
98
99
        $error = \sprintf('Unrecognized rule %s', $rule->getName());
100
        throw new \RuntimeException($error);
101
    }
102
103
    /**
104
     * @param \Generator $coroutine
105
     * @return mixed
106
     * @throws \RuntimeException
107
     */
108
    private function coroutine(\Generator $coroutine)
109
    {
110
        while ($coroutine->valid()) {
111
            $value = $coroutine->current();
112
113
            if ($value instanceof RuleInterface) {
114
                $value = $this->build($value);
115
            }
116
117
            $coroutine->send($value);
118
        }
119
120
        return $coroutine->getReturn();
121
    }
122
}
123