Skip to content

Commit be1cd2f

Browse files
davidkvasnovskydg
andauthored
Fix PhpHelpers::checkCode crash when PHP binary path contains spaces (#415)
* Dedent: fixed inline content and atLineStart tracking [Closes #412, #413] * fixed dedent * improved TagParser::parseType() * added length to Position * StatementNode: added tagPositions * updated github actions * wip * Fix PhpHelpers::checkCode crash when PHP binary path contains spaces proc_open with a string command goes through the shell, which splits the path at spaces. This breaks on macOS with Laravel Herd where PHP_BINARY is e.g. /Users/.../Application Support/Herd/bin/php84. Using an array bypasses the shell entirely, which is also what the existing bypass_shell option was intended to do (but only works on Windows). --------- Co-authored-by: David Grudl <david@grudl.com>
1 parent 1c83b08 commit be1cd2f

2 files changed

Lines changed: 87 additions & 1 deletion

File tree

src/Latte/Compiler/PhpHelpers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ private static function codePointToUtf8(int $num): string
251251
public static function checkCode(string $phpBinary, string $code, string $name): void
252252
{
253253
$process = proc_open(
254-
$phpBinary . ' -l -n',
254+
[$phpBinary, '-l', '-n'],
255255
[['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']],
256256
$pipes,
257257
null,

src/Latte/Compiler/TemplateParser.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,11 @@ public function parseLatteStatement(?\Closure $resolver = null): ?Node
215215
$this->lookFor[$startTag] = $res->current() ?: null;
216216
$content = $this->parseFragment($resolver ?? $this->lastResolver);
217217
if ($this->dedent) {
218+
<<<<<<< fix/php-binary-space-in-path
219+
$this->applyDedent($content, $startTag);
220+
=======
218221
Dedent::apply($content, $startTag);
222+
>>>>>>> master
219223
}
220224

221225
if (!$this->stream->is(Token::Latte_TagOpen)) {
@@ -506,4 +510,86 @@ public function isTagAllowed(string $name): bool
506510
{
507511
return !$this->policy || $this->policy->isTagAllowed($name);
508512
}
513+
<<<<<<< fix/php-binary-space-in-path
514+
515+
516+
private function applyDedent(FragmentNode $fragment, Tag $startTag): void
517+
{
518+
$baseIndent = null;
519+
$atLineStart = true;
520+
$inlineChecked = false;
521+
// When the tag is inside an HTML element, preserve indent up to the tag's column
522+
// (structural indent). When top-level, strip to column 1.
523+
$tagIndentLen = $startTag->htmlElement !== null ? $startTag->position->column - 1 : 0;
524+
525+
foreach ($fragment->children as $i => $child) {
526+
if ($child instanceof Nodes\TextNode && $child->content === '') {
527+
continue;
528+
529+
} elseif (!$inlineChecked) {
530+
$inlineChecked = true;
531+
if ($child->position?->line === $startTag->position->line) {
532+
return;
533+
}
534+
}
535+
if (!$child instanceof Nodes\TextNode) {
536+
continue;
537+
}
538+
539+
$lines = explode("\n", $child->content);
540+
$lineCount = count($lines);
541+
$lastContinues = !str_ends_with($child->content, "\n") && $i + 1 < count($fragment->children);
542+
543+
foreach ($lines as $j => &$line) {
544+
$isLineStart = $j === 0 ? $atLineStart : true;
545+
if (!$isLineStart || $line === '') {
546+
continue;
547+
}
548+
549+
$hasContent = trim($line) !== '';
550+
$continuesWithExpr = !$hasContent && $j === $lineCount - 1 && $lastContinues;
551+
552+
if ($baseIndent === null) {
553+
if ($hasContent) {
554+
preg_match('/^(\t+| +)/', $line, $m);
555+
$baseIndent = $m[1] ?? null;
556+
if ($baseIndent === null || strlen($baseIndent) <= $tagIndentLen) {
557+
return; // first content line has no indent beyond tag level
558+
}
559+
560+
} elseif ($continuesWithExpr) {
561+
// Top-level tag whose first child is an HTML element: leave structural
562+
// HTML indentation untouched.
563+
if ($startTag->htmlElement === null
564+
&& ($fragment->children[$i + 1] ?? null) instanceof Nodes\Html\ElementNode
565+
) {
566+
return;
567+
}
568+
$baseIndent = $line;
569+
if (strlen($baseIndent) <= $tagIndentLen) {
570+
return;
571+
}
572+
573+
} else {
574+
continue; // blank line before detection
575+
}
576+
577+
} elseif (!str_starts_with($line, $baseIndent)) {
578+
if ($hasContent || $continuesWithExpr) {
579+
throw new CompileException('Inconsistent indentation.', $child->position ? new Position($child->position->line + $j, 1) : null);
580+
}
581+
582+
continue; // blank line, strip silently
583+
}
584+
585+
$line = substr($line, 0, $tagIndentLen) . substr($line, strlen((string) $baseIndent));
586+
}
587+
588+
unset($line);
589+
$child->content = implode("\n", $lines);
590+
$atLineStart = str_ends_with($child->content, "\n");
591+
}
592+
}
593+
=======
594+
>>>>>>> master
509595
}

0 commit comments

Comments
 (0)