Completed
Push — master ( ec809d...e321a5 )
by
unknown
01:54
created

BladeXCompiler::attributesToString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Spatie\BladeX;
4
5
use Exception;
6
use SimpleXMLElement;
7
use Spatie\BladeX\Exceptions\CouldNotParseBladeXComponent;
8
9
class BladeXCompiler
10
{
11
    /** @var \Spatie\BladeX\BladeX */
12
    protected $bladeX;
13
14
    public function __construct(BladeX $bladeX)
15
    {
16
        return $this->bladeX = $bladeX;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
17
    }
18
19
    public function compile(string $viewContents): string
20
    {
21
        return array_reduce(
22
            $this->bladeX->getRegisteredComponents(),
23
            [$this, 'parseComponentHtml'],
24
            $viewContents
25
        );
26
    }
27
28
    protected function parseComponentHtml(string $viewContents, BladeXComponent $bladeXComponent)
29
    {
30
        $viewContents = $this->parseSlots($viewContents);
31
32
        $viewContents = $this->parseSelfClosingTags($viewContents, $bladeXComponent);
33
34
        $viewContents = $this->parseOpeningTags($viewContents, $bladeXComponent);
35
36
        $viewContents = $this->parseClosingTags($viewContents, $bladeXComponent);
37
38
        return $viewContents;
39
    }
40
41
    protected function parseSelfClosingTags(string $viewContents, BladeXComponent $bladeXComponent): string
42
    {
43
        $prefix = $this->bladeX->getPrefix();
44
45
        $pattern = "/<\s*{$prefix}{$bladeXComponent->name}\s*([^>]*)\/>/m";
46
47
        return preg_replace_callback($pattern, function (array $regexResult) use ($bladeXComponent) {
48
            [$componentHtml, $attributesString] = $regexResult;
0 ignored issues
show
Bug introduced by
The variable $componentHtml does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $attributesString does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
49
50
            $attributes = $this->getComponentAttributes($bladeXComponent, $attributesString);
51
52
            return $this->componentString($bladeXComponent, $attributes);
53
        }, $viewContents);
54
    }
55
56
    protected function parseOpeningTags(string $viewContents, BladeXComponent $bladeXComponent): string
57
    {
58
        $prefix = $this->bladeX->getPrefix();
59
60
        $pattern = "/<\s*{$prefix}{$bladeXComponent->name}\s*([^>]*)(?<!\/)>/m";
61
62
        return preg_replace_callback($pattern, function (array $regexResult) use ($bladeXComponent) {
63
            [$componentHtml, $attributesString] = $regexResult;
0 ignored issues
show
Bug introduced by
The variable $componentHtml does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $attributesString does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
64
65
            $attributes = $this->getComponentAttributes($bladeXComponent, $attributesString);
66
67
            return $this->componentStartString($bladeXComponent, $attributes);
68
        }, $viewContents);
69
    }
70
71
    protected function parseClosingTags(string $viewContents, BladeXComponent $bladeXComponent): string
72
    {
73
        $prefix = $this->bladeX->getPrefix();
74
75
        $pattern = "/<\/\s*{$prefix}{$bladeXComponent->name}[^>]*>/m";
76
77
        return preg_replace($pattern, $this->componentEndString(), $viewContents);
78
    }
79
80
    protected function componentString(BladeXComponent $bladeXComponent, array $attributes = []): string
81
    {
82
        return $this->componentStartString($bladeXComponent, $attributes).$this->componentEndString();
83
    }
84
85
    protected function componentStartString(BladeXComponent $bladeXComponent, array $attributes = []): string
86
    {
87
        $attributesString = $this->attributesToString($attributes);
88
89
        $componentAttributeString = "[{$attributesString}]";
90
91
        if ($bladeXComponent->viewModelClass) {
92
            $componentAttributeString = "array_merge({$componentAttributeString}, app({$bladeXComponent->viewModelClass}::class, {$componentAttributeString})->toArray())";
93
        }
94
95
        return  "@component('{$bladeXComponent->bladeViewName}', {$componentAttributeString})";
96
    }
97
98
    protected function componentEndString(): string
99
    {
100
        return '@endcomponent';
101
    }
102
103
    protected function getComponentAttributes(BladeXComponent $bladeXComponent, string $attributesString): array
104
    {
105
        $prefix = $this->bladeX->getPrefix();
106
107
        $elementName = $prefix.$bladeXComponent->name;
108
109
        $componentHtml = "<{$elementName} {$attributesString} />";
110
111
        $componentHtml = $this->parseBindAttributes($componentHtml);
112
113
        $componentHtml = $this->setXmlNamespace('bind', $componentHtml);
114
115
        return $this->getHtmlElementAttributes($componentHtml, $bladeXComponent);
116
    }
117
118
    protected function getHtmlElementAttributes(string $htmlElement, BladeXComponent $bladeXComponent): array
119
    {
120
        try {
121
            $componentXml = new SimpleXMLElement($htmlElement);
122
        } catch (Exception $exception) {
123
            throw CouldNotParseBladeXComponent::invalidHtml($htmlElement, $bladeXComponent, $exception);
124
        }
125
126
        $stringAttributes = collect($componentXml->attributes())
127
            ->mapWithKeys(function ($value, $attribute) {
128
                $value = str_replace("'", "\\'", $value);
129
130
                return [$attribute => "'{$value}'"];
131
            });
132
133
        $bindAttributes = collect($componentXml->attributes('bind'));
134
135
        return $stringAttributes
136
            ->merge($bindAttributes)
137
            ->mapWithKeys(function ($value, $attribute) {
138
                $value = str_replace(['<', '>'], ['&lt;', '&gt;'], $value);
139
140
                return [camel_case($attribute) => $value];
141
            })
142
            ->toArray();
143
    }
144
145
    protected function parseSlots(string $viewContents): string
146
    {
147
        $pattern = '/<\s*slot[^>]*name=[\'"](.*)[\'"][^>]*>((.|\n)*?)<\s*\/\s*slot>/m';
148
149
        return preg_replace_callback($pattern, function ($regexResult) {
150
            [$slot, $name, $contents] = $regexResult;
0 ignored issues
show
Bug introduced by
The variable $slot does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $name does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $contents does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
151
152
            return "@slot('{$name}'){$contents}@endslot";
153
        }, $viewContents);
154
    }
155
156
    protected function isOpeningHtmlTag(string $tagName, string $html): bool
157
    {
158
        return ! ends_with($html, ["</{$tagName}>", '/>']);
159
    }
160
161
    protected function parseBindAttributes(string $html): string
162
    {
163
        return preg_replace("/\s+:([\w-]+)=/m", ' bind:$1=', $html);
164
    }
165
166
    protected function setXmlNamespace(string $namespace, string $html): string
167
    {
168
        return preg_replace("/^<\s*([\w-]*)\s/m", "<$1 xmlns:bind='{$namespace}' ", $html);
169
    }
170
171
    protected function attributesToString(array $attributes): string
172
    {
173
        return collect($attributes)
174
            ->map(function (string $value, string $attribute) {
175
                return "'{$attribute}' => {$value}";
176
            })
177
            ->implode(',');
178
    }
179
}
180