AnonymousComponent.php000064400000001747150145553620011147 0ustar00view = $view; $this->data = $data; } /** * Get the view / view contents that represent the component. * * @return string */ public function render() { return $this->view; } /** * Get the data that should be supplied to the view. * * @return array */ public function data() { $this->attributes = $this->attributes ?: new ComponentAttributeBag; return $this->data + ['attributes' => $this->attributes]; } } AppendableAttributeValue.php000064400000000552150145553620012201 0ustar00value = $value; } } Compilers/BladeCompiler.php000064400000045570150145553620011735 0ustar00setPath($path); } if (! is_null($this->cachePath)) { $contents = $this->compileString($this->files->get($this->getPath())); if (! empty($this->getPath())) { $contents = $this->appendFilePath($contents); } $this->files->put( $this->getCompiledPath($this->getPath()), $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."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 = call_user_func($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); } return $result; } /** * Store the blocks that do not receive compilation. * * @param string $value * @return string */ protected function storeUncompiledBlocks($value) { if (strpos($value, '@verbatim') !== false) { $value = $this->storeVerbatimBlocks($value); } if (strpos($value, '@php') !== false) { $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('/(?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('/(?storeRawBlock(""); }, $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 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, "\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 $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 !== '' ? "" : ""; }); $this->directive('unless'.$name, function ($expression) use ($name) { return $expression !== '' ? "" : ""; }); $this->directive('else'.$name, function ($expression) use ($name) { return $expression !== '' ? "" : ""; }); $this->directive('end'.$name, function () { return ''; }); } /** * 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)) { static::component($value, null, $prefix); } else { static::component($key, $value, $prefix); } } } /** * Get the registered class component aliases. * * @return array */ public function getClassComponentAliases() { return $this->classComponentAliases; } /** * 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 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 ? "startComponent('{$path}', {$expression}); ?>" : "startComponent('{$path}'); ?>"; }); $this->directive('end'.$alias, function ($expression) { return '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 "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; } } Compilers/Compiler.php000075500000003405150145553620010777 0ustar00files = $files; $this->cachePath = $cachePath; } /** * Get the path to the compiled version of a view. * * @param string $path * @return string */ public function getCompiledPath($path) { return $this->cachePath.'/'.sha1($path).'.php'; } /** * Determine if the view at the given path is expired. * * @param string $path * @return bool */ public function isExpired($path) { $compiled = $this->getCompiledPath($path); // If the compiled file doesn't exist we will indicate that the view is expired // so that it can be re-compiled. Else, we will verify the last modification // of the views is less than the modification times of the compiled views. if (! $this->files->exists($compiled)) { return true; } return $this->files->lastModified($path) >= $this->files->lastModified($compiled); } } Compilers/CompilerInterface.php000075500000001060150145553620012613 0ustar00 * @author Taylor Otwell */ class ComponentTagCompiler { /** * The Blade compiler instance. * * @var \Illuminate\View\Compilers\BladeCompiler */ protected $blade; /** * The component class aliases. * * @var array */ protected $aliases = []; /** * The component class namespaces. * * @var array */ protected $namespaces = []; /** * The "bind:" attributes that have been compiled for the current component. * * @var array */ protected $boundAttributes = []; /** * Create new component tag compiler. * * @param array $aliases * @param \Illuminate\View\Compilers\BladeCompiler|null * @return void */ public function __construct(array $aliases = [], array $namespaces = [], ?BladeCompiler $blade = null) { $this->aliases = $aliases; $this->namespaces = $namespaces; $this->blade = $blade ?: new BladeCompiler(new Filesystem, sys_get_temp_dir()); } /** * Compile the component and slot tags within the given string. * * @param string $value * @return string */ public function compile(string $value) { $value = $this->compileSlots($value); return $this->compileTags($value); } /** * Compile the tags within the given string. * * @param string $value * @return string * * @throws \InvalidArgumentException */ public function compileTags(string $value) { $value = $this->compileSelfClosingTags($value); $value = $this->compileOpeningTags($value); $value = $this->compileClosingTags($value); return $value; } /** * Compile the opening tags within the given string. * * @param string $value * @return string * * @throws \InvalidArgumentException */ protected function compileOpeningTags(string $value) { $pattern = "/ < \s* x[-\:]([\w\-\:\.]*) (? (?: \s+ (?: (?: \{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\} ) | (?: [\w\-:.@]+ ( = (?: \\\"[^\\\"]*\\\" | \'[^\']*\' | [^\'\\\"=<>]+ ) )? ) ) )* \s* ) (? /x"; return preg_replace_callback($pattern, function (array $matches) { $this->boundAttributes = []; $attributes = $this->getAttributesFromAttributeString($matches['attributes']); return $this->componentString($matches[1], $attributes); }, $value); } /** * Compile the self-closing tags within the given string. * * @param string $value * @return string * * @throws \InvalidArgumentException */ protected function compileSelfClosingTags(string $value) { $pattern = "/ < \s* x[-\:]([\w\-\:\.]*) \s* (? (?: \s+ (?: (?: \{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\} ) | (?: [\w\-:.@]+ ( = (?: \\\"[^\\\"]*\\\" | \'[^\']*\' | [^\'\\\"=<>]+ ) )? ) ) )* \s* ) \/> /x"; return preg_replace_callback($pattern, function (array $matches) { $this->boundAttributes = []; $attributes = $this->getAttributesFromAttributeString($matches['attributes']); return $this->componentString($matches[1], $attributes)."\n@endcomponentClass "; }, $value); } /** * Compile the Blade component string for the given component and attributes. * * @param string $component * @param array $attributes * @return string * * @throws \InvalidArgumentException */ protected function componentString(string $component, array $attributes) { $class = $this->componentClass($component); [$data, $attributes] = $this->partitionDataAndAttributes($class, $attributes); $data = $data->mapWithKeys(function ($value, $key) { return [Str::camel($key) => $value]; }); // If the component doesn't exists as a class we'll assume it's a class-less // component and pass the component as a view parameter to the data so it // can be accessed within the component and we can render out the view. if (! class_exists($class)) { $parameters = [ 'view' => "'$class'", 'data' => '['.$this->attributesToString($data->all(), $escapeBound = false).']', ]; $class = AnonymousComponent::class; } else { $parameters = $data->all(); } return " @component('{$class}', '{$component}', [".$this->attributesToString($parameters, $escapeBound = false).']) withAttributes(['.$this->attributesToString($attributes->all(), $escapeAttributes = $class !== DynamicComponent::class).']); ?>'; } /** * Get the component class for a given component alias. * * @param string $component * @return string * * @throws \InvalidArgumentException */ public function componentClass(string $component) { $viewFactory = Container::getInstance()->make(Factory::class); if (isset($this->aliases[$component])) { if (class_exists($alias = $this->aliases[$component])) { return $alias; } if ($viewFactory->exists($alias)) { return $alias; } throw new InvalidArgumentException( "Unable to locate class or view [{$alias}] for component [{$component}]." ); } if ($class = $this->findClassByComponent($component)) { return $class; } if (class_exists($class = $this->guessClassName($component))) { return $class; } if ($viewFactory->exists($view = $this->guessViewName($component))) { return $view; } throw new InvalidArgumentException( "Unable to locate a class or view for component [{$component}]." ); } /** * Find the class for the given component using the registered namespaces. * * @param string $component * @return string|null */ public function findClassByComponent(string $component) { $segments = explode('::', $component); $prefix = $segments[0]; if (! isset($this->namespaces[$prefix]) || ! isset($segments[1])) { return; } if (class_exists($class = $this->namespaces[$prefix].'\\'.$this->formatClassName($segments[1]))) { return $class; } } /** * Guess the class name for the given component. * * @param string $component * @return string */ public function guessClassName(string $component) { $namespace = Container::getInstance() ->make(Application::class) ->getNamespace(); $class = $this->formatClassName($component); return $namespace.'View\\Components\\'.$class; } /** * Format the class name for the given component. * * @param string $component * @return string */ public function formatClassName(string $component) { $componentPieces = array_map(function ($componentPiece) { return ucfirst(Str::camel($componentPiece)); }, explode('.', $component)); return implode('\\', $componentPieces); } /** * Guess the view name for the given component. * * @param string $name * @return string */ public function guessViewName($name) { $prefix = 'components.'; $delimiter = ViewFinderInterface::HINT_PATH_DELIMITER; if (Str::contains($name, $delimiter)) { return Str::replaceFirst($delimiter, $delimiter.$prefix, $name); } return $prefix.$name; } /** * Partition the data and extra attributes from the given array of attributes. * * @param string $class * @param array $attributes * @return array */ public function partitionDataAndAttributes($class, array $attributes) { // If the class doesn't exists, we'll assume it's a class-less component and // return all of the attributes as both data and attributes since we have // now way to partition them. The user can exclude attributes manually. if (! class_exists($class)) { return [collect($attributes), collect($attributes)]; } $constructor = (new ReflectionClass($class))->getConstructor(); $parameterNames = $constructor ? collect($constructor->getParameters())->map->getName()->all() : []; return collect($attributes)->partition(function ($value, $key) use ($parameterNames) { return in_array(Str::camel($key), $parameterNames); })->all(); } /** * Compile the closing tags within the given string. * * @param string $value * @return string */ protected function compileClosingTags(string $value) { return preg_replace("/<\/\s*x[-\:][\w\-\:\.]*\s*>/", ' @endcomponentClass ', $value); } /** * Compile the slot tags within the given string. * * @param string $value * @return string */ public function compileSlots(string $value) { $value = preg_replace_callback('/<\s*x[\-\:]slot\s+(:?)name=(?(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+))\s*>/', function ($matches) { $name = $this->stripQuotes($matches['name']); if ($matches[1] !== ':') { $name = "'{$name}'"; } return " @slot({$name}) "; }, $value); return preg_replace('/<\/\s*x[\-\:]slot[^>]*>/', ' @endslot', $value); } /** * Get an array of attributes from the given attribute string. * * @param string $attributeString * @return array */ protected function getAttributesFromAttributeString(string $attributeString) { $attributeString = $this->parseAttributeBag($attributeString); $attributeString = $this->parseBindAttributes($attributeString); $pattern = '/ (?[\w\-:.@]+) ( = (? ( \"[^\"]+\" | \\\'[^\\\']+\\\' | [^\s>]+ ) ) )? /x'; if (! preg_match_all($pattern, $attributeString, $matches, PREG_SET_ORDER)) { return []; } return collect($matches)->mapWithKeys(function ($match) { $attribute = $match['attribute']; $value = $match['value'] ?? null; if (is_null($value)) { $value = 'true'; $attribute = Str::start($attribute, 'bind:'); } $value = $this->stripQuotes($value); if (Str::startsWith($attribute, 'bind:')) { $attribute = Str::after($attribute, 'bind:'); $this->boundAttributes[$attribute] = true; } else { $value = "'".$this->compileAttributeEchos($value)."'"; } return [$attribute => $value]; })->toArray(); } /** * Parse the attribute bag in a given attribute string into it's fully-qualified syntax. * * @param string $attributeString * @return string */ protected function parseAttributeBag(string $attributeString) { $pattern = "/ (?:^|\s+) # start of the string or whitespace between attributes \{\{\s*(\\\$attributes(?:[^}]+?(?blade->compileEchos($attributeString); $value = $this->escapeSingleQuotesOutsideOfPhpBlocks($value); $value = str_replace('', '.\'', $value); return $value; } /** * Escape the single quotes in the given string that are outside of PHP blocks. * * @param string $value * @return string */ protected function escapeSingleQuotesOutsideOfPhpBlocks(string $value) { return collect(token_get_all($value))->map(function ($token) { if (! is_array($token)) { return $token; } return $token[0] === T_INLINE_HTML ? str_replace("'", "\\'", $token[1]) : $token[1]; })->implode(''); } /** * Convert an array of attributes to a string. * * @param array $attributes * @param bool $escapeBound * @return string */ protected function attributesToString(array $attributes, $escapeBound = true) { return collect($attributes) ->map(function (string $value, string $attribute) use ($escapeBound) { return $escapeBound && isset($this->boundAttributes[$attribute]) && $value !== 'true' && ! is_numeric($value) ? "'{$attribute}' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute({$value})" : "'{$attribute}' => {$value}"; }) ->implode(','); } /** * Strip any quotes from the given string. * * @param string $value * @return string */ public function stripQuotes(string $value) { return Str::startsWith($value, ['"', '\'']) ? substr($value, 1, -1) : $value; } } Compilers/Concerns/CompilesAuthorizations.php000064400000004706150145553620015520 0ustar00check{$expression}): ?>"; } /** * Compile the cannot statements into valid PHP. * * @param string $expression * @return string */ protected function compileCannot($expression) { return "denies{$expression}): ?>"; } /** * Compile the canany statements into valid PHP. * * @param string $expression * @return string */ protected function compileCanany($expression) { return "any{$expression}): ?>"; } /** * Compile the else-can statements into valid PHP. * * @param string $expression * @return string */ protected function compileElsecan($expression) { return "check{$expression}): ?>"; } /** * Compile the else-cannot statements into valid PHP. * * @param string $expression * @return string */ protected function compileElsecannot($expression) { return "denies{$expression}): ?>"; } /** * Compile the else-canany statements into valid PHP. * * @param string $expression * @return string */ protected function compileElsecanany($expression) { return "any{$expression}): ?>"; } /** * Compile the end-can statements into valid PHP. * * @return string */ protected function compileEndcan() { return ''; } /** * Compile the end-cannot statements into valid PHP. * * @return string */ protected function compileEndcannot() { return ''; } /** * Compile the end-canany statements into valid PHP. * * @return string */ protected function compileEndcanany() { return ''; } } Compilers/Concerns/CompilesComments.php000064400000000635150145553620014257 0ustar00contentTags[0], $this->contentTags[1]); return preg_replace($pattern, '', $value); } } Compilers/Concerns/CompilesComponents.php000064400000011572150145553620014621 0ustar00startComponent{$expression}; ?>"; } /** * Get a new component hash for a component name. * * @param string $component * @return string */ public static function newComponentHash(string $component) { static::$componentHashStack[] = $hash = sha1($component); return $hash; } /** * Compile a class component opening. * * @param string $component * @param string $alias * @param string $data * @param string $hash * @return string */ public static function compileClassComponentOpening(string $component, string $alias, string $data, string $hash) { return implode("\n", [ '', 'getContainer()->make('.Str::finish($component, '::class').', '.($data ?: '[]').'); ?>', 'withName('.$alias.'); ?>', 'shouldRender()): ?>', 'startComponent($component->resolveView(), $component->data()); ?>', ]); } /** * Compile the end-component statements into valid PHP. * * @return string */ protected function compileEndComponent() { $hash = array_pop(static::$componentHashStack); return implode("\n", [ '', '', '', '', 'renderComponent(); ?>', ]); } /** * Compile the end-component statements into valid PHP. * * @return string */ public function compileEndComponentClass() { return static::compileEndComponent()."\n".implode("\n", [ '', ]); } /** * Compile the slot statements into valid PHP. * * @param string $expression * @return string */ protected function compileSlot($expression) { return "slot{$expression}; ?>"; } /** * Compile the end-slot statements into valid PHP. * * @return string */ protected function compileEndSlot() { return 'endSlot(); ?>'; } /** * Compile the component-first statements into valid PHP. * * @param string $expression * @return string */ protected function compileComponentFirst($expression) { return "startComponentFirst{$expression}; ?>"; } /** * Compile the end-component-first statements into valid PHP. * * @return string */ protected function compileEndComponentFirst() { return $this->compileEndComponent(); } /** * Compile the prop statement into valid PHP. * * @param string $expression * @return string */ protected function compileProps($expression) { return "exceptProps{$expression}; ?> \$__value) { \$\$__key = \$\$__key ?? \$__value; } ?> \$__value) { if (array_key_exists(\$__key, \$__defined_vars)) unset(\$\$__key); } ?> "; } /** * Sanitize the given component attribute value. * * @param mixed $value * @return mixed */ public static function sanitizeComponentAttribute($value) { return is_string($value) || (is_object($value) && ! $value instanceof ComponentAttributeBag && method_exists($value, '__toString')) ? e($value) : $value; } } Compilers/Concerns/CompilesConditionals.php000064400000014754150145553620015127 0ustar00guard{$guard}->check()): ?>"; } /** * Compile the else-auth statements into valid PHP. * * @param string|null $guard * @return string */ protected function compileElseAuth($guard = null) { $guard = is_null($guard) ? '()' : $guard; return "guard{$guard}->check()): ?>"; } /** * Compile the end-auth statements into valid PHP. * * @return string */ protected function compileEndAuth() { return ''; } /** * Compile the env statements into valid PHP. * * @param string $environments * @return string */ protected function compileEnv($environments) { return "environment{$environments}): ?>"; } /** * Compile the end-env statements into valid PHP. * * @return string */ protected function compileEndEnv() { return ''; } /** * Compile the production statements into valid PHP. * * @return string */ protected function compileProduction() { return "environment('production')): ?>"; } /** * Compile the end-production statements into valid PHP. * * @return string */ protected function compileEndProduction() { return ''; } /** * Compile the if-guest statements into valid PHP. * * @param string|null $guard * @return string */ protected function compileGuest($guard = null) { $guard = is_null($guard) ? '()' : $guard; return "guard{$guard}->guest()): ?>"; } /** * Compile the else-guest statements into valid PHP. * * @param string|null $guard * @return string */ protected function compileElseGuest($guard = null) { $guard = is_null($guard) ? '()' : $guard; return "guard{$guard}->guest()): ?>"; } /** * Compile the end-guest statements into valid PHP. * * @return string */ protected function compileEndGuest() { return ''; } /** * Compile the has-section statements into valid PHP. * * @param string $expression * @return string */ protected function compileHasSection($expression) { return "yieldContent{$expression}))): ?>"; } /** * Compile the section-missing statements into valid PHP. * * @param string $expression * @return string */ protected function compileSectionMissing($expression) { return "yieldContent{$expression}))): ?>"; } /** * Compile the if statements into valid PHP. * * @param string $expression * @return string */ protected function compileIf($expression) { return ""; } /** * Compile the unless statements into valid PHP. * * @param string $expression * @return string */ protected function compileUnless($expression) { return ""; } /** * Compile the else-if statements into valid PHP. * * @param string $expression * @return string */ protected function compileElseif($expression) { return ""; } /** * Compile the else statements into valid PHP. * * @return string */ protected function compileElse() { return ''; } /** * Compile the end-if statements into valid PHP. * * @return string */ protected function compileEndif() { return ''; } /** * Compile the end-unless statements into valid PHP. * * @return string */ protected function compileEndunless() { return ''; } /** * Compile the if-isset statements into valid PHP. * * @param string $expression * @return string */ protected function compileIsset($expression) { return ""; } /** * Compile the end-isset statements into valid PHP. * * @return string */ protected function compileEndIsset() { return ''; } /** * Compile the switch statements into valid PHP. * * @param string $expression * @return string */ protected function compileSwitch($expression) { $this->firstCaseInSwitch = true; return "firstCaseInSwitch) { $this->firstCaseInSwitch = false; return "case {$expression}: ?>"; } return ""; } /** * Compile the default statements in switch case into valid PHP. * * @return string */ protected function compileDefault() { return ''; } /** * Compile the end switch statements into valid PHP. * * @return string */ protected function compileEndSwitch() { return ''; } /** * Compile an once block into valid PHP. * * @return string */ protected function compileOnce($id = null) { $id = $id ? $this->stripParentheses($id) : "'".(string) Str::uuid()."'"; return 'hasRenderedOnce('.$id.')): $__env->markAsRenderedOnce('.$id.'); ?>'; } /** * Compile an end-once block into valid PHP. * * @return string */ public function compileEndOnce() { return ''; } } Compilers/Concerns/CompilesEchos.php000064400000004702150145553620013532 0ustar00getEchoMethods() as $method) { $value = $this->$method($value); } return $value; } /** * Get the echo methods in the proper order for compilation. * * @return array */ protected function getEchoMethods() { return [ 'compileRawEchos', 'compileEscapedEchos', 'compileRegularEchos', ]; } /** * Compile the "raw" echo statements. * * @param string $value * @return string */ protected function compileRawEchos($value) { $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->rawTags[0], $this->rawTags[1]); $callback = function ($matches) { $whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3]; return $matches[1] ? substr($matches[0], 1) : "{$whitespace}"; }; return preg_replace_callback($pattern, $callback, $value); } /** * Compile the "regular" echo statements. * * @param string $value * @return string */ protected function compileRegularEchos($value) { $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]); $callback = function ($matches) { $whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3]; $wrapped = sprintf($this->echoFormat, $matches[2]); return $matches[1] ? substr($matches[0], 1) : "{$whitespace}"; }; return preg_replace_callback($pattern, $callback, $value); } /** * Compile the escaped echo statements. * * @param string $value * @return string */ protected function compileEscapedEchos($value) { $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->escapedTags[0], $this->escapedTags[1]); $callback = function ($matches) { $whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3]; return $matches[1] ? $matches[0] : "{$whitespace}"; }; return preg_replace_callback($pattern, $callback, $value); } } Compilers/Concerns/CompilesErrors.php000064400000001645150145553620013750 0ustar00stripParentheses($expression); return 'getBag($__errorArgs[1] ?? \'default\'); if ($__bag->has($__errorArgs[0])) : if (isset($message)) { $__messageOriginal = $message; } $message = $__bag->first($__errorArgs[0]); ?>'; } /** * Compile the enderror statements into valid PHP. * * @param string $expression * @return string */ protected function compileEnderror($expression) { return ''; } } Compilers/Concerns/CompilesHelpers.php000064400000001726150145553620014076 0ustar00'; } /** * Compile the "dd" statements into valid PHP. * * @param string $arguments * @return string */ protected function compileDd($arguments) { return ""; } /** * Compile the "dump" statements into valid PHP. * * @param string $arguments * @return string */ protected function compileDump($arguments) { return ""; } /** * Compile the method statements into valid PHP. * * @param string $method * @return string */ protected function compileMethod($method) { return ""; } } Compilers/Concerns/CompilesIncludes.php000064400000004601150145553620014235 0ustar00renderEach{$expression}; ?>"; } /** * Compile the include statements into valid PHP. * * @param string $expression * @return string */ protected function compileInclude($expression) { $expression = $this->stripParentheses($expression); return "make({$expression}, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>"; } /** * Compile the include-if statements into valid PHP. * * @param string $expression * @return string */ protected function compileIncludeIf($expression) { $expression = $this->stripParentheses($expression); return "exists({$expression})) echo \$__env->make({$expression}, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>"; } /** * Compile the include-when statements into valid PHP. * * @param string $expression * @return string */ protected function compileIncludeWhen($expression) { $expression = $this->stripParentheses($expression); return "renderWhen($expression, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path'])); ?>"; } /** * Compile the include-unless statements into valid PHP. * * @param string $expression * @return string */ protected function compileIncludeUnless($expression) { $expression = $this->stripParentheses($expression); return "renderWhen(! $expression, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path'])); ?>"; } /** * Compile the include-first statements into valid PHP. * * @param string $expression * @return string */ protected function compileIncludeFirst($expression) { $expression = $this->stripParentheses($expression); return "first({$expression}, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>"; } } Compilers/Concerns/CompilesInjections.php000064400000000771150145553620014600 0ustar00"; } } Compilers/Concerns/CompilesJson.php000064400000001325150145553620013400 0ustar00stripParentheses($expression)); $options = isset($parts[1]) ? trim($parts[1]) : $this->encodingOptions; $depth = isset($parts[2]) ? trim($parts[2]) : 512; return ""; } } Compilers/Concerns/CompilesLayouts.php000064400000005701150145553620014131 0ustar00stripParentheses($expression); $echo = "make({$expression}, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>"; $this->footer[] = $echo; return ''; } /** * Compile the extends-first statements into valid PHP. * * @param string $expression * @return string */ protected function compileExtendsFirst($expression) { $expression = $this->stripParentheses($expression); $echo = "first({$expression}, \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>"; $this->footer[] = $echo; return ''; } /** * Compile the section statements into valid PHP. * * @param string $expression * @return string */ protected function compileSection($expression) { $this->lastSection = trim($expression, "()'\" "); return "startSection{$expression}; ?>"; } /** * Replace the @parent directive to a placeholder. * * @return string */ protected function compileParent() { return ViewFactory::parentPlaceholder($this->lastSection ?: ''); } /** * Compile the yield statements into valid PHP. * * @param string $expression * @return string */ protected function compileYield($expression) { return "yieldContent{$expression}; ?>"; } /** * Compile the show statements into valid PHP. * * @return string */ protected function compileShow() { return 'yieldSection(); ?>'; } /** * Compile the append statements into valid PHP. * * @return string */ protected function compileAppend() { return 'appendSection(); ?>'; } /** * Compile the overwrite statements into valid PHP. * * @return string */ protected function compileOverwrite() { return 'stopSection(true); ?>'; } /** * Compile the stop statements into valid PHP. * * @return string */ protected function compileStop() { return 'stopSection(); ?>'; } /** * Compile the end-section statements into valid PHP. * * @return string */ protected function compileEndsection() { return 'stopSection(); ?>'; } } Compilers/Concerns/CompilesLoops.php000064400000010536150145553620013567 0ustar00forElseCounter; preg_match('/\( *(.*) +as *(.*)\)$/is', $expression, $matches); $iteratee = trim($matches[1]); $iteration = trim($matches[2]); $initLoop = "\$__currentLoopData = {$iteratee}; \$__env->addLoop(\$__currentLoopData);"; $iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();'; return ""; } /** * Compile the for-else-empty and empty statements into valid PHP. * * @param string $expression * @return string */ protected function compileEmpty($expression) { if ($expression) { return ""; } $empty = '$__empty_'.$this->forElseCounter--; return "popLoop(); \$loop = \$__env->getLastLoop(); if ({$empty}): ?>"; } /** * Compile the end-for-else statements into valid PHP. * * @return string */ protected function compileEndforelse() { return ''; } /** * Compile the end-empty statements into valid PHP. * * @return string */ protected function compileEndEmpty() { return ''; } /** * Compile the for statements into valid PHP. * * @param string $expression * @return string */ protected function compileFor($expression) { return ""; } /** * Compile the for-each statements into valid PHP. * * @param string $expression * @return string */ protected function compileForeach($expression) { preg_match('/\( *(.*) +as *(.*)\)$/is', $expression, $matches); $iteratee = trim($matches[1]); $iteration = trim($matches[2]); $initLoop = "\$__currentLoopData = {$iteratee}; \$__env->addLoop(\$__currentLoopData);"; $iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();'; return ""; } /** * Compile the break statements into valid PHP. * * @param string $expression * @return string */ protected function compileBreak($expression) { if ($expression) { preg_match('/\(\s*(-?\d+)\s*\)$/', $expression, $matches); return $matches ? '' : ""; } return ''; } /** * Compile the continue statements into valid PHP. * * @param string $expression * @return string */ protected function compileContinue($expression) { if ($expression) { preg_match('/\(\s*(-?\d+)\s*\)$/', $expression, $matches); return $matches ? '' : ""; } return ''; } /** * Compile the end-for statements into valid PHP. * * @return string */ protected function compileEndfor() { return ''; } /** * Compile the end-for-each statements into valid PHP. * * @return string */ protected function compileEndforeach() { return 'popLoop(); $loop = $__env->getLastLoop(); ?>'; } /** * Compile the while statements into valid PHP. * * @param string $expression * @return string */ protected function compileWhile($expression) { return ""; } /** * Compile the end-while statements into valid PHP. * * @return string */ protected function compileEndwhile() { return ''; } } Compilers/Concerns/CompilesRawPhp.php000064400000001154150145553620013670 0ustar00"; } return '@php'; } /** * Compile the unset statements into valid PHP. * * @param string $expression * @return string */ protected function compileUnset($expression) { return ""; } } Compilers/Concerns/CompilesStacks.php000064400000002364150145553620013723 0ustar00yieldPushContent{$expression}; ?>"; } /** * Compile the push statements into valid PHP. * * @param string $expression * @return string */ protected function compilePush($expression) { return "startPush{$expression}; ?>"; } /** * Compile the end-push statements into valid PHP. * * @return string */ protected function compileEndpush() { return 'stopPush(); ?>'; } /** * Compile the prepend statements into valid PHP. * * @param string $expression * @return string */ protected function compilePrepend($expression) { return "startPrepend{$expression}; ?>"; } /** * Compile the end-prepend statements into valid PHP. * * @return string */ protected function compileEndprepend() { return 'stopPrepend(); ?>'; } } Compilers/Concerns/CompilesTranslations.php000064400000002035150145553620015147 0ustar00startTranslation(); ?>'; } elseif ($expression[1] === '[') { return "startTranslation{$expression}; ?>"; } return "get{$expression}; ?>"; } /** * Compile the end-lang statements into valid PHP. * * @return string */ protected function compileEndlang() { return 'renderTranslation(); ?>'; } /** * Compile the choice statements into valid PHP. * * @param string $expression * @return string */ protected function compileChoice($expression) { return "choice{$expression}; ?>"; } } Component.php000064400000016436150145553620007237 0ustar00render(); if ($view instanceof ViewContract) { return $view; } if ($view instanceof Htmlable) { return $view; } $resolver = function ($view) { $factory = Container::getInstance()->make('view'); return $factory->exists($view) ? $view : $this->createBladeViewFromString($factory, $view); }; return $view instanceof Closure ? function (array $data = []) use ($view, $resolver) { return $resolver($view($data)); } : $resolver($view); } /** * Create a Blade view with the raw component string content. * * @param \Illuminate\Contracts\View\Factory $factory * @param string $contents * @return string */ protected function createBladeViewFromString($factory, $contents) { $factory->addNamespace( '__components', $directory = Container::getInstance()['config']->get('view.compiled') ); if (! is_file($viewFile = $directory.'/'.sha1($contents).'.blade.php')) { if (! is_dir($directory)) { mkdir($directory, 0755, true); } file_put_contents($viewFile, $contents); } return '__components::'.basename($viewFile, '.blade.php'); } /** * Get the data that should be supplied to the view. * * @author Freek Van der Herten * @author Brent Roose * * @return array */ public function data() { $this->attributes = $this->attributes ?: new ComponentAttributeBag; return array_merge($this->extractPublicProperties(), $this->extractPublicMethods()); } /** * Extract the public properties for the component. * * @return array */ protected function extractPublicProperties() { $class = get_class($this); if (! isset(static::$propertyCache[$class])) { $reflection = new ReflectionClass($this); static::$propertyCache[$class] = collect($reflection->getProperties(ReflectionProperty::IS_PUBLIC)) ->reject(function (ReflectionProperty $property) { return $property->isStatic(); }) ->reject(function (ReflectionProperty $property) { return $this->shouldIgnore($property->getName()); }) ->map(function (ReflectionProperty $property) { return $property->getName(); })->all(); } $values = []; foreach (static::$propertyCache[$class] as $property) { $values[$property] = $this->{$property}; } return $values; } /** * Extract the public methods for the component. * * @return array */ protected function extractPublicMethods() { $class = get_class($this); if (! isset(static::$methodCache[$class])) { $reflection = new ReflectionClass($this); static::$methodCache[$class] = collect($reflection->getMethods(ReflectionMethod::IS_PUBLIC)) ->reject(function (ReflectionMethod $method) { return $this->shouldIgnore($method->getName()); }) ->map(function (ReflectionMethod $method) { return $method->getName(); }); } $values = []; foreach (static::$methodCache[$class] as $method) { $values[$method] = $this->createVariableFromMethod(new ReflectionMethod($this, $method)); } return $values; } /** * Create a callable variable from the given method. * * @param \ReflectionMethod $method * @return mixed */ protected function createVariableFromMethod(ReflectionMethod $method) { return $method->getNumberOfParameters() === 0 ? $this->createInvokableVariable($method->getName()) : Closure::fromCallable([$this, $method->getName()]); } /** * Create an invokable, toStringable variable for the given component method. * * @param string $method * @return \Illuminate\View\InvokableComponentVariable */ protected function createInvokableVariable(string $method) { return new InvokableComponentVariable(function () use ($method) { return $this->{$method}(); }); } /** * Determine if the given property / method should be ignored. * * @param string $name * @return bool */ protected function shouldIgnore($name) { return Str::startsWith($name, '__') || in_array($name, $this->ignoredMethods()); } /** * Get the methods that should be ignored. * * @return array */ protected function ignoredMethods() { return array_merge([ 'data', 'render', 'resolveView', 'shouldRender', 'view', 'withName', 'withAttributes', ], $this->except); } /** * Set the component alias name. * * @param string $name * @return $this */ public function withName($name) { $this->componentName = $name; return $this; } /** * Set the extra attributes that the component should make available. * * @param array $attributes * @return $this */ public function withAttributes(array $attributes) { $this->attributes = $this->attributes ?: new ComponentAttributeBag; $this->attributes->setAttributes($attributes); return $this; } /** * Determine if the component should be rendered. * * @return bool */ public function shouldRender() { return true; } } ComponentAttributeBag.php000064400000022140150145553620011522 0ustar00attributes = $attributes; } /** * Get the first attribute's value. * * @param mixed $default * @return mixed */ public function first($default = null) { return $this->getIterator()->current() ?? value($default); } /** * Get a given attribute from the attribute array. * * @param string $key * @param mixed $default * @return mixed */ public function get($key, $default = null) { return $this->attributes[$key] ?? value($default); } /** * Only include the given attribute from the attribute array. * * @param mixed|array $keys * @return static */ public function only($keys) { if (is_null($keys)) { $values = $this->attributes; } else { $keys = Arr::wrap($keys); $values = Arr::only($this->attributes, $keys); } return new static($values); } /** * Exclude the given attribute from the attribute array. * * @param mixed|array $keys * @return static */ public function except($keys) { if (is_null($keys)) { $values = $this->attributes; } else { $keys = Arr::wrap($keys); $values = Arr::except($this->attributes, $keys); } return new static($values); } /** * Filter the attributes, returning a bag of attributes that pass the filter. * * @param callable $callback * @return static */ public function filter($callback) { return new static(collect($this->attributes)->filter($callback)->all()); } /** * Return a bag of attributes that have keys starting with the given value / pattern. * * @param string $string * @return static */ public function whereStartsWith($string) { return $this->filter(function ($value, $key) use ($string) { return Str::startsWith($key, $string); }); } /** * Return a bag of attributes with keys that do not start with the given value / pattern. * * @param string $string * @return static */ public function whereDoesntStartWith($string) { return $this->filter(function ($value, $key) use ($string) { return ! Str::startsWith($key, $string); }); } /** * Return a bag of attributes that have keys starting with the given value / pattern. * * @param string $string * @return static */ public function thatStartWith($string) { return $this->whereStartsWith($string); } /** * Exclude the given attribute from the attribute array. * * @param mixed|array $keys * @return static */ public function exceptProps($keys) { $props = []; foreach ($keys as $key => $defaultValue) { $key = is_numeric($key) ? $defaultValue : $key; $props[] = $key; $props[] = Str::kebab($key); } return $this->except($props); } /** * Merge additional attributes / values into the attribute bag. * * @param array $attributeDefaults * @param bool $escape * @return static */ public function merge(array $attributeDefaults = [], $escape = true) { $attributeDefaults = array_map(function ($value) use ($escape) { return $this->shouldEscapeAttributeValue($escape, $value) ? e($value) : $value; }, $attributeDefaults); [$appendableAttributes, $nonAppendableAttributes] = collect($this->attributes) ->partition(function ($value, $key) use ($attributeDefaults) { return $key === 'class' || (isset($attributeDefaults[$key]) && $attributeDefaults[$key] instanceof AppendableAttributeValue); }); $attributes = $appendableAttributes->mapWithKeys(function ($value, $key) use ($attributeDefaults, $escape) { $defaultsValue = isset($attributeDefaults[$key]) && $attributeDefaults[$key] instanceof AppendableAttributeValue ? $this->resolveAppendableAttributeDefault($attributeDefaults, $key, $escape) : ($attributeDefaults[$key] ?? ''); return [$key => implode(' ', array_unique(array_filter([$defaultsValue, $value])))]; })->merge($nonAppendableAttributes)->all(); return new static(array_merge($attributeDefaults, $attributes)); } /** * Determine if the specific attribute value should be escaped. * * @param bool $escape * @param mixed $value * @return bool */ protected function shouldEscapeAttributeValue($escape, $value) { if (! $escape) { return false; } return ! is_object($value) && ! is_null($value) && ! is_bool($value); } /** * Create a new appendable attribute value. * * @param mixed $value * @return \Illuminate\View\AppendableAttributeValue */ public function prepends($value) { return new AppendableAttributeValue($value); } /** * Resolve an appendable attribute value default value. * * @param array $attributeDefaults * @param string $key * @param bool $escape * @return mixed */ protected function resolveAppendableAttributeDefault($attributeDefaults, $key, $escape) { if ($this->shouldEscapeAttributeValue($escape, $value = $attributeDefaults[$key]->value)) { $value = e($value); } return $value; } /** * Get all of the raw attributes. * * @return array */ public function getAttributes() { return $this->attributes; } /** * Set the underlying attributes. * * @param array $attributes * @return void */ public function setAttributes(array $attributes) { if (isset($attributes['attributes']) && $attributes['attributes'] instanceof self) { $parentBag = $attributes['attributes']; unset($attributes['attributes']); $attributes = $parentBag->merge($attributes, $escape = false)->getAttributes(); } $this->attributes = $attributes; } /** * Get content as a string of HTML. * * @return string */ public function toHtml() { return (string) $this; } /** * Merge additional attributes / values into the attribute bag. * * @param array $attributeDefaults * @return \Illuminate\Support\HtmlString */ public function __invoke(array $attributeDefaults = []) { return new HtmlString((string) $this->merge($attributeDefaults)); } /** * Determine if the given offset exists. * * @param string $offset * @return bool */ public function offsetExists($offset) { return isset($this->attributes[$offset]); } /** * Get the value at the given offset. * * @param string $offset * @return mixed */ public function offsetGet($offset) { return $this->get($offset); } /** * Set the value at a given offset. * * @param string $offset * @param mixed $value * @return void */ public function offsetSet($offset, $value) { $this->attributes[$offset] = $value; } /** * Remove the value at the given offset. * * @param string $offset * @return void */ public function offsetUnset($offset) { unset($this->attributes[$offset]); } /** * Get an iterator for the items. * * @return \ArrayIterator */ public function getIterator() { return new ArrayIterator($this->attributes); } /** * Implode the attributes into a single HTML ready string. * * @return string */ public function __toString() { $string = ''; foreach ($this->attributes as $key => $value) { if ($value === false || is_null($value)) { continue; } if ($value === true) { $value = $key; } $string .= ' '.$key.'="'.str_replace('"', '\\"', trim($value)).'"'; } return trim($string); } } Concerns/ManagesComponents.php000064400000007664150145553620012473 0ustar00componentStack[] = $view; $this->componentData[$this->currentComponent()] = $data; $this->slots[$this->currentComponent()] = []; } } /** * Get the first view that actually exists from the given list, and start a component. * * @param array $names * @param array $data * @return void */ public function startComponentFirst(array $names, array $data = []) { $name = Arr::first($names, function ($item) { return $this->exists($item); }); $this->startComponent($name, $data); } /** * Render the current component. * * @return string */ public function renderComponent() { $view = array_pop($this->componentStack); $data = $this->componentData(); if ($view instanceof Closure) { $view = $view($data); } if ($view instanceof View) { return $view->with($data)->render(); } elseif ($view instanceof Htmlable) { return $view->toHtml(); } else { return $this->make($view, $data)->render(); } } /** * Get the data for the given component. * * @return array */ protected function componentData() { $defaultSlot = new HtmlString(trim(ob_get_clean())); $slots = array_merge([ '__default' => $defaultSlot, ], $this->slots[count($this->componentStack)]); return array_merge( $this->componentData[count($this->componentStack)], ['slot' => $defaultSlot], $this->slots[count($this->componentStack)], ['__laravel_slots' => $slots] ); } /** * Start the slot rendering process. * * @param string $name * @param string|null $content * @return void */ public function slot($name, $content = null) { if (func_num_args() > 2) { throw new InvalidArgumentException('You passed too many arguments to the ['.$name.'] slot.'); } elseif (func_num_args() === 2) { $this->slots[$this->currentComponent()][$name] = $content; } elseif (ob_start()) { $this->slots[$this->currentComponent()][$name] = ''; $this->slotStack[$this->currentComponent()][] = $name; } } /** * Save the slot content for rendering. * * @return void */ public function endSlot() { last($this->componentStack); $currentSlot = array_pop( $this->slotStack[$this->currentComponent()] ); $this->slots[$this->currentComponent()] [$currentSlot] = new HtmlString(trim(ob_get_clean())); } /** * Get the index for the current component. * * @return int */ protected function currentComponent() { return count($this->componentStack) - 1; } } Concerns/ManagesEvents.php000064400000011644150145553620011603 0ustar00addViewEvent($view, $callback, 'creating: '); } return $creators; } /** * Register multiple view composers via an array. * * @param array $composers * @return array */ public function composers(array $composers) { $registered = []; foreach ($composers as $callback => $views) { $registered = array_merge($registered, $this->composer($views, $callback)); } return $registered; } /** * Register a view composer event. * * @param array|string $views * @param \Closure|string $callback * @return array */ public function composer($views, $callback) { $composers = []; foreach ((array) $views as $view) { $composers[] = $this->addViewEvent($view, $callback, 'composing: '); } return $composers; } /** * Add an event for a given view. * * @param string $view * @param \Closure|string $callback * @param string $prefix * @return \Closure|null */ protected function addViewEvent($view, $callback, $prefix = 'composing: ') { $view = $this->normalizeName($view); if ($callback instanceof Closure) { $this->addEventListener($prefix.$view, $callback); return $callback; } elseif (is_string($callback)) { return $this->addClassEvent($view, $callback, $prefix); } } /** * Register a class based view composer. * * @param string $view * @param string $class * @param string $prefix * @return \Closure */ protected function addClassEvent($view, $class, $prefix) { $name = $prefix.$view; // When registering a class based view "composer", we will simply resolve the // classes from the application IoC container then call the compose method // on the instance. This allows for convenient, testable view composers. $callback = $this->buildClassEventCallback( $class, $prefix ); $this->addEventListener($name, $callback); return $callback; } /** * Build a class based container callback Closure. * * @param string $class * @param string $prefix * @return \Closure */ protected function buildClassEventCallback($class, $prefix) { [$class, $method] = $this->parseClassEvent($class, $prefix); // Once we have the class and method name, we can build the Closure to resolve // the instance out of the IoC container and call the method on it with the // given arguments that are passed to the Closure as the composer's data. return function () use ($class, $method) { return call_user_func_array( [$this->container->make($class), $method], func_get_args() ); }; } /** * Parse a class based composer name. * * @param string $class * @param string $prefix * @return array */ protected function parseClassEvent($class, $prefix) { return Str::parseCallback($class, $this->classEventMethodForPrefix($prefix)); } /** * Determine the class event method based on the given prefix. * * @param string $prefix * @return string */ protected function classEventMethodForPrefix($prefix) { return Str::contains($prefix, 'composing') ? 'compose' : 'create'; } /** * Add a listener to the event dispatcher. * * @param string $name * @param \Closure $callback * @return void */ protected function addEventListener($name, $callback) { if (Str::contains($name, '*')) { $callback = function ($name, array $data) use ($callback) { return $callback($data[0]); }; } $this->events->listen($name, $callback); } /** * Call the composer for a given view. * * @param \Illuminate\Contracts\View\View $view * @return void */ public function callComposer(ViewContract $view) { $this->events->dispatch('composing: '.$view->name(), [$view]); } /** * Call the creator for a given view. * * @param \Illuminate\Contracts\View\View $view * @return void */ public function callCreator(ViewContract $view) { $this->events->dispatch('creating: '.$view->name(), [$view]); } } Concerns/ManagesLayouts.php000064400000012350150145553620011772 0ustar00sectionStack[] = $section; } } else { $this->extendSection($section, $content instanceof View ? $content : e($content)); } } /** * Inject inline content into a section. * * @param string $section * @param string $content * @return void */ public function inject($section, $content) { $this->startSection($section, $content); } /** * Stop injecting content into a section and return its contents. * * @return string */ public function yieldSection() { if (empty($this->sectionStack)) { return ''; } return $this->yieldContent($this->stopSection()); } /** * Stop injecting content into a section. * * @param bool $overwrite * @return string * * @throws \InvalidArgumentException */ public function stopSection($overwrite = false) { if (empty($this->sectionStack)) { throw new InvalidArgumentException('Cannot end a section without first starting one.'); } $last = array_pop($this->sectionStack); if ($overwrite) { $this->sections[$last] = ob_get_clean(); } else { $this->extendSection($last, ob_get_clean()); } return $last; } /** * Stop injecting content into a section and append it. * * @return string * * @throws \InvalidArgumentException */ public function appendSection() { if (empty($this->sectionStack)) { throw new InvalidArgumentException('Cannot end a section without first starting one.'); } $last = array_pop($this->sectionStack); if (isset($this->sections[$last])) { $this->sections[$last] .= ob_get_clean(); } else { $this->sections[$last] = ob_get_clean(); } return $last; } /** * Append content to a given section. * * @param string $section * @param string $content * @return void */ protected function extendSection($section, $content) { if (isset($this->sections[$section])) { $content = str_replace(static::parentPlaceholder($section), $content, $this->sections[$section]); } $this->sections[$section] = $content; } /** * Get the string contents of a section. * * @param string $section * @param string $default * @return string */ public function yieldContent($section, $default = '') { $sectionContent = $default instanceof View ? $default : e($default); if (isset($this->sections[$section])) { $sectionContent = $this->sections[$section]; } $sectionContent = str_replace('@@parent', '--parent--holder--', $sectionContent); return str_replace( '--parent--holder--', '@parent', str_replace(static::parentPlaceholder($section), '', $sectionContent) ); } /** * Get the parent placeholder for the current request. * * @param string $section * @return string */ public static function parentPlaceholder($section = '') { if (! isset(static::$parentPlaceholder[$section])) { static::$parentPlaceholder[$section] = '##parent-placeholder-'.sha1($section).'##'; } return static::$parentPlaceholder[$section]; } /** * Check if section exists. * * @param string $name * @return bool */ public function hasSection($name) { return array_key_exists($name, $this->sections); } /** * Check if section does not exist. * * @param string $name * @return bool */ public function sectionMissing($name) { return ! $this->hasSection($name); } /** * Get the contents of a section. * * @param string $name * @param string|null $default * @return mixed */ public function getSection($name, $default = null) { return $this->getSections()[$name] ?? $default; } /** * Get the entire array of sections. * * @return array */ public function getSections() { return $this->sections; } /** * Flush all of the sections. * * @return void */ public function flushSections() { $this->sections = []; $this->sectionStack = []; } } Concerns/ManagesLoops.php000064400000004300150145553620011422 0ustar00loopsStack); $this->loopsStack[] = [ 'iteration' => 0, 'index' => 0, 'remaining' => $length ?? null, 'count' => $length, 'first' => true, 'last' => isset($length) ? $length == 1 : null, 'odd' => false, 'even' => true, 'depth' => count($this->loopsStack) + 1, 'parent' => $parent ? (object) $parent : null, ]; } /** * Increment the top loop's indices. * * @return void */ public function incrementLoopIndices() { $loop = $this->loopsStack[$index = count($this->loopsStack) - 1]; $this->loopsStack[$index] = array_merge($this->loopsStack[$index], [ 'iteration' => $loop['iteration'] + 1, 'index' => $loop['iteration'], 'first' => $loop['iteration'] == 0, 'odd' => ! $loop['odd'], 'even' => ! $loop['even'], 'remaining' => isset($loop['count']) ? $loop['remaining'] - 1 : null, 'last' => isset($loop['count']) ? $loop['iteration'] == $loop['count'] - 1 : null, ]); } /** * Pop a loop from the top of the loop stack. * * @return void */ public function popLoop() { array_pop($this->loopsStack); } /** * Get an instance of the last loop in the stack. * * @return \stdClass|null */ public function getLastLoop() { if ($last = Arr::last($this->loopsStack)) { return (object) $last; } } /** * Get the entire loop stack. * * @return array */ public function getLoopStack() { return $this->loopsStack; } } Concerns/ManagesStacks.php000064400000010333150145553620011561 0ustar00pushStack[] = $section; } } else { $this->extendPush($section, $content); } } /** * Stop injecting content into a push section. * * @return string * * @throws \InvalidArgumentException */ public function stopPush() { if (empty($this->pushStack)) { throw new InvalidArgumentException('Cannot end a push stack without first starting one.'); } return tap(array_pop($this->pushStack), function ($last) { $this->extendPush($last, ob_get_clean()); }); } /** * Append content to a given push section. * * @param string $section * @param string $content * @return void */ protected function extendPush($section, $content) { if (! isset($this->pushes[$section])) { $this->pushes[$section] = []; } if (! isset($this->pushes[$section][$this->renderCount])) { $this->pushes[$section][$this->renderCount] = $content; } else { $this->pushes[$section][$this->renderCount] .= $content; } } /** * Start prepending content into a push section. * * @param string $section * @param string $content * @return void */ public function startPrepend($section, $content = '') { if ($content === '') { if (ob_start()) { $this->pushStack[] = $section; } } else { $this->extendPrepend($section, $content); } } /** * Stop prepending content into a push section. * * @return string * * @throws \InvalidArgumentException */ public function stopPrepend() { if (empty($this->pushStack)) { throw new InvalidArgumentException('Cannot end a prepend operation without first starting one.'); } return tap(array_pop($this->pushStack), function ($last) { $this->extendPrepend($last, ob_get_clean()); }); } /** * Prepend content to a given stack. * * @param string $section * @param string $content * @return void */ protected function extendPrepend($section, $content) { if (! isset($this->prepends[$section])) { $this->prepends[$section] = []; } if (! isset($this->prepends[$section][$this->renderCount])) { $this->prepends[$section][$this->renderCount] = $content; } else { $this->prepends[$section][$this->renderCount] = $content.$this->prepends[$section][$this->renderCount]; } } /** * Get the string contents of a push section. * * @param string $section * @param string $default * @return string */ public function yieldPushContent($section, $default = '') { if (! isset($this->pushes[$section]) && ! isset($this->prepends[$section])) { return $default; } $output = ''; if (isset($this->prepends[$section])) { $output .= implode(array_reverse($this->prepends[$section])); } if (isset($this->pushes[$section])) { $output .= implode($this->pushes[$section]); } return $output; } /** * Flush all of the stacks. * * @return void */ public function flushStacks() { $this->pushes = []; $this->prepends = []; $this->pushStack = []; } } Concerns/ManagesTranslations.php000064400000001373150145553620013016 0ustar00translationReplacements = $replacements; } /** * Render the current translation. * * @return string */ public function renderTranslation() { return $this->container->make('translator')->get( trim(ob_get_clean()), $this->translationReplacements ); } } DynamicComponent.php000064400000011135150145553620010533 0ustar00component = $component; } /** * Get the view / contents that represent the component. * * @return \Illuminate\Contracts\View\View|string */ public function render() { $template = <<<'EOF' getAttributes())->mapWithKeys(function ($value, $key) { return [Illuminate\Support\Str::camel(str_replace([':', '.'], ' ', $key)) => $value]; })->all(), EXTR_SKIP); ?> {{ props }} {{ slots }} {{ defaultSlot }} EOF; return function ($data) use ($template) { $bindings = $this->bindings($class = $this->classForComponent()); return str_replace( [ '{{ component }}', '{{ props }}', '{{ bindings }}', '{{ attributes }}', '{{ slots }}', '{{ defaultSlot }}', ], [ $this->component, $this->compileProps($bindings), $this->compileBindings($bindings), class_exists($class) ? '{{ $attributes }}' : '', $this->compileSlots($data['__laravel_slots']), '{{ $slot ?? "" }}', ], $template ); }; } /** * Compile the @props directive for the component. * * @param array $bindings * @return string */ protected function compileProps(array $bindings) { if (empty($bindings)) { return ''; } return '@props('.'[\''.implode('\',\'', collect($bindings)->map(function ($dataKey) { return Str::camel($dataKey); })->all()).'\']'.')'; } /** * Compile the bindings for the component. * * @param array $bindings * @return string */ protected function compileBindings(array $bindings) { return collect($bindings)->map(function ($key) { return ':'.$key.'="$'.Str::camel(str_replace([':', '.'], ' ', $key)).'"'; })->implode(' '); } /** * Compile the slots for the component. * * @param array $slots * @return string */ protected function compileSlots(array $slots) { return collect($slots)->map(function ($slot, $name) { return $name === '__default' ? null : '{{ $'.$name.' }}'; })->filter()->implode(PHP_EOL); } /** * Get the class for the current component. * * @return string */ protected function classForComponent() { if (isset(static::$componentClasses[$this->component])) { return static::$componentClasses[$this->component]; } return static::$componentClasses[$this->component] = $this->compiler()->componentClass($this->component); } /** * Get the names of the variables that should be bound to the component. * * @param string $class * @return array */ protected function bindings(string $class) { [$data, $attributes] = $this->compiler()->partitionDataAndAttributes($class, $this->attributes->getAttributes()); return array_keys($data->all()); } /** * Get an instance of the Blade tag compiler. * * @return \Illuminate\View\Compilers\ComponentTagCompiler */ protected function compiler() { if (! static::$compiler) { static::$compiler = new ComponentTagCompiler( Container::getInstance()->make('blade.compiler')->getClassComponentAliases(), Container::getInstance()->make('blade.compiler')->getClassComponentNamespaces(), Container::getInstance()->make('blade.compiler') ); } return static::$compiler; } } Engines/CompilerEngine.php000075500000005323150145553620011561 0ustar00compiler = $compiler; } /** * Get the evaluated contents of the view. * * @param string $path * @param array $data * @return string */ public function get($path, array $data = []) { $this->lastCompiled[] = $path; // If this given view has expired, which means it has simply been edited since // it was last compiled, we will re-compile the views so we can evaluate a // fresh copy of the view. We'll pass the compiler the path of the view. if ($this->compiler->isExpired($path)) { $this->compiler->compile($path); } // Once we have the path to the compiled file, we will evaluate the paths with // typical PHP just like any other templates. We also keep a stack of views // which have been rendered for right exception messages to be generated. $results = $this->evaluatePath($this->compiler->getCompiledPath($path), $data); array_pop($this->lastCompiled); return $results; } /** * Handle a view exception. * * @param \Throwable $e * @param int $obLevel * @return void * * @throws \Throwable */ protected function handleViewException(Throwable $e, $obLevel) { $e = new ErrorException($this->getMessage($e), 0, 1, $e->getFile(), $e->getLine(), $e); parent::handleViewException($e, $obLevel); } /** * Get the exception message for an exception. * * @param \Throwable $e * @return string */ protected function getMessage(Throwable $e) { return $e->getMessage().' (View: '.realpath(last($this->lastCompiled)).')'; } /** * Get the compiler implementation. * * @return \Illuminate\View\Compilers\CompilerInterface */ public function getCompiler() { return $this->compiler; } } Engines/Engine.php000075500000000552150145553620010065 0ustar00lastRendered; } } Engines/EngineResolver.php000075500000002406150145553620011607 0ustar00resolved[$engine]); $this->resolvers[$engine] = $resolver; } /** * Resolve an engine instance by name. * * @param string $engine * @return \Illuminate\Contracts\View\Engine * * @throws \InvalidArgumentException */ public function resolve($engine) { if (isset($this->resolved[$engine])) { return $this->resolved[$engine]; } if (isset($this->resolvers[$engine])) { return $this->resolved[$engine] = call_user_func($this->resolvers[$engine]); } throw new InvalidArgumentException("Engine [{$engine}] not found."); } } Engines/FileEngine.php000064400000001403150145553620010656 0ustar00files = $files; } /** * Get the evaluated contents of the view. * * @param string $path * @param array $data * @return string */ public function get($path, array $data = []) { return $this->files->get($path); } } Engines/PhpEngine.php000075500000003531150145553620010535 0ustar00files = $files; } /** * Get the evaluated contents of the view. * * @param string $path * @param array $data * @return string */ public function get($path, array $data = []) { return $this->evaluatePath($path, $data); } /** * Get the evaluated contents of the view at the given path. * * @param string $path * @param array $data * @return string */ protected function evaluatePath($path, $data) { $obLevel = ob_get_level(); ob_start(); // We'll evaluate the contents of the view inside a try/catch block so we can // flush out any stray output that might get out before an error occurs or // an exception is thrown. This prevents any partial views from leaking. try { $this->files->getRequire($path, $data); } catch (Throwable $e) { $this->handleViewException($e, $obLevel); } return ltrim(ob_get_clean()); } /** * Handle a view exception. * * @param \Throwable $e * @param int $obLevel * @return void * * @throws \Throwable */ protected function handleViewException(Throwable $e, $obLevel) { while (ob_get_level() > $obLevel) { ob_end_clean(); } throw $e; } } Factory.php000075500000034140150145553620006677 0ustar00 'blade', 'php' => 'php', 'css' => 'file', 'html' => 'file', ]; /** * The view composer events. * * @var array */ protected $composers = []; /** * The number of active rendering operations. * * @var int */ protected $renderCount = 0; /** * The "once" block IDs that have been rendered. * * @var array */ protected $renderedOnce = []; /** * Create a new view factory instance. * * @param \Illuminate\View\Engines\EngineResolver $engines * @param \Illuminate\View\ViewFinderInterface $finder * @param \Illuminate\Contracts\Events\Dispatcher $events * @return void */ public function __construct(EngineResolver $engines, ViewFinderInterface $finder, Dispatcher $events) { $this->finder = $finder; $this->events = $events; $this->engines = $engines; $this->share('__env', $this); } /** * Get the evaluated view contents for the given view. * * @param string $path * @param \Illuminate\Contracts\Support\Arrayable|array $data * @param array $mergeData * @return \Illuminate\Contracts\View\View */ public function file($path, $data = [], $mergeData = []) { $data = array_merge($mergeData, $this->parseData($data)); return tap($this->viewInstance($path, $path, $data), function ($view) { $this->callCreator($view); }); } /** * Get the evaluated view contents for the given view. * * @param string $view * @param \Illuminate\Contracts\Support\Arrayable|array $data * @param array $mergeData * @return \Illuminate\Contracts\View\View */ public function make($view, $data = [], $mergeData = []) { $path = $this->finder->find( $view = $this->normalizeName($view) ); // Next, we will create the view instance and call the view creator for the view // which can set any data, etc. Then we will return the view instance back to // the caller for rendering or performing other view manipulations on this. $data = array_merge($mergeData, $this->parseData($data)); return tap($this->viewInstance($view, $path, $data), function ($view) { $this->callCreator($view); }); } /** * Get the first view that actually exists from the given list. * * @param array $views * @param \Illuminate\Contracts\Support\Arrayable|array $data * @param array $mergeData * @return \Illuminate\Contracts\View\View * * @throws \InvalidArgumentException */ public function first(array $views, $data = [], $mergeData = []) { $view = Arr::first($views, function ($view) { return $this->exists($view); }); if (! $view) { throw new InvalidArgumentException('None of the views in the given array exist.'); } return $this->make($view, $data, $mergeData); } /** * Get the rendered content of the view based on a given condition. * * @param bool $condition * @param string $view * @param \Illuminate\Contracts\Support\Arrayable|array $data * @param array $mergeData * @return string */ public function renderWhen($condition, $view, $data = [], $mergeData = []) { if (! $condition) { return ''; } return $this->make($view, $this->parseData($data), $mergeData)->render(); } /** * Get the rendered contents of a partial from a loop. * * @param string $view * @param array $data * @param string $iterator * @param string $empty * @return string */ public function renderEach($view, $data, $iterator, $empty = 'raw|') { $result = ''; // If is actually data in the array, we will loop through the data and append // an instance of the partial view to the final result HTML passing in the // iterated value of this data array, allowing the views to access them. if (count($data) > 0) { foreach ($data as $key => $value) { $result .= $this->make( $view, ['key' => $key, $iterator => $value] )->render(); } } // If there is no data in the array, we will render the contents of the empty // view. Alternatively, the "empty view" could be a raw string that begins // with "raw|" for convenience and to let this know that it is a string. else { $result = Str::startsWith($empty, 'raw|') ? substr($empty, 4) : $this->make($empty)->render(); } return $result; } /** * Normalize a view name. * * @param string $name * @return string */ protected function normalizeName($name) { return ViewName::normalize($name); } /** * Parse the given data into a raw array. * * @param mixed $data * @return array */ protected function parseData($data) { return $data instanceof Arrayable ? $data->toArray() : $data; } /** * Create a new view instance from the given arguments. * * @param string $view * @param string $path * @param \Illuminate\Contracts\Support\Arrayable|array $data * @return \Illuminate\Contracts\View\View */ protected function viewInstance($view, $path, $data) { return new View($this, $this->getEngineFromPath($path), $view, $path, $data); } /** * Determine if a given view exists. * * @param string $view * @return bool */ public function exists($view) { try { $this->finder->find($view); } catch (InvalidArgumentException $e) { return false; } return true; } /** * Get the appropriate view engine for the given path. * * @param string $path * @return \Illuminate\Contracts\View\Engine * * @throws \InvalidArgumentException */ public function getEngineFromPath($path) { if (! $extension = $this->getExtension($path)) { throw new InvalidArgumentException("Unrecognized extension in file: {$path}."); } $engine = $this->extensions[$extension]; return $this->engines->resolve($engine); } /** * Get the extension used by the view file. * * @param string $path * @return string|null */ protected function getExtension($path) { $extensions = array_keys($this->extensions); return Arr::first($extensions, function ($value) use ($path) { return Str::endsWith($path, '.'.$value); }); } /** * Add a piece of shared data to the environment. * * @param array|string $key * @param mixed|null $value * @return mixed */ public function share($key, $value = null) { $keys = is_array($key) ? $key : [$key => $value]; foreach ($keys as $key => $value) { $this->shared[$key] = $value; } return $value; } /** * Increment the rendering counter. * * @return void */ public function incrementRender() { $this->renderCount++; } /** * Decrement the rendering counter. * * @return void */ public function decrementRender() { $this->renderCount--; } /** * Check if there are no active render operations. * * @return bool */ public function doneRendering() { return $this->renderCount == 0; } /** * Determine if the given once token has been rendered. * * @param string $id * @return bool */ public function hasRenderedOnce(string $id) { return isset($this->renderedOnce[$id]); } /** * Mark the given once token as having been rendered. * * @param string $id * @return void */ public function markAsRenderedOnce(string $id) { $this->renderedOnce[$id] = true; } /** * Add a location to the array of view locations. * * @param string $location * @return void */ public function addLocation($location) { $this->finder->addLocation($location); } /** * Add a new namespace to the loader. * * @param string $namespace * @param string|array $hints * @return $this */ public function addNamespace($namespace, $hints) { $this->finder->addNamespace($namespace, $hints); return $this; } /** * Prepend a new namespace to the loader. * * @param string $namespace * @param string|array $hints * @return $this */ public function prependNamespace($namespace, $hints) { $this->finder->prependNamespace($namespace, $hints); return $this; } /** * Replace the namespace hints for the given namespace. * * @param string $namespace * @param string|array $hints * @return $this */ public function replaceNamespace($namespace, $hints) { $this->finder->replaceNamespace($namespace, $hints); return $this; } /** * Register a valid view extension and its engine. * * @param string $extension * @param string $engine * @param \Closure|null $resolver * @return void */ public function addExtension($extension, $engine, $resolver = null) { $this->finder->addExtension($extension); if (isset($resolver)) { $this->engines->register($engine, $resolver); } unset($this->extensions[$extension]); $this->extensions = array_merge([$extension => $engine], $this->extensions); } /** * Flush all of the factory state like sections and stacks. * * @return void */ public function flushState() { $this->renderCount = 0; $this->renderedOnce = []; $this->flushSections(); $this->flushStacks(); } /** * Flush all of the section contents if done rendering. * * @return void */ public function flushStateIfDoneRendering() { if ($this->doneRendering()) { $this->flushState(); } } /** * Get the extension to engine bindings. * * @return array */ public function getExtensions() { return $this->extensions; } /** * Get the engine resolver instance. * * @return \Illuminate\View\Engines\EngineResolver */ public function getEngineResolver() { return $this->engines; } /** * Get the view finder instance. * * @return \Illuminate\View\ViewFinderInterface */ public function getFinder() { return $this->finder; } /** * Set the view finder instance. * * @param \Illuminate\View\ViewFinderInterface $finder * @return void */ public function setFinder(ViewFinderInterface $finder) { $this->finder = $finder; } /** * Flush the cache of views located by the finder. * * @return void */ public function flushFinderCache() { $this->getFinder()->flush(); } /** * Get the event dispatcher instance. * * @return \Illuminate\Contracts\Events\Dispatcher */ public function getDispatcher() { return $this->events; } /** * Set the event dispatcher instance. * * @param \Illuminate\Contracts\Events\Dispatcher $events * @return void */ public function setDispatcher(Dispatcher $events) { $this->events = $events; } /** * Get the IoC container instance. * * @return \Illuminate\Contracts\Container\Container */ public function getContainer() { return $this->container; } /** * Set the IoC container instance. * * @param \Illuminate\Contracts\Container\Container $container * @return void */ public function setContainer(Container $container) { $this->container = $container; } /** * Get an item from the shared data. * * @param string $key * @param mixed $default * @return mixed */ public function shared($key, $default = null) { return Arr::get($this->shared, $key, $default); } /** * Get all of the shared data for the environment. * * @return array */ public function getShared() { return $this->shared; } } FileViewFinder.php000075500000016211150145553620010131 0ustar00files = $files; $this->paths = array_map([$this, 'resolvePath'], $paths); if (isset($extensions)) { $this->extensions = $extensions; } } /** * Get the fully qualified location of the view. * * @param string $name * @return string */ public function find($name) { if (isset($this->views[$name])) { return $this->views[$name]; } if ($this->hasHintInformation($name = trim($name))) { return $this->views[$name] = $this->findNamespacedView($name); } return $this->views[$name] = $this->findInPaths($name, $this->paths); } /** * Get the path to a template with a named path. * * @param string $name * @return string */ protected function findNamespacedView($name) { [$namespace, $view] = $this->parseNamespaceSegments($name); return $this->findInPaths($view, $this->hints[$namespace]); } /** * Get the segments of a template with a named path. * * @param string $name * @return array * * @throws \InvalidArgumentException */ protected function parseNamespaceSegments($name) { $segments = explode(static::HINT_PATH_DELIMITER, $name); if (count($segments) !== 2) { throw new InvalidArgumentException("View [{$name}] has an invalid name."); } if (! isset($this->hints[$segments[0]])) { throw new InvalidArgumentException("No hint path defined for [{$segments[0]}]."); } return $segments; } /** * Find the given view in the list of paths. * * @param string $name * @param array $paths * @return string * * @throws \InvalidArgumentException */ protected function findInPaths($name, $paths) { foreach ((array) $paths as $path) { foreach ($this->getPossibleViewFiles($name) as $file) { if ($this->files->exists($viewPath = $path.'/'.$file)) { return $viewPath; } } } throw new InvalidArgumentException("View [{$name}] not found."); } /** * Get an array of possible view files. * * @param string $name * @return array */ protected function getPossibleViewFiles($name) { return array_map(function ($extension) use ($name) { return str_replace('.', '/', $name).'.'.$extension; }, $this->extensions); } /** * Add a location to the finder. * * @param string $location * @return void */ public function addLocation($location) { $this->paths[] = $this->resolvePath($location); } /** * Prepend a location to the finder. * * @param string $location * @return void */ public function prependLocation($location) { array_unshift($this->paths, $this->resolvePath($location)); } /** * Resolve the path. * * @param string $path * @return string */ protected function resolvePath($path) { return realpath($path) ?: $path; } /** * Add a namespace hint to the finder. * * @param string $namespace * @param string|array $hints * @return void */ public function addNamespace($namespace, $hints) { $hints = (array) $hints; if (isset($this->hints[$namespace])) { $hints = array_merge($this->hints[$namespace], $hints); } $this->hints[$namespace] = $hints; } /** * Prepend a namespace hint to the finder. * * @param string $namespace * @param string|array $hints * @return void */ public function prependNamespace($namespace, $hints) { $hints = (array) $hints; if (isset($this->hints[$namespace])) { $hints = array_merge($hints, $this->hints[$namespace]); } $this->hints[$namespace] = $hints; } /** * Replace the namespace hints for the given namespace. * * @param string $namespace * @param string|array $hints * @return void */ public function replaceNamespace($namespace, $hints) { $this->hints[$namespace] = (array) $hints; } /** * Register an extension with the view finder. * * @param string $extension * @return void */ public function addExtension($extension) { if (($index = array_search($extension, $this->extensions)) !== false) { unset($this->extensions[$index]); } array_unshift($this->extensions, $extension); } /** * Returns whether or not the view name has any hint information. * * @param string $name * @return bool */ public function hasHintInformation($name) { return strpos($name, static::HINT_PATH_DELIMITER) > 0; } /** * Flush the cache of located views. * * @return void */ public function flush() { $this->views = []; } /** * Get the filesystem instance. * * @return \Illuminate\Filesystem\Filesystem */ public function getFilesystem() { return $this->files; } /** * Set the active view paths. * * @param array $paths * @return $this */ public function setPaths($paths) { $this->paths = $paths; return $this; } /** * Get the active view paths. * * @return array */ public function getPaths() { return $this->paths; } /** * Get the views that have been located. * * @return array */ public function getViews() { return $this->views; } /** * Get the namespace to file path hints. * * @return array */ public function getHints() { return $this->hints; } /** * Get registered extensions. * * @return array */ public function getExtensions() { return $this->extensions; } } InvokableComponentVariable.php000064400000003713150145553620012532 0ustar00callable = $callable; } /** * Resolve the displayable value that the class is deferring. * * @return \Illuminate\Contracts\Support\Htmlable|string */ public function resolveDisplayableValue() { return $this->__invoke(); } /** * Get an interator instance for the variable. * * @return \ArrayIterator */ public function getIterator() { $result = $this->__invoke(); return new ArrayIterator($result instanceof Enumerable ? $result->all() : $result); } /** * Dynamically proxy attribute access to the variable. * * @param string $key * @return mixed */ public function __get($key) { return $this->__invoke()->{$key}; } /** * Dynamically proxy method access to the variable. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { return $this->__invoke()->{$method}(...$parameters); } /** * Resolve the variable. * * @return mixed */ public function __invoke() { return call_user_func($this->callable); } /** * Resolve the variable as a string. * * @return mixed */ public function __toString() { return (string) $this->__invoke(); } } LICENSE.md000064400000002063150145553620006157 0ustar00The MIT License (MIT) Copyright (c) Taylor Otwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Middleware/ShareErrorsFromSession.php000064400000002622150145553620013771 0ustar00view = $view; } /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { // If the current session has an "errors" variable bound to it, we will share // its value with all view instances so the views can easily access errors // without having to bind. An empty bag is set when there aren't errors. $this->view->share( 'errors', $request->session()->get('errors') ?: new ViewErrorBag ); // Putting the errors in the view for every view allows the developer to just // assume that some errors are always available, which is convenient since // they don't have to continually run checks for the presence of errors. return $next($request); } } View.php000075500000023011150145553620006175 0ustar00view = $view; $this->path = $path; $this->engine = $engine; $this->factory = $factory; $this->data = $data instanceof Arrayable ? $data->toArray() : (array) $data; } /** * Get the string contents of the view. * * @param callable|null $callback * @return array|string * * @throws \Throwable */ public function render(callable $callback = null) { try { $contents = $this->renderContents(); $response = isset($callback) ? $callback($this, $contents) : null; // Once we have the contents of the view, we will flush the sections if we are // done rendering all views so that there is nothing left hanging over when // another view gets rendered in the future by the application developer. $this->factory->flushStateIfDoneRendering(); return ! is_null($response) ? $response : $contents; } catch (Throwable $e) { $this->factory->flushState(); throw $e; } } /** * Get the contents of the view instance. * * @return string */ protected function renderContents() { // We will keep track of the amount of views being rendered so we can flush // the section after the complete rendering operation is done. This will // clear out the sections for any separate views that may be rendered. $this->factory->incrementRender(); $this->factory->callComposer($this); $contents = $this->getContents(); // Once we've finished rendering the view, we'll decrement the render count // so that each sections get flushed out next time a view is created and // no old sections are staying around in the memory of an environment. $this->factory->decrementRender(); return $contents; } /** * Get the evaluated contents of the view. * * @return string */ protected function getContents() { return $this->engine->get($this->path, $this->gatherData()); } /** * Get the data bound to the view instance. * * @return array */ public function gatherData() { $data = array_merge($this->factory->getShared(), $this->data); foreach ($data as $key => $value) { if ($value instanceof Renderable) { $data[$key] = $value->render(); } } return $data; } /** * Get the sections of the rendered view. * * @return array * * @throws \Throwable */ public function renderSections() { return $this->render(function () { return $this->factory->getSections(); }); } /** * Add a piece of data to the view. * * @param string|array $key * @param mixed $value * @return $this */ public function with($key, $value = null) { if (is_array($key)) { $this->data = array_merge($this->data, $key); } else { $this->data[$key] = $value; } return $this; } /** * Add a view instance to the view data. * * @param string $key * @param string $view * @param array $data * @return $this */ public function nest($key, $view, array $data = []) { return $this->with($key, $this->factory->make($view, $data)); } /** * Add validation errors to the view. * * @param \Illuminate\Contracts\Support\MessageProvider|array $provider * @param string $bag * @return $this */ public function withErrors($provider, $bag = 'default') { return $this->with('errors', (new ViewErrorBag)->put( $bag, $this->formatErrors($provider) )); } /** * Parse the given errors into an appropriate value. * * @param \Illuminate\Contracts\Support\MessageProvider|array|string $provider * @return \Illuminate\Support\MessageBag */ protected function formatErrors($provider) { return $provider instanceof MessageProvider ? $provider->getMessageBag() : new MessageBag((array) $provider); } /** * Get the name of the view. * * @return string */ public function name() { return $this->getName(); } /** * Get the name of the view. * * @return string */ public function getName() { return $this->view; } /** * Get the array of view data. * * @return array */ public function getData() { return $this->data; } /** * Get the path to the view file. * * @return string */ public function getPath() { return $this->path; } /** * Set the path to the view. * * @param string $path * @return void */ public function setPath($path) { $this->path = $path; } /** * Get the view factory instance. * * @return \Illuminate\View\Factory */ public function getFactory() { return $this->factory; } /** * Get the view's rendering engine. * * @return \Illuminate\Contracts\View\Engine */ public function getEngine() { return $this->engine; } /** * Determine if a piece of data is bound. * * @param string $key * @return bool */ public function offsetExists($key) { return array_key_exists($key, $this->data); } /** * Get a piece of bound data to the view. * * @param string $key * @return mixed */ public function offsetGet($key) { return $this->data[$key]; } /** * Set a piece of data on the view. * * @param string $key * @param mixed $value * @return void */ public function offsetSet($key, $value) { $this->with($key, $value); } /** * Unset a piece of data from the view. * * @param string $key * @return void */ public function offsetUnset($key) { unset($this->data[$key]); } /** * Get a piece of data from the view. * * @param string $key * @return mixed */ public function &__get($key) { return $this->data[$key]; } /** * Set a piece of data on the view. * * @param string $key * @param mixed $value * @return void */ public function __set($key, $value) { $this->with($key, $value); } /** * Check if a piece of data is bound to the view. * * @param string $key * @return bool */ public function __isset($key) { return isset($this->data[$key]); } /** * Remove a piece of bound data from the view. * * @param string $key * @return void */ public function __unset($key) { unset($this->data[$key]); } /** * Dynamically bind parameters to the view. * * @param string $method * @param array $parameters * @return \Illuminate\View\View * * @throws \BadMethodCallException */ public function __call($method, $parameters) { if (static::hasMacro($method)) { return $this->macroCall($method, $parameters); } if (! Str::startsWith($method, 'with')) { throw new BadMethodCallException(sprintf( 'Method %s::%s does not exist.', static::class, $method )); } return $this->with(Str::camel(substr($method, 4)), $parameters[0]); } /** * Get content as a string of HTML. * * @return string */ public function toHtml() { return $this->render(); } /** * Get the string contents of the view. * * @return string * * @throws \Throwable */ public function __toString() { return $this->render(); } } ViewFinderInterface.php000075500000002653150145553620011157 0ustar00registerFactory(); $this->registerViewFinder(); $this->registerBladeCompiler(); $this->registerEngineResolver(); } /** * Register the view environment. * * @return void */ public function registerFactory() { $this->app->singleton('view', function ($app) { // Next we need to grab the engine resolver instance that will be used by the // environment. The resolver will be used by an environment to get each of // the various engine implementations such as plain PHP or Blade engine. $resolver = $app['view.engine.resolver']; $finder = $app['view.finder']; $factory = $this->createFactory($resolver, $finder, $app['events']); // We will also set the container instance on this view environment since the // view composers may be classes registered in the container, which allows // for great testable, flexible composers for the application developer. $factory->setContainer($app); $factory->share('app', $app); return $factory; }); } /** * Create a new Factory Instance. * * @param \Illuminate\View\Engines\EngineResolver $resolver * @param \Illuminate\View\ViewFinderInterface $finder * @param \Illuminate\Contracts\Events\Dispatcher $events * @return \Illuminate\View\Factory */ protected function createFactory($resolver, $finder, $events) { return new Factory($resolver, $finder, $events); } /** * Register the view finder implementation. * * @return void */ public function registerViewFinder() { $this->app->bind('view.finder', function ($app) { return new FileViewFinder($app['files'], $app['config']['view.paths']); }); } /** * Register the Blade compiler implementation. * * @return void */ public function registerBladeCompiler() { $this->app->singleton('blade.compiler', function ($app) { return tap(new BladeCompiler($app['files'], $app['config']['view.compiled']), function ($blade) { $blade->component('dynamic-component', DynamicComponent::class); }); }); } /** * Register the engine resolver instance. * * @return void */ public function registerEngineResolver() { $this->app->singleton('view.engine.resolver', function () { $resolver = new EngineResolver; // Next, we will register the various view engines with the resolver so that the // environment will resolve the engines needed for various views based on the // extension of view file. We call a method for each of the view's engines. foreach (['file', 'php', 'blade'] as $engine) { $this->{'register'.ucfirst($engine).'Engine'}($resolver); } return $resolver; }); } /** * Register the file engine implementation. * * @param \Illuminate\View\Engines\EngineResolver $resolver * @return void */ public function registerFileEngine($resolver) { $resolver->register('file', function () { return new FileEngine($this->app['files']); }); } /** * Register the PHP engine implementation. * * @param \Illuminate\View\Engines\EngineResolver $resolver * @return void */ public function registerPhpEngine($resolver) { $resolver->register('php', function () { return new PhpEngine($this->app['files']); }); } /** * Register the Blade engine implementation. * * @param \Illuminate\View\Engines\EngineResolver $resolver * @return void */ public function registerBladeEngine($resolver) { $resolver->register('blade', function () { return new CompilerEngine($this->app['blade.compiler'], $this->app['files']); }); } } composer.json000064400000002012150145553620007267 0ustar00{ "name": "illuminate/view", "description": "The Illuminate View package.", "license": "MIT", "homepage": "https://laravel.com", "support": { "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, "authors": [ { "name": "Taylor Otwell", "email": "taylor@laravel.com" } ], "require": { "php": "^7.3", "ext-json": "*", "illuminate/collections": "^8.0", "illuminate/container": "^8.0", "illuminate/contracts": "^8.0", "illuminate/events": "^8.0", "illuminate/filesystem": "^8.0", "illuminate/macroable": "^8.0", "illuminate/support": "^8.0" }, "autoload": { "psr-4": { "Illuminate\\View\\": "" } }, "extra": { "branch-alias": { "dev-master": "8.x-dev" } }, "config": { "sort-packages": true }, "minimum-stability": "dev" }