Completed
Push — master ( 783cc3...112ad4 )
by
unknown
04:56 queued 03:47
created

Compiler::isOpeningHtmlTag()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
namespace Spatie\BladeX;
4
5
use Illuminate\Support\Str;
6
7
class Compiler
8
{
9
    /** @var \Spatie\BladeX\BladeX */
10
    protected $bladeX;
11
12
    public function __construct(BladeX $bladeX)
13
    {
14
        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...
15
    }
16
17
    public function compile(string $viewContents): string
18
    {
19
        return array_reduce(
20
            $this->bladeX->registeredComponents(),
21
            [$this, 'parseComponentHtml'],
22
            $viewContents
23
        );
24
    }
25
26
    protected function parseComponentHtml(string $viewContents, Component $component)
27
    {
28
        $viewContents = $this->parseSlots($viewContents);
29
30
        $viewContents = $this->parseSelfClosingTags($viewContents, $component);
31
32
        $viewContents = $this->parseOpeningTags($viewContents, $component);
33
34
        $viewContents = $this->parseClosingTags($viewContents, $component);
35
36
        return $viewContents;
37
    }
38
39 View Code Duplication
    protected function parseSelfClosingTags(string $viewContents, Component $component): string
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
40
    {
41
        $pattern = "/<\s*{$component->getTag()}\s*(?<attributes>(?:\s+[\w\-:]+(=(?:\\\"[^\\\"]+\\\"|\'[^\']+\'|[^\'\\\"=<>]+))?)*\s*)\/>/";
42
43
        return preg_replace_callback($pattern, function (array $matches) use ($component) {
44
            $attributes = $this->getAttributesFromAttributeString($matches['attributes']);
45
46
            return $this->componentString($component, $attributes);
47
        }, $viewContents);
48
    }
49
50 View Code Duplication
    protected function parseOpeningTags(string $viewContents, Component $component): string
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
51
    {
52
        $pattern = "/<\s*{$component->getTag()}(?<attributes>(?:\s+[\w\-:]+(=(?:\\\"[^\\\"]*\\\"|\'[^\']*\'|[^\'\\\"=<>]+))?)*\s*)(?<![\/=\-])>/";
53
54
        return preg_replace_callback($pattern, function (array $matches) use ($component) {
55
            $attributes = $this->getAttributesFromAttributeString($matches['attributes']);
56
57
            return $this->componentStartString($component, $attributes);
58
        }, $viewContents);
59
    }
60
61
    protected function parseClosingTags(string $viewContents, Component $component): string
62
    {
63
        $pattern = "/<\/\s*{$component->getTag()}\s*>/";
64
65
        return preg_replace($pattern, $this->componentEndString($component), $viewContents);
66
    }
67
68
    protected function componentString(Component $component, array $attributes = []): string
69
    {
70
        return $this->componentStartString($component, $attributes).$this->componentEndString($component);
71
    }
72
73
    protected function componentStartString(Component $component, array $attributes = []): string
74
    {
75
        $attributesString = $this->attributesToString($attributes);
76
77
        $componentAttributeString = "[{$attributesString}]";
78
79
        if ($component->view === 'bladex::context') {
80
            return " @php(app(Spatie\BladeX\ContextStack::class)->push({$componentAttributeString})) ";
81
        }
82
83
        if ($component->viewModel) {
84
            $componentAttributeString = "
85
                array_merge(
86
                    app(Spatie\BladeX\ContextStack::class)->read(),
87
                    {$componentAttributeString},
88
                    app(
89
                        '{$component->viewModel}',
90
                        array_merge(
91
                            app(Spatie\BladeX\ContextStack::class)->read(),
92
                            {$componentAttributeString}
93
                        )
94
                    )->toArray()
95
                )";
96
        }
97
98
        return " @component(
99
           '{$component->view}',
100
           array_merge(app(Spatie\BladeX\ContextStack::class)->read(),
101
           {$componentAttributeString})
102
        ) ";
103
    }
104
105
    protected function componentEndString(Component $component): string
106
    {
107
        if ($component->view === 'bladex::context') {
108
            return "@php(app(Spatie\BladeX\ContextStack::class)->pop())";
109
        }
110
111
        return ' @endcomponent ';
112
    }
113
114
    protected function getAttributesFromAttributeString(string $attributeString): array
115
    {
116
        $attributeString = $this->parseBindAttributes($attributeString);
117
118
        $pattern = '/(?<attribute>[\w:-]+)(=(?<value>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+)))?/';
119
120
        if (! preg_match_all($pattern, $attributeString, $matches, PREG_SET_ORDER)) {
121
            return [];
122
        }
123
124
        return collect($matches)->mapWithKeys(function ($match) {
125
            $attribute = Str::camel($match['attribute']);
126
            $value = $match['value'] ?? null;
127
128
            if (is_null($value)) {
129
                $value = 'true';
130
                $attribute = Str::start($attribute, 'bind:');
131
            }
132
133
            $value = $this->stripQuotes($value);
134
135
            if (Str::startsWith($attribute, 'bind:')) {
136
                $attribute = Str::after($attribute, 'bind:');
137
            } else {
138
                $value = str_replace("'", "\\'", $value);
139
                $value = "'{$value}'";
140
            }
141
142
            return [$attribute => $value];
143
        })->toArray();
144
    }
145
146
    protected function parseSlots(string $viewContents): string
147
    {
148
        $openingPattern = '/<\s*slot\s+name=(?<name>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+))\s*>/';
149
        $viewContents = preg_replace_callback($openingPattern, function ($matches) {
150
            $name = $this->stripQuotes($matches['name']);
151
152
            return " @slot('{$name}') ";
153
        }, $viewContents);
154
155
        $closingPattern = '/<\/\s*slot[^>]*>/';
156
        $viewContents = preg_replace($closingPattern, ' @endslot', $viewContents);
157
158
        return $viewContents;
159
    }
160
161
    protected function parseBindAttributes(string $attributeString): string
162
    {
163
        return preg_replace("/\s*:([\w-]+)=/m", ' bind:$1=', $attributeString);
164
    }
165
166
    protected function attributesToString(array $attributes): string
167
    {
168
        return collect($attributes)
169
            ->map(function (string $value, string $attribute) {
170
                return "'{$attribute}' => {$value}";
171
            })
172
            ->implode(',');
173
    }
174
175
    protected function stripQuotes(string $string): string
176
    {
177
        if (Str::startsWith($string, ['"', '\''])) {
178
            return substr($string, 1, -1);
179
        }
180
181
        return $string;
182
    }
183
}
184