Completed
Push — master ( 28994a...370eae )
by
unknown
01:22
created

BladeXCompiler::getAttributesFromAttributeString()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 9.0808
c 0
b 0
f 0
cc 5
nc 2
nop 1
1
<?php
2
3
namespace Spatie\BladeX;
4
5
class BladeXCompiler
6
{
7
    /** @var \Spatie\BladeX\BladeX */
8
    protected $bladeX;
9
10
    public function __construct(BladeX $bladeX)
11
    {
12
        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...
13
    }
14
15
    public function compile(string $viewContents): string
16
    {
17
        return array_reduce(
18
            $this->bladeX->getRegisteredComponents(),
19
            [$this, 'parseComponentHtml'],
20
            $viewContents
21
        );
22
    }
23
24
    protected function parseComponentHtml(string $viewContents, BladeXComponent $bladeXComponent)
25
    {
26
        $viewContents = $this->parseSlots($viewContents);
27
28
        $viewContents = $this->parseSelfClosingTags($viewContents, $bladeXComponent);
29
30
        $viewContents = $this->parseOpeningTags($viewContents, $bladeXComponent);
31
32
        $viewContents = $this->parseClosingTags($viewContents, $bladeXComponent);
33
34
        return $viewContents;
35
    }
36
37
    protected function parseSelfClosingTags(string $viewContents, BladeXComponent $bladeXComponent): string
38
    {
39
        $prefix = $this->bladeX->getPrefix();
40
41
        $pattern = "/<\s*{$prefix}{$bladeXComponent->name}\s*(.*)\s*\/>/m";
42
43
        return preg_replace_callback($pattern, function (array $regexResult) use ($bladeXComponent) {
44
            [$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...
45
46
            $attributes = $this->getAttributesFromAttributeString($attributesString);
47
48
            return $this->componentString($bladeXComponent, $attributes);
49
        }, $viewContents);
50
    }
51
52
    protected function parseOpeningTags(string $viewContents, BladeXComponent $bladeXComponent): string
53
    {
54
        $prefix = $this->bladeX->getPrefix();
55
56
        $pattern = "/<\s*{$prefix}{$bladeXComponent->name}((?:\s+[\w\-:]*=(?:\\\"(?:.*?)\\\"|\'(?:.*)\'|[^\'\\\"=<>]*))*\s*)(?<![\/=\-])>/m";
57
58
        return preg_replace_callback($pattern, function (array $regexResult) use ($bladeXComponent) {
59
            [$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...
60
61
            $attributes = $this->getAttributesFromAttributeString($attributesString);
62
63
            return $this->componentStartString($bladeXComponent, $attributes);
64
        }, $viewContents);
65
    }
66
67
    protected function parseClosingTags(string $viewContents, BladeXComponent $bladeXComponent): string
68
    {
69
        $prefix = $this->bladeX->getPrefix();
70
71
        $pattern = "/<\/\s*{$prefix}{$bladeXComponent->name}[^>]*>/m";
72
73
        return preg_replace($pattern, $this->componentEndString($bladeXComponent), $viewContents);
74
    }
75
76
    protected function componentString(BladeXComponent $bladeXComponent, array $attributes = []): string
77
    {
78
        return $this->componentStartString($bladeXComponent, $attributes).$this->componentEndString($bladeXComponent);
79
    }
80
81
    protected function componentStartString(BladeXComponent $bladeXComponent, array $attributes = []): string
82
    {
83
        $attributesString = $this->attributesToString($attributes);
84
85
        $componentAttributeString = "[{$attributesString}]";
86
87
        if ($bladeXComponent->bladeViewName === 'bladex::context') {
88
            return "@php(app(Spatie\BladeX\ContextStack::class)->push({$componentAttributeString}))";
89
        }
90
91
        if ($bladeXComponent->viewModel) {
92
            $componentAttributeString = "
93
                array_merge(
94
                    app(Spatie\BladeX\ContextStack::class)->read(),
95
                    {$componentAttributeString},
96
                    app(
97
                        '{$bladeXComponent->viewModel}',
98
                        array_merge(
99
                            app(Spatie\BladeX\ContextStack::class)->read(),
100
                            {$componentAttributeString}
101
                        )
102
                    )->toArray()
103
                )";
104
        }
105
106
        return " @component(
107
           '{$bladeXComponent->bladeViewName}',
108
           array_merge(app(Spatie\BladeX\ContextStack::class)->read(), {$componentAttributeString})) ";
109
    }
110
111
    protected function componentEndString(BladeXComponent $bladeXComponent): string
112
    {
113
        if ($bladeXComponent->bladeViewName === 'bladex::context') {
114
            return "@php(app(Spatie\BladeX\ContextStack::class)->pop())";
115
        }
116
117
        return ' @endcomponent ';
118
    }
119
120
    protected function getAttributesFromAttributeString(string $attributeString): array
121
    {
122
        $attributeString = $this->parseBindAttributes($attributeString);
123
124
        $pattern = '/(?<attribute>[\w:-]+)(=(?<value>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+)))?/';
125
126
        if (! preg_match_all($pattern, $attributeString, $matches, PREG_SET_ORDER)) {
127
            return [];
128
        }
129
130
        return collect($matches)->mapWithKeys(function ($match) {
131
            $attribute = camel_case($match['attribute']);
132
            $value = $match['value'] ?? null;
133
134
            if (is_null($value)) {
135
                $value = 'true';
136
                $attribute = str_start($attribute, 'bind:');
137
            }
138
139
            $value = $this->stripQuotes($value);
140
141
            if (! starts_with($attribute, 'bind:')) {
142
                $value = str_replace("'", "\\'", $value);
143
                $value = "'{$value}'";
144
            }
145
146
            if (starts_with($attribute, 'bind:')) {
147
                $attribute = str_after($attribute, 'bind:');
148
            }
149
150
            return [$attribute => $value];
151
        })->toArray();
152
    }
153
154
    protected function parseSlots(string $viewContents): string
155
    {
156
        $openingPattern = '/<\s*slot\s+name=(?<name>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+))\s*>/';
157
        $viewContents = preg_replace_callback($openingPattern, function ($matches) {
158
            $name = $this->stripQuotes($matches['name']);
159
160
            return " @slot('{$name}') ";
161
        }, $viewContents);
162
163
        $closingPattern = '/<\/\s*slot[^>]*>/';
164
        $viewContents = preg_replace($closingPattern, ' @endslot', $viewContents);
165
166
        return $viewContents;
167
    }
168
169
    protected function isOpeningHtmlTag(string $tagName, string $html): bool
170
    {
171
        return ! ends_with($html, ["</{$tagName}>", '/>']);
172
    }
173
174
    protected function parseBindAttributes(string $attributeString): string
175
    {
176
        return preg_replace("/\s*:([\w-]+)=/m", ' bind:$1=', $attributeString);
177
    }
178
179
    protected function attributesToString(array $attributes): string
180
    {
181
        return collect($attributes)
182
            ->map(function (string $value, string $attribute) {
183
                return "'{$attribute}' => {$value}";
184
            })
185
            ->implode(',');
186
    }
187
188
    protected function stripQuotes(string $string): string
189
    {
190
        if (starts_with($string, ['"', '\''])) {
191
            return substr($string, 1, -1);
192
        }
193
194
        return $string;
195
    }
196
}
197