%PDF- %PDF-
Direktori : /home/silvzytp/ccd-ind-code/vendor/laravel/framework/src/Illuminate/View/Compilers/ |
Current File : //home/silvzytp/ccd-ind-code/vendor/laravel/framework/src/Illuminate/View/Compilers/BladeCompiler.php |
<?php namespace Illuminate\View\Compilers; use Illuminate\Container\Container; use Illuminate\Contracts\Support\Htmlable; use Illuminate\Contracts\View\Factory as ViewFactory; use Illuminate\Contracts\View\View; use Illuminate\Support\Arr; use Illuminate\Support\Str; use Illuminate\Support\Traits\ReflectsClosures; use Illuminate\View\Component; use InvalidArgumentException; class BladeCompiler extends Compiler implements CompilerInterface { use Concerns\CompilesAuthorizations, Concerns\CompilesClasses, Concerns\CompilesComments, Concerns\CompilesComponents, Concerns\CompilesConditionals, Concerns\CompilesEchos, Concerns\CompilesErrors, Concerns\CompilesFragments, Concerns\CompilesHelpers, Concerns\CompilesIncludes, Concerns\CompilesInjections, Concerns\CompilesJson, Concerns\CompilesJs, Concerns\CompilesLayouts, Concerns\CompilesLoops, Concerns\CompilesRawPhp, Concerns\CompilesStacks, Concerns\CompilesTranslations, ReflectsClosures; /** * 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 = []; /** * All of the registered precompilers. * * @var array */ protected $precompilers = []; /** * The file currently being compiled. * * @var string */ protected $path; /** * All of the available compiler functions. * * @var string[] */ protected $compilers = [ // 'Comments', 'Extensions', 'Statements', 'Echos', ]; /** * Array of opening and closing tags for raw echos. * * @var string[] */ protected $rawTags = ['{!!', '!!}']; /** * Array of opening and closing tags for regular echos. * * @var string[] */ protected $contentTags = ['{{', '}}']; /** * Array of opening and closing tags for escaped echos. * * @var string[] */ protected $escapedTags = ['{{{', '}}}']; /** * The "regular" / legacy echo string format. * * @var string */ protected $echoFormat = 'e(%s)'; /** * Array of footer lines to be added to the template. * * @var array */ protected $footer = []; /** * Array to temporarily store the raw blocks found in the template. * * @var array */ protected $rawBlocks = []; /** * The array of anonymous component paths to search for components in. * * @var array */ protected $anonymousComponentPaths = []; /** * The array of anonymous component namespaces to autoload from. * * @var array */ protected $anonymousComponentNamespaces = []; /** * The array of class component aliases and their class names. * * @var array */ protected $classComponentAliases = []; /** * The array of class component namespaces to autoload from. * * @var array */ protected $classComponentNamespaces = []; /** * Indicates if component tags should be compiled. * * @var bool */ protected $compilesComponentTags = true; /** * 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())) { $contents = $this->appendFilePath($contents); } $this->ensureCompiledDirectoryExists( $compiledPath = $this->getCompiledPath($this->getPath()) ); $this->files->put($compiledPath, $contents); } } /** * Append the file path to the compiled string. * * @param string $contents * @return string */ protected function appendFilePath($contents) { $tokens = $this->getOpenAndClosingPhpTokens($contents); if ($tokens->isNotEmpty() && $tokens->last() !== T_CLOSE_TAG) { $contents .= ' ?>'; } return $contents."<?php /**PATH {$this->getPath()} ENDPATH**/ ?>"; } /** * 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(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) { [$this->footer, $result] = [[], '']; // First we will compile the Blade component tags. This is a precompile style // step which compiles the component Blade tags into @component directives // that may be used by Blade. Then we should call any other precompilers. $value = $this->compileComponentTags( $this->compileComments($this->storeUncompiledBlocks($value)) ); foreach ($this->precompilers as $precompiler) { $value = $precompiler($value); } // 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); } if (! empty($this->echoHandlers)) { $result = $this->addBladeCompilerVariable($result); } return str_replace( ['##BEGIN-COMPONENT-CLASS##', '##END-COMPONENT-CLASS##'], '', $result); } /** * Evaluate and render a Blade string to HTML. * * @param string $string * @param array $data * @param bool $deleteCachedView * @return string */ public static function render($string, $data = [], $deleteCachedView = false) { $component = new class($string) extends Component { protected $template; public function __construct($template) { $this->template = $template; } public function render() { return $this->template; } }; $view = Container::getInstance() ->make(ViewFactory::class) ->make($component->resolveView(), $data); return tap($view->render(), function () use ($view, $deleteCachedView) { if ($deleteCachedView) { unlink($view->getPath()); } }); } /** * Render a component instance to HTML. * * @param \Illuminate\View\Component $component * @return string */ public static function renderComponent(Component $component) { $data = $component->data(); $view = value($component->resolveView(), $data); if ($view instanceof View) { return $view->with($data)->render(); } elseif ($view instanceof Htmlable) { return $view->toHtml(); } else { return Container::getInstance() ->make(ViewFactory::class) ->make($view, $data) ->render(); } } /** * Store the blocks that do not receive compilation. * * @param string $value * @return string */ protected function storeUncompiledBlocks($value) { if (str_contains($value, '@verbatim')) { $value = $this->storeVerbatimBlocks($value); } if (str_contains($value, '@php')) { $value = $this->storePhpBlocks($value); } return $value; } /** * 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 ); } /** * Compile the component tags. * * @param string $value * @return string */ protected function compileComponentTags($value) { if (! $this->compilesComponentTags) { return $value; } return (new ComponentTagCompiler( $this->classComponentAliases, $this->classComponentNamespaces, $this ))->compile($value); } /** * 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 temporarily 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, "\n") ."\n".implode("\n", 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 = $compiler($value, $this); } return $value; } /** * Compile Blade statements that start with "@". * * @param string $template * @return string */ protected function compileStatements($template) { preg_match_all('/\B@(@?\w+(?:::\w+)?)([ \t]*)(\( ( [\S\s]*? ) \))?/x', $template, $matches); for ($i = 0; isset($matches[0][$i]); $i++) { $match = [ $matches[0][$i], $matches[1][$i], $matches[2][$i], $matches[3][$i] ?: null, $matches[4][$i] ?: null, ]; // Here we check to see if we have properly found the closing parenthesis by // regex pattern or not, and will recursively continue on to the next ")" // then check again until the tokenizer confirms we find the right one. while (isset($match[4]) && Str::endsWith($match[0], ')') && ! $this->hasEvenNumberOfParentheses($match[0])) { $rest = Str::before(Str::after($template, $match[0]), ')'); $match[0] = $match[0].$rest.')'; $match[3] = $match[3].$rest.')'; $match[4] = $match[4].$rest; } $template = Str::replaceFirst($match[0], $this->compileStatement($match), $template); } return $template; } /** * Determine if the given expression has the same number of opening and closing parentheses. * * @param string $expression * @return bool */ protected function hasEvenNumberOfParentheses(string $expression) { $tokens = token_get_all('<?php '.$expression); if (Arr::last($tokens) !== ')') { return false; } $opening = 0; $closing = 0; foreach ($tokens as $token) { if ($token == ')') { $closing++; } elseif ($token == '(') { $opening++; } } return $opening === $closing; } /** * 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)); } else { return $match[0]; } 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) { $value ??= ''; if (str_starts_with($value, '(') && str_ends_with($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('unless'.$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 class-based component alias directive. * * @param string $class * @param string|null $alias * @param string $prefix * @return void */ public function component($class, $alias = null, $prefix = '') { if (! is_null($alias) && str_contains($alias, '\\')) { [$class, $alias] = [$alias, $class]; } if (is_null($alias)) { $alias = str_contains($class, '\\View\\Components\\') ? collect(explode('\\', Str::after($class, '\\View\\Components\\')))->map(function ($segment) { return Str::kebab($segment); })->implode(':') : Str::kebab(class_basename($class)); } if (! empty($prefix)) { $alias = $prefix.'-'.$alias; } $this->classComponentAliases[$alias] = $class; } /** * Register an array of class-based components. * * @param array $components * @param string $prefix * @return void */ public function components(array $components, $prefix = '') { foreach ($components as $key => $value) { if (is_numeric($key)) { $this->component($value, null, $prefix); } else { $this->component($key, $value, $prefix); } } } /** * Get the registered class component aliases. * * @return array */ public function getClassComponentAliases() { return $this->classComponentAliases; } /** * Register a new anonymous component path. * * @param string $path * @param string|null $prefix * @return void */ public function anonymousComponentPath(string $path, string $prefix = null) { $prefixHash = md5($prefix ?: $path); $this->anonymousComponentPaths[] = [ 'path' => $path, 'prefix' => $prefix, 'prefixHash' => $prefixHash, ]; Container::getInstance() ->make(ViewFactory::class) ->addNamespace($prefixHash, $path); } /** * Register an anonymous component namespace. * * @param string $directory * @param string|null $prefix * @return void */ public function anonymousComponentNamespace(string $directory, string $prefix = null) { $prefix ??= $directory; $this->anonymousComponentNamespaces[$prefix] = Str::of($directory) ->replace('/', '.') ->trim('. ') ->toString(); } /** * Register a class-based component namespace. * * @param string $namespace * @param string $prefix * @return void */ public function componentNamespace($namespace, $prefix) { $this->classComponentNamespaces[$prefix] = $namespace; } /** * Get the registered anonymous component paths. * * @return array */ public function getAnonymousComponentPaths() { return $this->anonymousComponentPaths; } /** * Get the registered anonymous component namespaces. * * @return array */ public function getAnonymousComponentNamespaces() { return $this->anonymousComponentNamespaces; } /** * Get the registered class component namespaces. * * @return array */ public function getClassComponentNamespaces() { return $this->classComponentNamespaces; } /** * Register a component alias directive. * * @param string $path * @param string|null $alias * @return void */ public function aliasComponent($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) { $this->aliasInclude($path, $alias); } /** * Register an include alias directive. * * @param string $path * @param string|null $alias * @return void */ public function aliasInclude($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 * * @throws \InvalidArgumentException */ 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; } /** * Register a new precompiler. * * @param callable $precompiler * @return void */ public function precompiler(callable $precompiler) { $this->precompilers[] = $precompiler; } /** * 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)'); } /** * Indicate that component tags should not be compiled. * * @return void */ public function withoutComponentTags() { $this->compilesComponentTags = false; } }