Completed
Push — master ( 4a310d...0cc28f )
by Freek
03:00 queued 01:06
created

BladeXCompiler::getComponentAttributes()   A

Complexity

Conditions 1
Paths 1

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 1
nc 1
nop 2
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->viewModelClass) {
92
            $componentAttributeString = "
93
                array_merge(
94
                    app(Spatie\BladeX\ContextStack::class)->read(),
95
                    {$componentAttributeString},
96
                    app(
97
                        {$bladeXComponent->viewModelClass}::class,
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
            if (starts_with($value, ['"', '\''])) {
140
                $value = substr($value, 1, -1);
141
            }
142
143
            if (! starts_with($attribute, 'bind:')) {
144
                $value = str_replace("'", "\\'", $value);
145
                $value = "'{$value}'";
146
            }
147
148
            if (starts_with($attribute, 'bind:')) {
149
                $attribute = str_after($attribute, 'bind:');
150
            }
151
152
            return [$attribute => $value];
153
        })->toArray();
154
    }
155
156
    protected function parseSlots(string $viewContents): string
157
    {
158
        $pattern = '/<\s*slot[^>]*name=[\'"](.*)[\'"][^>]*>((.|\n)*?)<\s*\/\s*slot>/m';
159
160
        return preg_replace_callback($pattern, function ($regexResult) {
161
            [$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...
162
163
            return "@slot('{$name}'){$contents}@endslot";
164
        }, $viewContents);
165
    }
166
167
    protected function isOpeningHtmlTag(string $tagName, string $html): bool
168
    {
169
        return !ends_with($html, ["</{$tagName}>", '/>']);
170
    }
171
172
    protected function parseBindAttributes(string $html): string
173
    {
174
        return preg_replace("/\s+:([\w-]+)=/m", ' bind:$1=', $html);
175
    }
176
177
    protected function attributesToString(array $attributes): string
178
    {
179
        return collect($attributes)
180
            ->map(function (string $value, string $attribute) {
181
                return "'{$attribute}' => {$value}";
182
            })
183
            ->implode(',');
184
    }
185
}
186