Skip to content

Commit aa3b7cf

Browse files
committed
Expanding Twig syntax to rely less on regex for better errors
1 parent 4bfbee5 commit aa3b7cf

File tree

3 files changed

+354
-130
lines changed

3 files changed

+354
-130
lines changed

src/TwigComponent/src/Twig/ComponentLexer.php

Lines changed: 2 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,10 @@
2525
*/
2626
class ComponentLexer extends Lexer
2727
{
28-
public const ATTRIBUTES_REGEX = '(?<attributes>(?:\s+[\w\-:.@]+(=(?:\\\"[^\\\"]*\\\"|\'[^\']*\'|[^\'\\\"=<>]+))?)*\s*)';
29-
public const OPEN_TAGS_REGEX = '/<\s*t:(?<name>([[\w\-\:\.]+))\s*'.self::ATTRIBUTES_REGEX.'(\s?)+>/';
30-
public const CLOSE_TAGS_REGEX = '/<\/\s*t:([\w\-\:\.]+)\s*>/';
31-
public const SELF_CLOSE_TAGS_REGEX = '/<\s*t:(?<name>([\w\-\:\.]+))\s*'.self::ATTRIBUTES_REGEX.'(\s?)+\/>/';
32-
public const BLOCK_TAGS_OPEN = '/<\s*t:block\s+name=("|\')(?<name>([\w\-\:\.]+))("|\')\s*>/';
33-
public const BLOCK_TAGS_CLOSE = '/<\s*\/\s*t:block\s*>/';
34-
public const ATTRIBUTE_BAG_REGEX = '/(?:^|\s+)\{\{\s*(attributes(?:.+?(?<!\s))?)\s*\}\}/x';
35-
public const ATTRIBUTE_KEY_VALUE_REGEX = '/(?<attribute>[\w\-:.@]+)(=(?<value>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+)))?/x';
36-
3728
public function tokenize(Source $source): TokenStream
3829
{
39-
$preparsed = $this->preparsed($source->getCode());
30+
$preLexer = new TwigPreLexer();
31+
$preparsed = $preLexer->preLexComponents($source->getCode());
4032

4133
return parent::tokenize(
4234
new Source(
@@ -46,124 +38,4 @@ public function tokenize(Source $source): TokenStream
4638
)
4739
);
4840
}
49-
50-
private function preparsed(string $value)
51-
{
52-
$value = $this->lexBlockTags($value);
53-
$value = $this->lexBlockTagsClose($value);
54-
$value = $this->lexSelfCloseTag($value);
55-
$value = $this->lexOpeningTags($value);
56-
$value = $this->lexClosingTag($value);
57-
58-
return $value;
59-
}
60-
61-
private function lexOpeningTags(string $value)
62-
{
63-
return preg_replace_callback(
64-
self::OPEN_TAGS_REGEX,
65-
function (array $matches) {
66-
$name = $matches['name'];
67-
$attributes = $this->getAttributesFromAttributeString($matches['attributes']);
68-
69-
return '{% component '.$name.' with '.$attributes.'%}';
70-
},
71-
$value
72-
);
73-
}
74-
75-
private function lexClosingTag(string $value)
76-
{
77-
return preg_replace(self::CLOSE_TAGS_REGEX, '{% endcomponent %}', $value);
78-
}
79-
80-
private function lexSelfCloseTag(string $value)
81-
{
82-
return preg_replace_callback(
83-
self::SELF_CLOSE_TAGS_REGEX,
84-
function (array $matches) {
85-
$name = $matches['name'];
86-
$attributes = $this->getAttributesFromAttributeString($matches['attributes']);
87-
88-
return "{{ component('".$name."', ".$attributes.') }}';
89-
},
90-
$value
91-
);
92-
}
93-
94-
private function lexBlockTags(string $value)
95-
{
96-
return preg_replace_callback(
97-
self::BLOCK_TAGS_OPEN,
98-
function (array $matches) {
99-
$name = $matches['name'];
100-
101-
return '{% block '.$name.' %}';
102-
},
103-
$value
104-
);
105-
}
106-
107-
private function lexBlockTagsClose(string $value)
108-
{
109-
return preg_replace(
110-
self::BLOCK_TAGS_CLOSE,
111-
'{% endblock %}',
112-
$value
113-
);
114-
}
115-
116-
protected function getAttributesFromAttributeString(string $attributeString)
117-
{
118-
$attributeString = $this->parseAttributeBag($attributeString);
119-
120-
if (!preg_match_all(self::ATTRIBUTE_KEY_VALUE_REGEX, $attributeString, $matches, \PREG_SET_ORDER)) {
121-
return '{}';
122-
}
123-
124-
$attributes = [];
125-
foreach ($matches as $match) {
126-
$attribute = $match['attribute'];
127-
$value = $match['value'] ?? null;
128-
129-
if (null === $value) {
130-
$value = 'true';
131-
}
132-
133-
if (str_starts_with($attribute, ':')) {
134-
$attribute = str_replace(':', '', $attribute);
135-
$value = $this->stripQuotes($value);
136-
}
137-
138-
$valueWithoutQuotes = $this->stripQuotes($value);
139-
140-
if (str_starts_with($valueWithoutQuotes, '{{') && (strpos($valueWithoutQuotes, '}}') === \strlen($valueWithoutQuotes) - 2)) {
141-
$value = substr($valueWithoutQuotes, 2, -2);
142-
} else {
143-
$value = $value;
144-
}
145-
146-
$attributes[$attribute] = $value;
147-
}
148-
149-
$out = '{';
150-
foreach ($attributes as $key => $value) {
151-
$key = "'$key'";
152-
$out .= "$key: $value,";
153-
}
154-
155-
return rtrim($out, ',').'}';
156-
}
157-
158-
public function stripQuotes(string $value)
159-
{
160-
return str_starts_with($value, '"') || str_starts_with($value, '\'')
161-
? substr($value, 1, -1)
162-
: $value;
163-
}
164-
165-
protected function parseAttributeBag(string $attributeString)
166-
{
167-
return preg_replace(self::ATTRIBUTE_BAG_REGEX, ' :attributes="$1"', $attributeString);
168-
}
16941
}

0 commit comments

Comments
 (0)