Vous êtes connecté en tant que anonymous Se Deconnecter
vendor/illuminate/view/Compilers/BladeCompiler.php
4f5a31d2
 <?php
 
 namespace Illuminate\View\Compilers;
 
 use Illuminate\Support\Arr;
 use Illuminate\Support\Str;
 use InvalidArgumentException;
 
 class BladeCompiler extends Compiler implements CompilerInterface
 {
     use Concerns\CompilesAuthorizations,
         Concerns\CompilesComments,
         Concerns\CompilesComponents,
         Concerns\CompilesConditionals,
         Concerns\CompilesEchos,
         Concerns\CompilesErrors,
         Concerns\CompilesHelpers,
         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|null  $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())
             );
 
             if (! empty($this->getPath())) {
                 $tokens = $this->getOpenAndClosingPhpTokens($contents);
 
                 // If the tokens we retrieved from the compiled contents have at least
                 // one opening tag and if that last token isn't the closing tag, we
                 // need to close the statement before adding the path at the end.
                 if ($tokens->isNotEmpty() && $tokens->last() !== T_CLOSE_TAG) {
                     $contents .= ' ?>';
                 }
 
                 $contents .= "<?php /**PATH {$this->getPath()} ENDPATH**/ ?>";
             }
 
             $this->files->put(
                 $this->getCompiledPath($this->getPath()), $contents
             );
         }
     }
 
     /**
      * Get the open and closing PHP tag tokens from the given string.
      *
      * @param  string  $contents
      * @return \Illuminate\Support\Collection
      */
     protected function getOpenAndClosingPhpTokens($contents)
     {
         return collect(token_get_all($contents))
             ->pluck($tokenNumber = 0)
             ->filter(function ($token) {
                 return in_array($token, [T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, T_CLOSE_TAG]);
             });
     }
 
     /**
      * 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)
     {
         [$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 component alias directive.
      *
      * @param  string  $path
      * @param  string|null  $alias
      * @return void
      */
     public function component($path, $alias = null)
     {
         $alias = $alias ?: Arr::last(explode('.', $path));
 
         $this->directive($alias, function ($expression) use ($path) {
             return $expression
                         ? "<?php \$__env->startComponent('{$path}', {$expression}); ?>"
                         : "<?php \$__env->startComponent('{$path}'); ?>";
         });
 
         $this->directive('end'.$alias, function ($expression) {
             return '<?php echo $__env->renderComponent(); ?>';
         });
     }
 
     /**
      * Register an include alias directive.
      *
      * @param  string  $path
      * @param  string|null  $alias
      * @return void
      */
     public function include($path, $alias = null)
     {
         $alias = $alias ?: Arr::last(explode('.', $path));
 
         $this->directive($alias, function ($expression) use ($path) {
             $expression = $this->stripParentheses($expression) ?: '[]';
 
             return "<?php echo \$__env->make('{$path}', {$expression}, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>";
         });
     }
 
     /**
      * Register a handler for custom directives.
      *
      * @param  string  $name
      * @param  callable  $handler
      * @return void
      */
     public function directive($name, callable $handler)
     {
         if (! preg_match('/^\w+(?:::\w+)?$/x', $name)) {
             throw new InvalidArgumentException("The directive name [{$name}] is not valid. Directive names must only contain alphanumeric characters and underscores.");
         }
 
         $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 withDoubleEncoding()
     {
         $this->setEchoFormat('e(%s, true)');
     }
 
     /**
      * Set the "echo" format to not double encode entities.
      *
      * @return void
      */
     public function withoutDoubleEncoding()
     {
         $this->setEchoFormat('e(%s, false)');
     }
 }