Skip to content

Commit 9ec2a71

Browse files
committed
[TwigComponent] Optimize TwigPreLexer
1 parent bf3a0f0 commit 9ec2a71

File tree

1 file changed

+32
-73
lines changed

1 file changed

+32
-73
lines changed

src/TwigComponent/src/Twig/TwigPreLexer.php

Lines changed: 32 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function preLexComponents(string $input): string
3939
return $input;
4040
}
4141

42-
$this->input = $input;
42+
$this->input = $input = str_replace(["\r\n", "\r"], "\n", $input);
4343
$this->length = \strlen($input);
4444
$output = '';
4545

@@ -126,7 +126,8 @@ public function preLexComponents(string $input): string
126126
// open the default block
127127
if (!empty($this->currentComponents)
128128
&& !$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock']) {
129-
$output .= $this->addDefaultBlock();
129+
$output .= '{% block content %}';
130+
$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock'] = true;
130131
}
131132

132133
$attributes = $this->consumeAttributes($componentName);
@@ -182,7 +183,8 @@ public function preLexComponents(string $input): string
182183
&& preg_match('/\S/', $char)
183184
&& !$this->check('{% block')
184185
) {
185-
$output .= $this->addDefaultBlock();
186+
$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock'] = true;
187+
$output .= '{% block content %}';
186188
}
187189

188190
$output .= $char;
@@ -199,29 +201,14 @@ public function preLexComponents(string $input): string
199201

200202
private function consumeComponentName(?string $customExceptionMessage = null): string
201203
{
202-
$start = $this->position;
203-
while ($this->position < $this->length && preg_match('/[A-Za-z0-9_:@\-.]/', $this->input[$this->position])) {
204-
++$this->position;
205-
}
204+
if (preg_match('/\G[A-Za-z0-9_:@\-.]+/', $this->input, $matches, 0, $this->position)) {
205+
$componentName = $matches[0];
206+
$this->position += \strlen($componentName);
206207

207-
$componentName = substr($this->input, $start, $this->position - $start);
208-
209-
if (empty($componentName)) {
210-
$exceptionMessage = $customExceptionMessage;
211-
if (null == $exceptionMessage) {
212-
$exceptionMessage = 'Expected component name when resolving the "<twig:" syntax.';
213-
}
214-
throw new SyntaxError($exceptionMessage, $this->line);
208+
return $componentName;
215209
}
216210

217-
return $componentName;
218-
}
219-
220-
private function consumeAttributeName(string $componentName): string
221-
{
222-
$message = \sprintf('Expected attribute name when parsing the "<twig:%s" syntax.', $componentName);
223-
224-
return $this->consumeComponentName($message);
211+
throw new SyntaxError($customExceptionMessage ?? 'Expected component name when resolving the "<twig:" syntax.', $this->line);
225212
}
226213

227214
private function consumeAttributes(string $componentName): string
@@ -251,7 +238,9 @@ private function consumeAttributes(string $componentName): string
251238
$isAttributeDynamic = true;
252239
}
253240

254-
$key = $this->consumeAttributeName($componentName);
241+
$message = \sprintf('Expected attribute name when parsing the "<twig:%s" syntax.', $componentName);
242+
// was called 'consumeAttributeName'
243+
$key = $this->consumeComponentName($message);
255244

256245
// <twig:component someProp> -> someProp: true
257246
if (!$this->check('=')) {
@@ -290,9 +279,8 @@ private function consumeAttributes(string $componentName): string
290279
*/
291280
private function consume(string $string): bool
292281
{
293-
$stringLength = \strlen($string);
294-
if (substr($this->input, $this->position, $stringLength) === $string) {
295-
$this->position += $stringLength;
282+
if (str_starts_with(substr($this->input, $this->position), $string)) {
283+
$this->position += \strlen($string);
296284

297285
return true;
298286
}
@@ -325,31 +313,25 @@ private function consumeChar($validChars = null): string
325313
*/
326314
private function consumeUntil(string $endString): string
327315
{
328-
$start = $this->position;
329-
$endCharLength = \strlen($endString);
316+
if (false === $endPosition = strpos($this->input, $endString, $this->position)) {
317+
$start = $this->position;
318+
$this->position = $this->length;
330319

331-
while ($this->position < $this->length) {
332-
if (substr($this->input, $this->position, $endCharLength) === $endString) {
333-
break;
334-
}
335-
336-
if ("\n" === $this->input[$this->position]) {
337-
++$this->line;
338-
}
339-
++$this->position;
320+
return substr($this->input, $start);
340321
}
341322

342-
return substr($this->input, $start, $this->position - $start);
323+
$content = substr($this->input, $this->position, $endPosition - $this->position);
324+
$this->line += substr_count($content, "\n");
325+
$this->position = $endPosition;
326+
327+
return $content;
343328
}
344329

345330
private function consumeWhitespace(): void
346331
{
347-
while ($this->position < $this->length && preg_match('/\s/', $this->input[$this->position])) {
348-
if ("\n" === $this->input[$this->position]) {
349-
++$this->line;
350-
}
351-
++$this->position;
352-
}
332+
$whitespace = substr($this->input, $this->position, strspn($this->input, " \t\n\r\0\x0B", $this->position));
333+
$this->line += substr_count($whitespace, "\n");
334+
$this->position += \strlen($whitespace);
353335
}
354336

355337
/**
@@ -374,18 +356,8 @@ private function expectAndConsumeChar(string $char): void
374356

375357
private function check(string $chars): bool
376358
{
377-
$charsLength = \strlen($chars);
378-
if ($this->position + $charsLength > $this->length) {
379-
return false;
380-
}
381-
382-
for ($i = 0; $i < $charsLength; ++$i) {
383-
if ($this->input[$this->position + $i] !== $chars[$i]) {
384-
return false;
385-
}
386-
}
387-
388-
return true;
359+
return $this->position + \strlen($chars) <= $this->length
360+
&& 0 === substr_compare($this->input, $chars, $this->position, \strlen($chars));
389361
}
390362

391363
private function consumeBlock(string $componentName): string
@@ -409,7 +381,7 @@ private function consumeBlock(string $componentName): string
409381
$output = "{% block {$blockName} %}";
410382

411383
$closingTag = '</twig:block>';
412-
if (!$this->doesStringEventuallyExist($closingTag)) {
384+
if (false === strpos($this->input, $closingTag, $this->position)) {
413385
throw new SyntaxError("Expected closing tag '{$closingTag}' for block '{$blockName}'.", $this->line);
414386
}
415387
$blockContents = $this->consumeUntilEndBlock();
@@ -448,7 +420,8 @@ private function consumeUntilEndBlock(): string
448420
if (!$inComment && '{% endblock %}' === substr($this->input, $this->position, 14)) {
449421
if (1 === $depth) {
450422
// in this case, we want to advance ALL the way beyond the endblock
451-
$this->position += 14 /* strlen('{% endblock %}') */;
423+
// strlen('{% endblock %}') = 14
424+
$this->position += 14;
452425
break;
453426
} else {
454427
--$depth;
@@ -512,18 +485,4 @@ private function consumeAttributeValue(string $quote): string
512485

513486
return implode('~', $parts);
514487
}
515-
516-
private function doesStringEventuallyExist(string $needle): bool
517-
{
518-
$remainingString = substr($this->input, $this->position);
519-
520-
return str_contains($remainingString, $needle);
521-
}
522-
523-
private function addDefaultBlock(): string
524-
{
525-
$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock'] = true;
526-
527-
return '{% block content %}';
528-
}
529488
}

0 commit comments

Comments
 (0)