Skip to content

Commit

Permalink
Engine::setPhpBinary() allows to lint generated PHP templates
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 7, 2023
1 parent 64aab3e commit ac958fc
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 32 deletions.
30 changes: 30 additions & 0 deletions src/Latte/Compiler/PhpHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,34 @@ private static function codePointToUtf8(int $num): string
default => throw new CompileException('Invalid UTF-8 codepoint escape sequence: Codepoint too large'),
};
}


public static function checkSyntax(string $phpBinary, string $code, string $name): void
{
$process = proc_open(
$phpBinary . ' -l -n',
[['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']],
$pipes,
null,
null,
['bypass_shell' => true],
);
if (!is_resource($process)) {
throw new CompileException('Unable to check that the generated PHP is syntactically correct.');
}

fwrite($pipes[0], $code);
fclose($pipes[0]);
$error = stream_get_contents($pipes[1]);
if (!proc_close($process)) {
return;
}
$error = strip_tags(explode("\n", $error)[1]);
$position = preg_match('~ on line (\d+)~', $error, $m)
? new Position((int) $m[1], 0)
: null;
$error = preg_replace('~(^Fatal error: | in Standard input code| on line \d+)~', '', $error);
throw (new CompileException('Syntax error in generated code: ' . trim($error), $position))
->setSource($code, $name);
}
}
2 changes: 1 addition & 1 deletion src/Latte/Compiler/Position.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ public function advance(string $str): self

public function toWords(): string
{
return "on line $this->line at column $this->column";
return "on line $this->line" . ($this->column ? " at column $this->column" : '');
}
}
15 changes: 15 additions & 0 deletions src/Latte/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Engine
private bool $strictTypes = false;
private ?Policy $policy = null;
private bool $sandboxed = false;
private ?string $phpBinary = null;


public function __construct()
Expand Down Expand Up @@ -126,6 +127,10 @@ public function compile(string $name): string
throw $e->setSource($source, $name);
}

if ($this->phpBinary) {
Compiler\PhpHelpers::checkSyntax($this->phpBinary, $code, "(compiled $name)");
}

return $code;
}

Expand Down Expand Up @@ -544,6 +549,16 @@ public function getLoader(): Loader
}


/**
* Enables linting of generated PHP templates.
*/
public function setPhpBinary(?string $phpBinary): static
{
$this->phpBinary = $phpBinary;
return $this;
}


/**
* @param object|mixed[] $params
* @return mixed[]
Expand Down
33 changes: 2 additions & 31 deletions src/Tools/Linter.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function scanDirectory(string $path): bool
private function createEngine(): Latte\Engine
{
$engine = new Latte\Engine;
$engine->setPhpBinary(PHP_BINARY);
$engine->addExtension(new Latte\Essential\TranslatorExtension(null));

if (class_exists(Nette\Bridges\ApplicationLatte\UIExtension::class)) {
Expand Down Expand Up @@ -91,7 +92,7 @@ public function lintLatte(string $file): bool
}

try {
$code = $this->engine->compile($s);
$this->engine->compile($s);

} catch (Latte\CompileException $e) {
if ($this->debug) {
Expand All @@ -106,40 +107,10 @@ public function lintLatte(string $file): bool
restore_error_handler();
}

if ($error = $this->lintPHP($code)) {
fwrite(STDERR, "[ERROR] $file $error\n");
return false;
}

return true;
}


private function lintPHP(string $code): ?string
{
$php = defined('PHP_BINARY') ? PHP_BINARY : 'php';
$stdin = tmpfile();
fwrite($stdin, $code);
fseek($stdin, 0);
$process = proc_open(
$php . ' -l -d display_errors=1',
[$stdin, ['pipe', 'w'], ['pipe', 'w']],
$pipes,
null,
null,
['bypass_shell' => true],
);
if (!is_resource($process)) {
return 'Unable to lint PHP code';
}
$error = stream_get_contents($pipes[1]);
if (proc_close($process)) {
return strip_tags(explode("\n", $error)[1]);
}
return null;
}


private function initialize(): void
{
if (function_exists('pcntl_signal')) {
Expand Down
18 changes: 18 additions & 0 deletions tests/common/syntax.error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


$latte = new Latte\Engine;
$latte->setLoader(new Latte\Loaders\StringLoader);
$latte->setPhpBinary(PHP_BINARY);

Assert::exception(
fn() => $latte->compile('{= [&$x] = []}'),
Latte\CompileException::class,
'Syntax error in generated code: Cannot assign %a% (on line %d%)',
);

0 comments on commit ac958fc

Please sign in to comment.