Passed
Push — main ( 47a406...6037b2 )
by Thierry
02:35
created

HtmlBuilder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 13
rs 9.9332
1
<?php
2
3
namespace Lagdo\UiBuilder\Html;
4
5
use Lagdo\UiBuilder\Component\Base\HtmlComponent;
6
use Lagdo\UiBuilder\Component\Base\HtmlElement;
7
use Lagdo\UiBuilder\Component\Html\Element;
8
use Lagdo\UiBuilder\Component\Html\Html;
9
use Closure;
10
use LogicException;
11
12
use function is_array;
13
use function is_string;
14
use function preg_replace;
15
use function stripos;
16
use function strlen;
17
use function strtolower;
18
use function substr;
19
20
class HtmlBuilder
21
{
22
    /**
23
     * @var int
24
     */
25
    public const TARGET_BUILDER = 'builder';
26
27
    /**
28
     * @var int
29
     */
30
    public const TARGET_COMPONENT = 'component';
31
32
    /**
33
     * @var int
34
     */
35
    public const TARGET_ELEMENT = 'element';
36
37
    /**
38
     * @var array<string, array<string, Closure>>
39
     */
40
    protected $factories = [
41
        self::TARGET_BUILDER => [],
42
        self::TARGET_COMPONENT => [],
43
        self::TARGET_ELEMENT => [],
44
    ];
45
46
    /**
47
     * The constructor
48
     */
49
    public function __construct()
50
    {
51
        $this->registerFactory('set', self::TARGET_COMPONENT,
52
            function(HtmlComponent $component, string $tagName,
53
                string $method, array $arguments): HtmlComponent {
54
                $component->element()->setAttribute($tagName, $arguments[0] ?? null);
55
                return $component;
56
            });
57
        $this->registerFactory('set', self::TARGET_ELEMENT,
58
            function(HtmlElement $element, string $tagName,
59
                string $method, array $arguments): HtmlElement {
60
                $element->setAttribute($tagName, $arguments[0] ?? null);
61
                return $element;
62
            });
63
    }
64
65
    /**
66
     * @param string $tagPrefix
67
     * @param string $tagTarget
68
     * @param Closure $factory
69
     *
70
     * @return void
71
     */
72
    public function registerFactory(string $tagPrefix, string $tagTarget, Closure $factory): void
73
    {
74
        // Do not overwrite existing factories.
75
        if(isset($this->factories[$tagTarget]) && !isset($this->factories[$tagTarget][$tagPrefix])) {
76
            $this->factories[$tagTarget][$tagPrefix] = $factory;
77
        }
78
    }
79
80
    /**
81
     * @param string $method
82
     *
83
     * @return string
84
     */
85
    private function getTagName(string $method): string
86
    {
87
        return strtolower(preg_replace('/(?<!^)([A-Z])/', '-$1', $method));
88
    }
89
90
    /**
91
     * @param string $method
92
     * @param array $arguments
93
     *
94
     * @return HtmlComponent|Element
95
     */
96
    public function callBuilderFactory(string $method, array $arguments): HtmlComponent|Element
97
    {
98
        $tagName = $this->getTagName($method);
99
        foreach($this->factories[self::TARGET_BUILDER] as $tagPrefix => $factory) {
100
            if (stripos($tagName, "$tagPrefix-") === 0) {
101
                $tagName = substr($tagName, strlen($tagPrefix) + 1);
102
                return $factory($tagName, $method, $arguments);
103
            }
104
        }
105
106
        return $this->createComponent($tagName, $arguments);
107
    }
108
109
    /**
110
     * @param HtmlComponent $component
111
     * @param string $method
112
     * @param array $arguments
113
     *
114
     * @return HtmlComponent
115
     * @throws LogicException When component is not initialized yet
116
     */
117
    public function callComponentFactory(HtmlComponent $component, string $method, array $arguments): HtmlComponent
118
    {
119
        $tagName = $this->getTagName($method);
120
        foreach($this->factories[self::TARGET_COMPONENT] as $tagPrefix => $factory) {
121
            if (stripos($tagName, "$tagPrefix-") === 0) {
122
                $tagName = substr($tagName, strlen($tagPrefix) + 1);
123
                return $factory($component, $tagName, $method, $arguments);
124
            }
125
        }
126
127
        throw new LogicException("No \"{$method}()\" method defined in the HTML component builder.");
128
    }
129
130
    /**
131
     * @param HtmlElement $element
132
     * @param string $method
133
     * @param array $arguments
134
     *
135
     * @return HtmlElement
136
     * @throws LogicException When component is not initialized yet
137
     */
138
    public function callElementFactory(HtmlElement $element, string $method, array $arguments): HtmlElement
139
    {
140
        $tagName = $this->getTagName($method);
141
        foreach($this->factories[self::TARGET_ELEMENT] as $tagPrefix => $factory) {
142
            if (stripos($tagName, "$tagPrefix-") === 0) {
143
                $tagName = substr($tagName, strlen($tagPrefix) + 1);
144
                return $factory($element, $tagName, $method, $arguments);
145
            }
146
        }
147
148
        throw new LogicException("No \"{$method}()\" method defined in the HTML element builder.");
149
    }
150
151
    /**
152
     * @param string $name
153
     * @param array $arguments
154
     *
155
     * @return HtmlElement
156
     */
157
    public function createElement(string $name, array $arguments = []): HtmlElement
158
    {
159
        $element = new HtmlElement($this, $name);
160
        // Resolve arguments
161
        foreach ($arguments as $argument) {
162
            switch (true) {
163
            case is_array($argument):
164
                $element->setAttributes($argument);
165
                break;
166
            case is_string($argument):
167
                $element->addChild(new Html($argument));
168
            }
169
        }
170
        return $element;
171
    }
172
173
    /**
174
     * @template T of HtmlComponent
175
     * @param string $name
176
     * @param array $arguments
177
     * @psalm-param class-string<T> $class
178
     *
179
     * @return T
0 ignored issues
show
Bug introduced by
The type Lagdo\UiBuilder\Html\T was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
180
     */
181
    public function createComponent(string $name, array $arguments = [],
182
        string $class = HtmlComponent::class): mixed
183
    {
184
        return new $class($this, $name, $arguments);
185
    }
186
187
    /**
188
     * @param string $name
189
     *
190
     * @return HtmlElement
191
     */
192
    public function tag(string $name, ...$arguments): HtmlElement
193
    {
194
        return $this->createElement($name, $arguments);
195
    }
196
197
    /**
198
     * @param array $arguments
199
     *
200
     * @return string
201
     */
202
    public function build(array $arguments): string
203
    {
204
        // The "root" component below will not be printed.
205
        $scope = new Scope($this->createComponent('root'));
206
        $scope->build($arguments);
207
        return $scope->html();
208
    }
209
}
210