469 lines
11 KiB
PHP
469 lines
11 KiB
PHP
<?php
|
|
|
|
namespace Illuminate\View\Compilers;
|
|
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Str;
|
|
|
|
class BladeCompiler extends Compiler implements CompilerInterface
|
|
{
|
|
use Concerns\CompilesAuthorizations,
|
|
Concerns\CompilesComments,
|
|
Concerns\CompilesComponents,
|
|
Concerns\CompilesConditionals,
|
|
Concerns\CompilesEchos,
|
|
Concerns\CompilesIncludes,
|
|
Concerns\CompilesInjections,
|
|
Concerns\CompilesJson,
|
|
Concerns\CompilesLayouts,
|
|
Concerns\CompilesLoops,
|
|
Concerns\CompilesRawPhp,
|
|
Concerns\CompilesStacks,
|
|
Concerns\CompilesTranslations;
|
|
|
|
/**
|
|
* All of the registered extensions.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $extensions = [];
|
|
|
|
/**
|
|
* All custom "directive" handlers.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $customDirectives = [];
|
|
|
|
/**
|
|
* All custom "condition" handlers.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $conditions = [];
|
|
|
|
/**
|
|
* The file currently being compiled.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $path;
|
|
|
|
/**
|
|
* All of the available compiler functions.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $compilers = [
|
|
'Comments',
|
|
'Extensions',
|
|
'Statements',
|
|
'Echos',
|
|
];
|
|
|
|
/**
|
|
* Array of opening and closing tags for raw echos.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $rawTags = ['{!!', '!!}'];
|
|
|
|
/**
|
|
* Array of opening and closing tags for regular echos.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $contentTags = ['{{', '}}'];
|
|
|
|
/**
|
|
* Array of opening and closing tags for escaped echos.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $escapedTags = ['{{{', '}}}'];
|
|
|
|
/**
|
|
* The "regular" / legacy echo string format.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $echoFormat = 'e(%s)';
|
|
|
|
/**
|
|
* Array of footer lines to be added to template.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $footer = [];
|
|
|
|
/**
|
|
* Array to temporary store the raw blocks found in the template.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $rawBlocks = [];
|
|
|
|
/**
|
|
* Compile the view at the given path.
|
|
*
|
|
* @param string $path
|
|
* @return void
|
|
*/
|
|
public function compile($path = null)
|
|
{
|
|
if ($path) {
|
|
$this->setPath($path);
|
|
}
|
|
|
|
if (! is_null($this->cachePath)) {
|
|
$contents = $this->compileString($this->files->get($this->getPath()));
|
|
|
|
$this->files->put($this->getCompiledPath($this->getPath()), $contents);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the path currently being compiled.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getPath()
|
|
{
|
|
return $this->path;
|
|
}
|
|
|
|
/**
|
|
* Set the path currently being compiled.
|
|
*
|
|
* @param string $path
|
|
* @return void
|
|
*/
|
|
public function setPath($path)
|
|
{
|
|
$this->path = $path;
|
|
}
|
|
|
|
/**
|
|
* Compile the given Blade template contents.
|
|
*
|
|
* @param string $value
|
|
* @return string
|
|
*/
|
|
public function compileString($value)
|
|
{
|
|
if (strpos($value, '@verbatim') !== false) {
|
|
$value = $this->storeVerbatimBlocks($value);
|
|
}
|
|
|
|
$this->footer = [];
|
|
|
|
if (strpos($value, '@php') !== false) {
|
|
$value = $this->storePhpBlocks($value);
|
|
}
|
|
|
|
$result = '';
|
|
|
|
// Here we will loop through all of the tokens returned by the Zend lexer and
|
|
// parse each one into the corresponding valid PHP. We will then have this
|
|
// template as the correctly rendered PHP that can be rendered natively.
|
|
foreach (token_get_all($value) as $token) {
|
|
$result .= is_array($token) ? $this->parseToken($token) : $token;
|
|
}
|
|
|
|
if (! empty($this->rawBlocks)) {
|
|
$result = $this->restoreRawContent($result);
|
|
}
|
|
|
|
// If there are any footer lines that need to get added to a template we will
|
|
// add them here at the end of the template. This gets used mainly for the
|
|
// template inheritance via the extends keyword that should be appended.
|
|
if (count($this->footer) > 0) {
|
|
$result = $this->addFooters($result);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Store the verbatim blocks and replace them with a temporary placeholder.
|
|
*
|
|
* @param string $value
|
|
* @return string
|
|
*/
|
|
protected function storeVerbatimBlocks($value)
|
|
{
|
|
return preg_replace_callback('/(?<!@)@verbatim(.*?)@endverbatim/s', function ($matches) {
|
|
return $this->storeRawBlock($matches[1]);
|
|
}, $value);
|
|
}
|
|
|
|
/**
|
|
* Store the PHP blocks and replace them with a temporary placeholder.
|
|
*
|
|
* @param string $value
|
|
* @return string
|
|
*/
|
|
protected function storePhpBlocks($value)
|
|
{
|
|
return preg_replace_callback('/(?<!@)@php(.*?)@endphp/s', function ($matches) {
|
|
return $this->storeRawBlock("<?php{$matches[1]}?>");
|
|
}, $value);
|
|
}
|
|
|
|
/**
|
|
* Store a raw block and return a unique raw placeholder.
|
|
*
|
|
* @param string $value
|
|
* @return string
|
|
*/
|
|
protected function storeRawBlock($value)
|
|
{
|
|
return $this->getRawPlaceholder(
|
|
array_push($this->rawBlocks, $value) - 1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Replace the raw placeholders with the original code stored in the raw blocks.
|
|
*
|
|
* @param string $result
|
|
* @return string
|
|
*/
|
|
protected function restoreRawContent($result)
|
|
{
|
|
$result = preg_replace_callback('/'.$this->getRawPlaceholder('(\d+)').'/', function ($matches) {
|
|
return $this->rawBlocks[$matches[1]];
|
|
}, $result);
|
|
|
|
$this->rawBlocks = [];
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get a placeholder to temporary mark the position of raw blocks.
|
|
*
|
|
* @param int|string $replace
|
|
* @return string
|
|
*/
|
|
protected function getRawPlaceholder($replace)
|
|
{
|
|
return str_replace('#', $replace, '@__raw_block_#__@');
|
|
}
|
|
|
|
/**
|
|
* Add the stored footers onto the given content.
|
|
*
|
|
* @param string $result
|
|
* @return string
|
|
*/
|
|
protected function addFooters($result)
|
|
{
|
|
return ltrim($result, PHP_EOL)
|
|
.PHP_EOL.implode(PHP_EOL, array_reverse($this->footer));
|
|
}
|
|
|
|
/**
|
|
* Parse the tokens from the template.
|
|
*
|
|
* @param array $token
|
|
* @return string
|
|
*/
|
|
protected function parseToken($token)
|
|
{
|
|
list($id, $content) = $token;
|
|
|
|
if ($id == T_INLINE_HTML) {
|
|
foreach ($this->compilers as $type) {
|
|
$content = $this->{"compile{$type}"}($content);
|
|
}
|
|
}
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Execute the user defined extensions.
|
|
*
|
|
* @param string $value
|
|
* @return string
|
|
*/
|
|
protected function compileExtensions($value)
|
|
{
|
|
foreach ($this->extensions as $compiler) {
|
|
$value = call_user_func($compiler, $value, $this);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Compile Blade statements that start with "@".
|
|
*
|
|
* @param string $value
|
|
* @return string
|
|
*/
|
|
protected function compileStatements($value)
|
|
{
|
|
return preg_replace_callback(
|
|
'/\B@(@?\w+(?:::\w+)?)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', function ($match) {
|
|
return $this->compileStatement($match);
|
|
}, $value
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Compile a single Blade @ statement.
|
|
*
|
|
* @param array $match
|
|
* @return string
|
|
*/
|
|
protected function compileStatement($match)
|
|
{
|
|
if (Str::contains($match[1], '@')) {
|
|
$match[0] = isset($match[3]) ? $match[1].$match[3] : $match[1];
|
|
} elseif (isset($this->customDirectives[$match[1]])) {
|
|
$match[0] = $this->callCustomDirective($match[1], Arr::get($match, 3));
|
|
} elseif (method_exists($this, $method = 'compile'.ucfirst($match[1]))) {
|
|
$match[0] = $this->$method(Arr::get($match, 3));
|
|
}
|
|
|
|
return isset($match[3]) ? $match[0] : $match[0].$match[2];
|
|
}
|
|
|
|
/**
|
|
* Call the given directive with the given value.
|
|
*
|
|
* @param string $name
|
|
* @param string|null $value
|
|
* @return string
|
|
*/
|
|
protected function callCustomDirective($name, $value)
|
|
{
|
|
if (Str::startsWith($value, '(') && Str::endsWith($value, ')')) {
|
|
$value = Str::substr($value, 1, -1);
|
|
}
|
|
|
|
return call_user_func($this->customDirectives[$name], trim($value));
|
|
}
|
|
|
|
/**
|
|
* Strip the parentheses from the given expression.
|
|
*
|
|
* @param string $expression
|
|
* @return string
|
|
*/
|
|
public function stripParentheses($expression)
|
|
{
|
|
if (Str::startsWith($expression, '(')) {
|
|
$expression = substr($expression, 1, -1);
|
|
}
|
|
|
|
return $expression;
|
|
}
|
|
|
|
/**
|
|
* Register a custom Blade compiler.
|
|
*
|
|
* @param callable $compiler
|
|
* @return void
|
|
*/
|
|
public function extend(callable $compiler)
|
|
{
|
|
$this->extensions[] = $compiler;
|
|
}
|
|
|
|
/**
|
|
* Get the extensions used by the compiler.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getExtensions()
|
|
{
|
|
return $this->extensions;
|
|
}
|
|
|
|
/**
|
|
* Register an "if" statement directive.
|
|
*
|
|
* @param string $name
|
|
* @param callable $callback
|
|
* @return void
|
|
*/
|
|
public function if($name, callable $callback)
|
|
{
|
|
$this->conditions[$name] = $callback;
|
|
|
|
$this->directive($name, function ($expression) use ($name) {
|
|
return $expression
|
|
? "<?php if (\Illuminate\Support\Facades\Blade::check('{$name}', {$expression})): ?>"
|
|
: "<?php if (\Illuminate\Support\Facades\Blade::check('{$name}')): ?>";
|
|
});
|
|
|
|
$this->directive('else'.$name, function ($expression) use ($name) {
|
|
return $expression
|
|
? "<?php elseif (\Illuminate\Support\Facades\Blade::check('{$name}', {$expression})): ?>"
|
|
: "<?php elseif (\Illuminate\Support\Facades\Blade::check('{$name}')): ?>";
|
|
});
|
|
|
|
$this->directive('end'.$name, function () {
|
|
return '<?php endif; ?>';
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Check the result of a condition.
|
|
*
|
|
* @param string $name
|
|
* @param array $parameters
|
|
* @return bool
|
|
*/
|
|
public function check($name, ...$parameters)
|
|
{
|
|
return call_user_func($this->conditions[$name], ...$parameters);
|
|
}
|
|
|
|
/**
|
|
* Register a handler for custom directives.
|
|
*
|
|
* @param string $name
|
|
* @param callable $handler
|
|
* @return void
|
|
*/
|
|
public function directive($name, callable $handler)
|
|
{
|
|
$this->customDirectives[$name] = $handler;
|
|
}
|
|
|
|
/**
|
|
* Get the list of custom directives.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getCustomDirectives()
|
|
{
|
|
return $this->customDirectives;
|
|
}
|
|
|
|
/**
|
|
* Set the echo format to be used by the compiler.
|
|
*
|
|
* @param string $format
|
|
* @return void
|
|
*/
|
|
public function setEchoFormat($format)
|
|
{
|
|
$this->echoFormat = $format;
|
|
}
|
|
|
|
/**
|
|
* Set the echo format to double encode entities.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function doubleEncode()
|
|
{
|
|
$this->setEchoFormat('e(%s, true)');
|
|
}
|
|
}
|