package and depencies
This commit is contained in:
@@ -51,7 +51,7 @@ class AnonymousComponent extends Component
|
||||
$this->attributes = $this->attributes ?: $this->newAttributeBag();
|
||||
|
||||
return array_merge(
|
||||
optional($this->data['attributes'] ?? null)->getAttributes() ?: [],
|
||||
($this->data['attributes'] ?? null)?->getAttributes() ?: [],
|
||||
$this->attributes->getAttributes(),
|
||||
$this->data,
|
||||
['attributes' => $this->attributes]
|
||||
|
||||
@@ -21,6 +21,7 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
Concerns\CompilesConditionals,
|
||||
Concerns\CompilesEchos,
|
||||
Concerns\CompilesErrors,
|
||||
Concerns\CompilesFragments,
|
||||
Concerns\CompilesHelpers,
|
||||
Concerns\CompilesIncludes,
|
||||
Concerns\CompilesInjections,
|
||||
@@ -122,6 +123,20 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
*/
|
||||
protected $rawBlocks = [];
|
||||
|
||||
/**
|
||||
* The array of anonymous component paths to search for components in.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $anonymousComponentPaths = [];
|
||||
|
||||
/**
|
||||
* The array of anonymous component namespaces to autoload from.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $anonymousComponentNamespaces = [];
|
||||
|
||||
/**
|
||||
* The array of class component aliases and their class names.
|
||||
*
|
||||
@@ -241,7 +256,7 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
);
|
||||
|
||||
foreach ($this->precompilers as $precompiler) {
|
||||
$value = call_user_func($precompiler, $value);
|
||||
$value = $precompiler($value);
|
||||
}
|
||||
|
||||
// Here we will loop through all of the tokens returned by the Zend lexer and
|
||||
@@ -340,11 +355,11 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
*/
|
||||
protected function storeUncompiledBlocks($value)
|
||||
{
|
||||
if (strpos($value, '@verbatim') !== false) {
|
||||
if (str_contains($value, '@verbatim')) {
|
||||
$value = $this->storeVerbatimBlocks($value);
|
||||
}
|
||||
|
||||
if (strpos($value, '@php') !== false) {
|
||||
if (str_contains($value, '@php')) {
|
||||
$value = $this->storePhpBlocks($value);
|
||||
}
|
||||
|
||||
@@ -504,12 +519,14 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
*/
|
||||
protected function compileStatement($match)
|
||||
{
|
||||
if (Str::contains($match[1], '@')) {
|
||||
if (str_contains($match[1], '@')) {
|
||||
$match[0] = isset($match[3]) ? $match[1].$match[3] : $match[1];
|
||||
} elseif (isset($this->customDirectives[$match[1]])) {
|
||||
$match[0] = $this->callCustomDirective($match[1], Arr::get($match, 3));
|
||||
} elseif (method_exists($this, $method = 'compile'.ucfirst($match[1]))) {
|
||||
$match[0] = $this->$method(Arr::get($match, 3));
|
||||
} else {
|
||||
return $match[0];
|
||||
}
|
||||
|
||||
return isset($match[3]) ? $match[0] : $match[0].$match[2];
|
||||
@@ -524,9 +541,9 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
*/
|
||||
protected function callCustomDirective($name, $value)
|
||||
{
|
||||
$value = $value ?? '';
|
||||
$value ??= '';
|
||||
|
||||
if (Str::startsWith($value, '(') && Str::endsWith($value, ')')) {
|
||||
if (str_starts_with($value, '(') && str_ends_with($value, ')')) {
|
||||
$value = Str::substr($value, 1, -1);
|
||||
}
|
||||
|
||||
@@ -625,12 +642,12 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
*/
|
||||
public function component($class, $alias = null, $prefix = '')
|
||||
{
|
||||
if (! is_null($alias) && Str::contains($alias, '\\')) {
|
||||
if (! is_null($alias) && str_contains($alias, '\\')) {
|
||||
[$class, $alias] = [$alias, $class];
|
||||
}
|
||||
|
||||
if (is_null($alias)) {
|
||||
$alias = Str::contains($class, '\\View\\Components\\')
|
||||
$alias = str_contains($class, '\\View\\Components\\')
|
||||
? collect(explode('\\', Str::after($class, '\\View\\Components\\')))->map(function ($segment) {
|
||||
return Str::kebab($segment);
|
||||
})->implode(':')
|
||||
@@ -672,6 +689,45 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
return $this->classComponentAliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new anonymous component path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string|null $prefix
|
||||
* @return void
|
||||
*/
|
||||
public function anonymousComponentPath(string $path, string $prefix = null)
|
||||
{
|
||||
$prefixHash = md5($prefix ?: $path);
|
||||
|
||||
$this->anonymousComponentPaths[] = [
|
||||
'path' => $path,
|
||||
'prefix' => $prefix,
|
||||
'prefixHash' => $prefixHash,
|
||||
];
|
||||
|
||||
Container::getInstance()
|
||||
->make(ViewFactory::class)
|
||||
->addNamespace($prefixHash, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an anonymous component namespace.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param string|null $prefix
|
||||
* @return void
|
||||
*/
|
||||
public function anonymousComponentNamespace(string $directory, string $prefix = null)
|
||||
{
|
||||
$prefix ??= $directory;
|
||||
|
||||
$this->anonymousComponentNamespaces[$prefix] = Str::of($directory)
|
||||
->replace('/', '.')
|
||||
->trim('. ')
|
||||
->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a class-based component namespace.
|
||||
*
|
||||
@@ -684,6 +740,26 @@ class BladeCompiler extends Compiler implements CompilerInterface
|
||||
$this->classComponentNamespaces[$prefix] = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered anonymous component paths.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAnonymousComponentPaths()
|
||||
{
|
||||
return $this->anonymousComponentPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered anonymous component namespaces.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAnonymousComponentNamespaces()
|
||||
{
|
||||
return $this->anonymousComponentNamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered class component namespaces.
|
||||
*
|
||||
|
||||
@@ -3,34 +3,64 @@
|
||||
namespace Illuminate\View\Compilers;
|
||||
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
|
||||
abstract class Compiler
|
||||
{
|
||||
/**
|
||||
* The Filesystem instance.
|
||||
* The filesystem instance.
|
||||
*
|
||||
* @var \Illuminate\Filesystem\Filesystem
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Get the cache path for the compiled views.
|
||||
* The cache path for the compiled views.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cachePath;
|
||||
|
||||
/**
|
||||
* The base path that should be removed from paths before hashing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $basePath;
|
||||
|
||||
/**
|
||||
* Determines if compiled views should be cached.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $shouldCache;
|
||||
|
||||
/**
|
||||
* The compiled view file extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $compiledExtension = 'php';
|
||||
|
||||
/**
|
||||
* Create a new compiler instance.
|
||||
*
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @param string $cachePath
|
||||
* @param string $basePath
|
||||
* @param bool $shouldCache
|
||||
* @param string $compiledExtension
|
||||
* @return void
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(Filesystem $files, $cachePath)
|
||||
public function __construct(
|
||||
Filesystem $files,
|
||||
$cachePath,
|
||||
$basePath = '',
|
||||
$shouldCache = true,
|
||||
$compiledExtension = 'php')
|
||||
{
|
||||
if (! $cachePath) {
|
||||
throw new InvalidArgumentException('Please provide a valid cache path.');
|
||||
@@ -38,6 +68,9 @@ abstract class Compiler
|
||||
|
||||
$this->files = $files;
|
||||
$this->cachePath = $cachePath;
|
||||
$this->basePath = $basePath;
|
||||
$this->shouldCache = $shouldCache;
|
||||
$this->compiledExtension = $compiledExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +81,7 @@ abstract class Compiler
|
||||
*/
|
||||
public function getCompiledPath($path)
|
||||
{
|
||||
return $this->cachePath.'/'.sha1('v2'.$path).'.php';
|
||||
return $this->cachePath.'/'.sha1('v2'.Str::after($path, $this->basePath)).'.'.$this->compiledExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +92,10 @@ abstract class Compiler
|
||||
*/
|
||||
public function isExpired($path)
|
||||
{
|
||||
if (! $this->shouldCache) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$compiled = $this->getCompiledPath($path);
|
||||
|
||||
// If the compiled file doesn't exist we will indicate that the view is expired
|
||||
|
||||
@@ -111,10 +111,18 @@ class ComponentTagCompiler
|
||||
(?:
|
||||
\s+
|
||||
(?:
|
||||
(?:
|
||||
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
|
||||
)
|
||||
|
|
||||
(?:
|
||||
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
|
||||
)
|
||||
|
|
||||
(?:
|
||||
(\:\\\$)(\w+)
|
||||
)
|
||||
|
|
||||
(?:
|
||||
[\w\-:.@]+
|
||||
(
|
||||
@@ -164,10 +172,18 @@ class ComponentTagCompiler
|
||||
(?:
|
||||
\s+
|
||||
(?:
|
||||
(?:
|
||||
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
|
||||
)
|
||||
|
|
||||
(?:
|
||||
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
|
||||
)
|
||||
|
|
||||
(?:
|
||||
(\:\\\$)(\w+)
|
||||
)
|
||||
|
|
||||
(?:
|
||||
[\w\-:.@]+
|
||||
(
|
||||
@@ -216,12 +232,16 @@ class ComponentTagCompiler
|
||||
return [Str::camel($key) => $value];
|
||||
});
|
||||
|
||||
// If the component doesn't exists as a class we'll assume it's a class-less
|
||||
// If the component doesn't exist 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)) {
|
||||
$view = Str::startsWith($component, 'mail::')
|
||||
? "\$__env->getContainer()->make(Illuminate\\View\\Factory::class)->make('{$component}')"
|
||||
: "'$class'";
|
||||
|
||||
$parameters = [
|
||||
'view' => "'$class'",
|
||||
'view' => $view,
|
||||
'data' => '['.$this->attributesToString($data->all(), $escapeBound = false).']',
|
||||
];
|
||||
|
||||
@@ -231,6 +251,9 @@ class ComponentTagCompiler
|
||||
}
|
||||
|
||||
return "##BEGIN-COMPONENT-CLASS##@component('{$class}', '{$component}', [".$this->attributesToString($parameters, $escapeBound = false).'])
|
||||
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag && $constructor = (new ReflectionClass('.$class.'::class))->getConstructor()): ?>
|
||||
<?php $attributes = $attributes->except(collect($constructor->getParameters())->map->getName()->all()); ?>
|
||||
<?php endif; ?>
|
||||
<?php $component->withAttributes(['.$this->attributesToString($attributes->all(), $escapeAttributes = $class !== DynamicComponent::class).']); ?>';
|
||||
}
|
||||
|
||||
@@ -268,12 +291,13 @@ class ComponentTagCompiler
|
||||
return $class;
|
||||
}
|
||||
|
||||
if ($viewFactory->exists($view = $this->guessViewName($component))) {
|
||||
return $view;
|
||||
if (! is_null($guess = $this->guessAnonymousComponentUsingNamespaces($viewFactory, $component)) ||
|
||||
! is_null($guess = $this->guessAnonymousComponentUsingPaths($viewFactory, $component))) {
|
||||
return $guess;
|
||||
}
|
||||
|
||||
if ($viewFactory->exists($view = $this->guessViewName($component).'.index')) {
|
||||
return $view;
|
||||
if (Str::startsWith($component, 'mail::')) {
|
||||
return $component;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(
|
||||
@@ -281,6 +305,72 @@ class ComponentTagCompiler
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find an anonymous component using the registered anonymous component paths.
|
||||
*
|
||||
* @param \Illuminate\Contracts\View\Factory $viewFactory
|
||||
* @param string $component
|
||||
* @return string|null
|
||||
*/
|
||||
protected function guessAnonymousComponentUsingPaths(Factory $viewFactory, string $component)
|
||||
{
|
||||
$delimiter = ViewFinderInterface::HINT_PATH_DELIMITER;
|
||||
|
||||
foreach ($this->blade->getAnonymousComponentPaths() as $path) {
|
||||
try {
|
||||
if (str_contains($component, $delimiter) &&
|
||||
! str_starts_with($component, $path['prefix'].$delimiter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$formattedComponent = str_starts_with($component, $path['prefix'].$delimiter)
|
||||
? Str::after($component, $delimiter)
|
||||
: $component;
|
||||
|
||||
if (! is_null($guess = match (true) {
|
||||
$viewFactory->exists($guess = $path['prefixHash'].$delimiter.$formattedComponent) => $guess,
|
||||
$viewFactory->exists($guess = $path['prefixHash'].$delimiter.$formattedComponent.'.index') => $guess,
|
||||
default => null,
|
||||
})) {
|
||||
return $guess;
|
||||
}
|
||||
} catch (InvalidArgumentException $e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find an anonymous component using the registered anonymous component namespaces.
|
||||
*
|
||||
* @param \Illuminate\Contracts\View\Factory $viewFactory
|
||||
* @param string $component
|
||||
* @return string|null
|
||||
*/
|
||||
protected function guessAnonymousComponentUsingNamespaces(Factory $viewFactory, string $component)
|
||||
{
|
||||
return collect($this->blade->getAnonymousComponentNamespaces())
|
||||
->filter(function ($directory, $prefix) use ($component) {
|
||||
return Str::startsWith($component, $prefix.'::');
|
||||
})
|
||||
->prepend('components', $component)
|
||||
->reduce(function ($carry, $directory, $prefix) use ($component, $viewFactory) {
|
||||
if (! is_null($carry)) {
|
||||
return $carry;
|
||||
}
|
||||
|
||||
$componentName = Str::after($component, $prefix.'::');
|
||||
|
||||
if ($viewFactory->exists($view = $this->guessViewName($componentName, $directory))) {
|
||||
return $view;
|
||||
}
|
||||
|
||||
if ($viewFactory->exists($view = $this->guessViewName($componentName, $directory).'.index')) {
|
||||
return $view;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the class for the given component using the registered namespaces.
|
||||
*
|
||||
@@ -293,7 +383,7 @@ class ComponentTagCompiler
|
||||
|
||||
$prefix = $segments[0];
|
||||
|
||||
if (! isset($this->namespaces[$prefix]) || ! isset($segments[1])) {
|
||||
if (! isset($this->namespaces[$prefix], $segments[1])) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,15 +428,18 @@ class ComponentTagCompiler
|
||||
* Guess the view name for the given component.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $prefix
|
||||
* @return string
|
||||
*/
|
||||
public function guessViewName($name)
|
||||
public function guessViewName($name, $prefix = 'components.')
|
||||
{
|
||||
$prefix = 'components.';
|
||||
if (! Str::endsWith($prefix, '.')) {
|
||||
$prefix .= '.';
|
||||
}
|
||||
|
||||
$delimiter = ViewFinderInterface::HINT_PATH_DELIMITER;
|
||||
|
||||
if (Str::contains($name, $delimiter)) {
|
||||
if (str_contains($name, $delimiter)) {
|
||||
return Str::replaceFirst($delimiter, $delimiter.$prefix, $name);
|
||||
}
|
||||
|
||||
@@ -362,7 +455,7 @@ class ComponentTagCompiler
|
||||
*/
|
||||
public function partitionDataAndAttributes($class, array $attributes)
|
||||
{
|
||||
// If the class doesn't exists, we'll assume it's a class-less component and
|
||||
// If the class doesn't exist, we'll assume it is 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)) {
|
||||
@@ -403,12 +496,16 @@ class ComponentTagCompiler
|
||||
<
|
||||
\s*
|
||||
x[\-\:]slot
|
||||
\s+
|
||||
(:?)name=(?<name>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+))
|
||||
(?:\:(?<inlineName>\w+(?:-\w+)*))?
|
||||
(?:\s+(:?)name=(?<name>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+)))?
|
||||
(?<attributes>
|
||||
(?:
|
||||
\s+
|
||||
(?:
|
||||
(?:
|
||||
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
|
||||
)
|
||||
|
|
||||
(?:
|
||||
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
|
||||
)
|
||||
@@ -435,9 +532,13 @@ class ComponentTagCompiler
|
||||
/x";
|
||||
|
||||
$value = preg_replace_callback($pattern, function ($matches) {
|
||||
$name = $this->stripQuotes($matches['name']);
|
||||
$name = $this->stripQuotes($matches['inlineName'] ?: $matches['name']);
|
||||
|
||||
if ($matches[1] !== ':') {
|
||||
if (Str::contains($name, '-') && ! empty($matches['inlineName'])) {
|
||||
$name = Str::camel($name);
|
||||
}
|
||||
|
||||
if ($matches[2] !== ':') {
|
||||
$name = "'{$name}'";
|
||||
}
|
||||
|
||||
@@ -459,8 +560,9 @@ class ComponentTagCompiler
|
||||
*/
|
||||
protected function getAttributesFromAttributeString(string $attributeString)
|
||||
{
|
||||
$attributeString = $this->parseShortAttributeSyntax($attributeString);
|
||||
$attributeString = $this->parseAttributeBag($attributeString);
|
||||
|
||||
$attributeString = $this->parseComponentTagClassStatements($attributeString);
|
||||
$attributeString = $this->parseBindAttributes($attributeString);
|
||||
|
||||
$pattern = '/
|
||||
@@ -495,7 +597,7 @@ class ComponentTagCompiler
|
||||
|
||||
$value = $this->stripQuotes($value);
|
||||
|
||||
if (Str::startsWith($attribute, 'bind:')) {
|
||||
if (str_starts_with($attribute, 'bind:')) {
|
||||
$attribute = Str::after($attribute, 'bind:');
|
||||
|
||||
$this->boundAttributes[$attribute] = true;
|
||||
@@ -503,7 +605,7 @@ class ComponentTagCompiler
|
||||
$value = "'".$this->compileAttributeEchos($value)."'";
|
||||
}
|
||||
|
||||
if (Str::startsWith($attribute, '::')) {
|
||||
if (str_starts_with($attribute, '::')) {
|
||||
$attribute = substr($attribute, 1);
|
||||
}
|
||||
|
||||
@@ -511,6 +613,21 @@ class ComponentTagCompiler
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a short attribute syntax like :$foo into a fully-qualified syntax like :foo="$foo".
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function parseShortAttributeSyntax(string $value)
|
||||
{
|
||||
$pattern = "/\s\:\\\$(\w+)/x";
|
||||
|
||||
return preg_replace_callback($pattern, function (array $matches) {
|
||||
return " :{$matches[1]}=\"\${$matches[1]}\"";
|
||||
}, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the attribute bag in a given attribute string into its fully-qualified syntax.
|
||||
*
|
||||
@@ -527,6 +644,27 @@ class ComponentTagCompiler
|
||||
return preg_replace($pattern, ' :attributes="$1"', $attributeString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse @class statements in a given attribute string into their fully-qualified syntax.
|
||||
*
|
||||
* @param string $attributeString
|
||||
* @return string
|
||||
*/
|
||||
protected function parseComponentTagClassStatements(string $attributeString)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/@(class)(\( ( (?>[^()]+) | (?2) )* \))/x', function ($match) {
|
||||
if ($match[1] === 'class') {
|
||||
$match[2] = str_replace('"', "'", $match[2]);
|
||||
|
||||
return ":class=\"\Illuminate\Support\Arr::toCssClasses{$match[2]}\"";
|
||||
}
|
||||
|
||||
return $match[0];
|
||||
}, $attributeString
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the "bind" attributes in a given attribute string into their fully-qualified syntax.
|
||||
*
|
||||
|
||||
@@ -23,7 +23,7 @@ trait CompilesComponents
|
||||
*/
|
||||
protected function compileComponent($expression)
|
||||
{
|
||||
[$component, $alias, $data] = strpos($expression, ',') !== false
|
||||
[$component, $alias, $data] = str_contains($expression, ',')
|
||||
? array_map('trim', explode(',', trim($expression, '()'), 3)) + ['', '', '']
|
||||
: [trim($expression, '()'), '', ''];
|
||||
|
||||
@@ -64,7 +64,7 @@ trait CompilesComponents
|
||||
{
|
||||
return implode("\n", [
|
||||
'<?php if (isset($component)) { $__componentOriginal'.$hash.' = $component; } ?>',
|
||||
'<?php $component = $__env->getContainer()->make('.Str::finish($component, '::class').', '.($data ?: '[]').'); ?>',
|
||||
'<?php $component = '.$component.'::resolve('.($data ?: '[]').' + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? (array) $attributes->getIterator() : [])); ?>',
|
||||
'<?php $component->withName('.$alias.'); ?>',
|
||||
'<?php if ($component->shouldRender()): ?>',
|
||||
'<?php $__env->startComponent($component->resolveView(), $component->data()); ?>',
|
||||
@@ -149,7 +149,11 @@ trait CompilesComponents
|
||||
*/
|
||||
protected function compileProps($expression)
|
||||
{
|
||||
return "<?php \$attributes = \$attributes->exceptProps{$expression}; ?>
|
||||
return "<?php \$attributes ??= new \\Illuminate\\View\\ComponentAttributeBag; ?>
|
||||
<?php foreach(\$attributes->onlyProps{$expression} as \$__key => \$__value) {
|
||||
\$\$__key = \$\$__key ?? \$__value;
|
||||
} ?>
|
||||
<?php \$attributes = \$attributes->exceptProps{$expression}; ?>
|
||||
<?php foreach (array_filter({$expression}, 'is_string', ARRAY_FILTER_USE_KEY) as \$__key => \$__value) {
|
||||
\$\$__key = \$\$__key ?? \$__value;
|
||||
} ?>
|
||||
@@ -182,7 +186,7 @@ trait CompilesComponents
|
||||
*/
|
||||
public static function sanitizeComponentAttribute($value)
|
||||
{
|
||||
if (is_object($value) && $value instanceof CanBeEscapedWhenCastToString) {
|
||||
if ($value instanceof CanBeEscapedWhenCastToString) {
|
||||
return $value->escapeWhenCastingToString();
|
||||
}
|
||||
|
||||
|
||||
@@ -304,4 +304,82 @@ trait CompilesConditionals
|
||||
{
|
||||
return '<?php endif; ?>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a selected block into valid PHP.
|
||||
*
|
||||
* @param string $condition
|
||||
* @return string
|
||||
*/
|
||||
protected function compileSelected($condition)
|
||||
{
|
||||
return "<?php if{$condition}: echo 'selected'; endif; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a checked block into valid PHP.
|
||||
*
|
||||
* @param string $condition
|
||||
* @return string
|
||||
*/
|
||||
protected function compileChecked($condition)
|
||||
{
|
||||
return "<?php if{$condition}: echo 'checked'; endif; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a disabled block into valid PHP.
|
||||
*
|
||||
* @param string $condition
|
||||
* @return string
|
||||
*/
|
||||
protected function compileDisabled($condition)
|
||||
{
|
||||
return "<?php if{$condition}: echo 'disabled'; endif; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a required block into valid PHP.
|
||||
*
|
||||
* @param string $condition
|
||||
* @return string
|
||||
*/
|
||||
protected function compileRequired($condition)
|
||||
{
|
||||
return "<?php if{$condition}: echo 'required'; endif; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a readonly block into valid PHP.
|
||||
*
|
||||
* @param string $condition
|
||||
* @return string
|
||||
*/
|
||||
protected function compileReadonly($condition)
|
||||
{
|
||||
return "<?php if{$condition}: echo 'readonly'; endif; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the push statements into valid PHP.
|
||||
*
|
||||
* @param string $expression
|
||||
* @return string
|
||||
*/
|
||||
protected function compilePushIf($expression)
|
||||
{
|
||||
$parts = explode(',', $this->stripParentheses($expression), 2);
|
||||
|
||||
return "<?php if({$parts[0]}): \$__env->startPush({$parts[1]}); ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the end-push statements into valid PHP.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function compileEndPushIf()
|
||||
{
|
||||
return '<?php $__env->stopPush(); endif; ?>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ trait CompilesEchos
|
||||
{
|
||||
$value = Str::of($value)
|
||||
->trim()
|
||||
->when(Str::endsWith($value, ';'), function ($str) {
|
||||
->when(str_ends_with($value, ';'), function ($str) {
|
||||
return $str->beforeLast(';');
|
||||
});
|
||||
|
||||
|
||||
36
vendor/laravel/framework/src/Illuminate/View/Compilers/Concerns/CompilesFragments.php
vendored
Normal file
36
vendor/laravel/framework/src/Illuminate/View/Compilers/Concerns/CompilesFragments.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\View\Compilers\Concerns;
|
||||
|
||||
trait CompilesFragments
|
||||
{
|
||||
/**
|
||||
* The last compiled fragment.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $lastFragment;
|
||||
|
||||
/**
|
||||
* Compile the fragment statements into valid PHP.
|
||||
*
|
||||
* @param string $expression
|
||||
* @return string
|
||||
*/
|
||||
protected function compileFragment($expression)
|
||||
{
|
||||
$this->lastFragment = trim($expression, "()'\" ");
|
||||
|
||||
return "<?php \$__env->startFragment{$expression}; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the end-fragment statements into valid PHP.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function compileEndfragment()
|
||||
{
|
||||
return '<?php echo $__env->stopFragment(); ?>';
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Illuminate\View\Compilers\Concerns;
|
||||
|
||||
use Illuminate\Foundation\Vite;
|
||||
|
||||
trait CompilesHelpers
|
||||
{
|
||||
/**
|
||||
@@ -46,4 +48,31 @@ trait CompilesHelpers
|
||||
{
|
||||
return "<?php echo method_field{$method}; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the "vite" statements into valid PHP.
|
||||
*
|
||||
* @param ?string $arguments
|
||||
* @return string
|
||||
*/
|
||||
protected function compileVite($arguments)
|
||||
{
|
||||
$arguments ??= '()';
|
||||
|
||||
$class = Vite::class;
|
||||
|
||||
return "<?php echo app('$class'){$arguments}; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the "viteReactRefresh" statements into valid PHP.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function compileViteReactRefresh()
|
||||
{
|
||||
$class = Vite::class;
|
||||
|
||||
return "<?php echo app('$class')->reactRefresh(); ?>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Illuminate\View\Compilers\Concerns;
|
||||
|
||||
use Illuminate\Contracts\View\ViewCompilationException;
|
||||
|
||||
trait CompilesLoops
|
||||
{
|
||||
/**
|
||||
@@ -16,12 +18,18 @@ trait CompilesLoops
|
||||
*
|
||||
* @param string $expression
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\View\ViewCompilationException
|
||||
*/
|
||||
protected function compileForelse($expression)
|
||||
{
|
||||
$empty = '$__empty_'.++$this->forElseCounter;
|
||||
|
||||
preg_match('/\( *(.*) +as *(.*)\)$/is', $expression, $matches);
|
||||
preg_match('/\( *(.+) +as +(.+)\)$/is', $expression ?? '', $matches);
|
||||
|
||||
if (count($matches) === 0) {
|
||||
throw new ViewCompilationException('Malformed @forelse statement.');
|
||||
}
|
||||
|
||||
$iteratee = trim($matches[1]);
|
||||
|
||||
@@ -87,10 +95,16 @@ trait CompilesLoops
|
||||
*
|
||||
* @param string $expression
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\View\ViewCompilationException
|
||||
*/
|
||||
protected function compileForeach($expression)
|
||||
{
|
||||
preg_match('/\( *(.*) +as *(.*)\)$/is', $expression, $matches);
|
||||
preg_match('/\( *(.+) +as +(.*)\)$/is', $expression ?? '', $matches);
|
||||
|
||||
if (count($matches) === 0) {
|
||||
throw new ViewCompilationException('Malformed @foreach statement.');
|
||||
}
|
||||
|
||||
$iteratee = trim($matches[1]);
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Illuminate\View\Compilers\Concerns;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait CompilesStacks
|
||||
{
|
||||
/**
|
||||
@@ -26,6 +28,24 @@ trait CompilesStacks
|
||||
return "<?php \$__env->startPush{$expression}; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the push-once statements into valid PHP.
|
||||
*
|
||||
* @param string $expression
|
||||
* @return string
|
||||
*/
|
||||
protected function compilePushOnce($expression)
|
||||
{
|
||||
$parts = explode(',', $this->stripParentheses($expression), 2);
|
||||
|
||||
[$stack, $id] = [$parts[0], $parts[1] ?? ''];
|
||||
|
||||
$id = trim($id) ?: "'".(string) Str::uuid()."'";
|
||||
|
||||
return '<?php if (! $__env->hasRenderedOnce('.$id.')): $__env->markAsRenderedOnce('.$id.');
|
||||
$__env->startPush('.$stack.'); ?>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the end-push statements into valid PHP.
|
||||
*
|
||||
@@ -36,6 +56,16 @@ trait CompilesStacks
|
||||
return '<?php $__env->stopPush(); ?>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the end-push-once statements into valid PHP.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function compileEndpushOnce()
|
||||
{
|
||||
return '<?php $__env->stopPush(); endif; ?>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the prepend statements into valid PHP.
|
||||
*
|
||||
@@ -47,6 +77,24 @@ trait CompilesStacks
|
||||
return "<?php \$__env->startPrepend{$expression}; ?>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the prepend-once statements into valid PHP.
|
||||
*
|
||||
* @param string $expression
|
||||
* @return string
|
||||
*/
|
||||
protected function compilePrependOnce($expression)
|
||||
{
|
||||
$parts = explode(',', $this->stripParentheses($expression), 2);
|
||||
|
||||
[$stack, $id] = [$parts[0], $parts[1] ?? ''];
|
||||
|
||||
$id = trim($id) ?: "'".(string) Str::uuid()."'";
|
||||
|
||||
return '<?php if (! $__env->hasRenderedOnce('.$id.')): $__env->markAsRenderedOnce('.$id.');
|
||||
$__env->startPrepend('.$stack.'); ?>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the end-prepend statements into valid PHP.
|
||||
*
|
||||
@@ -56,4 +104,14 @@ trait CompilesStacks
|
||||
{
|
||||
return '<?php $__env->stopPrepend(); ?>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the end-prepend-once statements into valid PHP.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function compileEndprependOnce()
|
||||
{
|
||||
return '<?php $__env->stopPrepend(); endif; ?>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,27 +6,12 @@ use Closure;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Contracts\View\View as ViewContract;
|
||||
use Illuminate\Support\Str;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
abstract class Component
|
||||
{
|
||||
/**
|
||||
* The cache of public property names, keyed by class.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $propertyCache = [];
|
||||
|
||||
/**
|
||||
* The cache of public method names, keyed by class.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $methodCache = [];
|
||||
|
||||
/**
|
||||
* The properties / methods that should not be exposed to the component.
|
||||
*
|
||||
@@ -48,6 +33,48 @@ abstract class Component
|
||||
*/
|
||||
public $attributes;
|
||||
|
||||
/**
|
||||
* The view factory instance, if any.
|
||||
*
|
||||
* @var \Illuminate\Contracts\View\Factory|null
|
||||
*/
|
||||
protected static $factory;
|
||||
|
||||
/**
|
||||
* The component resolver callback.
|
||||
*
|
||||
* @var (\Closure(string, array): Component)|null
|
||||
*/
|
||||
protected static $componentsResolver;
|
||||
|
||||
/**
|
||||
* The cache of blade view names, keyed by contents.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected static $bladeViewCache = [];
|
||||
|
||||
/**
|
||||
* The cache of public property names, keyed by class.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $propertyCache = [];
|
||||
|
||||
/**
|
||||
* The cache of public method names, keyed by class.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $methodCache = [];
|
||||
|
||||
/**
|
||||
* The cache of constructor parameters, keyed by class.
|
||||
*
|
||||
* @var array<class-string, array<int, string>>
|
||||
*/
|
||||
protected static $constructorParametersCache = [];
|
||||
|
||||
/**
|
||||
* Get the view / view contents that represent the component.
|
||||
*
|
||||
@@ -55,6 +82,49 @@ abstract class Component
|
||||
*/
|
||||
abstract public function render();
|
||||
|
||||
/**
|
||||
* Resolve the component instance with the given data.
|
||||
*
|
||||
* @param array $data
|
||||
* @return static
|
||||
*/
|
||||
public static function resolve($data)
|
||||
{
|
||||
if (static::$componentsResolver) {
|
||||
return call_user_func(static::$componentsResolver, static::class, $data);
|
||||
}
|
||||
|
||||
$parameters = static::extractConstructorParameters();
|
||||
|
||||
$dataKeys = array_keys($data);
|
||||
|
||||
if (empty(array_diff($parameters, $dataKeys))) {
|
||||
return new static(...array_intersect_key($data, array_flip($parameters)));
|
||||
}
|
||||
|
||||
return Container::getInstance()->make(static::class, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the constructor parameters for the component.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function extractConstructorParameters()
|
||||
{
|
||||
if (! isset(static::$constructorParametersCache[static::class])) {
|
||||
$class = new ReflectionClass(static::class);
|
||||
|
||||
$constructor = $class->getConstructor();
|
||||
|
||||
static::$constructorParametersCache[static::class] = $constructor
|
||||
? collect($constructor->getParameters())->map->getName()->all()
|
||||
: [];
|
||||
}
|
||||
|
||||
return static::$constructorParametersCache[static::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the Blade view or view file that should be used when rendering the component.
|
||||
*
|
||||
@@ -73,11 +143,7 @@ abstract class Component
|
||||
}
|
||||
|
||||
$resolver = function ($view) {
|
||||
$factory = Container::getInstance()->make('view');
|
||||
|
||||
return strlen($view) <= PHP_MAXPATHLEN && $factory->exists($view)
|
||||
? $view
|
||||
: $this->createBladeViewFromString($factory, $view);
|
||||
return $this->extractBladeViewFromString($view);
|
||||
};
|
||||
|
||||
return $view instanceof Closure ? function (array $data = []) use ($view, $resolver) {
|
||||
@@ -86,6 +152,27 @@ abstract class Component
|
||||
: $resolver($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Blade view with the raw component string content.
|
||||
*
|
||||
* @param string $contents
|
||||
* @return string
|
||||
*/
|
||||
protected function extractBladeViewFromString($contents)
|
||||
{
|
||||
$key = sprintf('%s::%s', static::class, $contents);
|
||||
|
||||
if (isset(static::$bladeViewCache[$key])) {
|
||||
return static::$bladeViewCache[$key];
|
||||
}
|
||||
|
||||
if (strlen($contents) <= PHP_MAXPATHLEN && $this->factory()->exists($contents)) {
|
||||
return static::$bladeViewCache[$key] = $contents;
|
||||
}
|
||||
|
||||
return static::$bladeViewCache[$key] = $this->createBladeViewFromString($this->factory(), $contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Blade view with the raw component string content.
|
||||
*
|
||||
@@ -223,7 +310,7 @@ abstract class Component
|
||||
*/
|
||||
protected function shouldIgnore($name)
|
||||
{
|
||||
return Str::startsWith($name, '__') ||
|
||||
return str_starts_with($name, '__') ||
|
||||
in_array($name, $this->ignoredMethods());
|
||||
}
|
||||
|
||||
@@ -293,4 +380,79 @@ abstract class Component
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluated view contents for the given view.
|
||||
*
|
||||
* @param string|null $view
|
||||
* @param \Illuminate\Contracts\Support\Arrayable|array $data
|
||||
* @param array $mergeData
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function view($view, $data = [], $mergeData = [])
|
||||
{
|
||||
return $this->factory()->make($view, $data, $mergeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view factory instance.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
protected function factory()
|
||||
{
|
||||
if (is_null(static::$factory)) {
|
||||
static::$factory = Container::getInstance()->make('view');
|
||||
}
|
||||
|
||||
return static::$factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the component's cached state.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function flushCache()
|
||||
{
|
||||
static::$bladeViewCache = [];
|
||||
static::$constructorParametersCache = [];
|
||||
static::$methodCache = [];
|
||||
static::$propertyCache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget the component's factory instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function forgetFactory()
|
||||
{
|
||||
static::$factory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget the component's resolver callback.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function forgetComponentsResolver()
|
||||
{
|
||||
static::$componentsResolver = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback that should be used to resolve components within views.
|
||||
*
|
||||
* @param \Closure(string $component, array $data): Component $resolver
|
||||
* @return void
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function resolveComponentsUsing($resolver)
|
||||
{
|
||||
static::$componentsResolver = $resolver;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Traits\Conditionable;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
{
|
||||
@@ -68,6 +69,17 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
return array_key_exists($key, $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given attribute is missing from the attribute array.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function missing($key)
|
||||
{
|
||||
return ! $this->has($key, $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only include the given attribute from the attribute array.
|
||||
*
|
||||
@@ -154,6 +166,17 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
return $this->whereStartsWith($needles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only include the given attribute from the attribute array.
|
||||
*
|
||||
* @param mixed|array $keys
|
||||
* @return static
|
||||
*/
|
||||
public function onlyProps($keys)
|
||||
{
|
||||
return $this->only($this->extractPropNames($keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude the given attribute from the attribute array.
|
||||
*
|
||||
@@ -161,6 +184,17 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
* @return static
|
||||
*/
|
||||
public function exceptProps($keys)
|
||||
{
|
||||
return $this->except($this->extractPropNames($keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract prop names from given keys.
|
||||
*
|
||||
* @param mixed|array $keys
|
||||
* @return array
|
||||
*/
|
||||
protected function extractPropNames($keys)
|
||||
{
|
||||
$props = [];
|
||||
|
||||
@@ -171,7 +205,7 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
$props[] = Str::kebab($key);
|
||||
}
|
||||
|
||||
return $this->except($props);
|
||||
return $props;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,8 +357,7 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
* @param string $offset
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return isset($this->attributes[$offset]);
|
||||
}
|
||||
@@ -335,8 +368,7 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
* @param string $offset
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
public function offsetGet($offset): mixed
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
@@ -348,8 +380,7 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
$this->attributes[$offset] = $value;
|
||||
}
|
||||
@@ -360,8 +391,7 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
* @param string $offset
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
unset($this->attributes[$offset]);
|
||||
}
|
||||
@@ -371,8 +401,7 @@ class ComponentAttributeBag implements ArrayAccess, Htmlable, IteratorAggregate
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return new ArrayIterator($this->attributes);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ trait ManagesEvents
|
||||
$composers = [];
|
||||
|
||||
foreach ((array) $views as $view) {
|
||||
$composers[] = $this->addViewEvent($view, $callback, 'composing: ');
|
||||
$composers[] = $this->addViewEvent($view, $callback);
|
||||
}
|
||||
|
||||
return $composers;
|
||||
@@ -145,7 +145,7 @@ trait ManagesEvents
|
||||
*/
|
||||
protected function classEventMethodForPrefix($prefix)
|
||||
{
|
||||
return Str::contains($prefix, 'composing') ? 'compose' : 'create';
|
||||
return str_contains($prefix, 'composing') ? 'compose' : 'create';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +157,7 @@ trait ManagesEvents
|
||||
*/
|
||||
protected function addEventListener($name, $callback)
|
||||
{
|
||||
if (Str::contains($name, '*')) {
|
||||
if (str_contains($name, '*')) {
|
||||
$callback = function ($name, array $data) use ($callback) {
|
||||
return $callback($data[0]);
|
||||
};
|
||||
|
||||
88
vendor/laravel/framework/src/Illuminate/View/Concerns/ManagesFragments.php
vendored
Normal file
88
vendor/laravel/framework/src/Illuminate/View/Concerns/ManagesFragments.php
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\View\Concerns;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
trait ManagesFragments
|
||||
{
|
||||
/**
|
||||
* All of the captured, rendered fragments.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fragments = [];
|
||||
|
||||
/**
|
||||
* The stack of in-progress fragment renders.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fragmentStack = [];
|
||||
|
||||
/**
|
||||
* Start injecting content into a fragment.
|
||||
*
|
||||
* @param string $fragment
|
||||
* @return void
|
||||
*/
|
||||
public function startFragment($fragment)
|
||||
{
|
||||
if (ob_start()) {
|
||||
$this->fragmentStack[] = $fragment;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop injecting content into a fragment.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function stopFragment()
|
||||
{
|
||||
if (empty($this->fragmentStack)) {
|
||||
throw new InvalidArgumentException('Cannot end a fragment without first starting one.');
|
||||
}
|
||||
|
||||
$last = array_pop($this->fragmentStack);
|
||||
|
||||
$this->fragments[$last] = ob_get_clean();
|
||||
|
||||
return $this->fragments[$last];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a fragment.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string|null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFragment($name, $default = null)
|
||||
{
|
||||
return $this->getFragments()[$name] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entire array of rendered fragments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFragments()
|
||||
{
|
||||
return $this->fragments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all of the fragments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function flushFragments()
|
||||
{
|
||||
$this->fragments = [];
|
||||
$this->fragmentStack = [];
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Illuminate\View\Concerns;
|
||||
|
||||
use Countable;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
|
||||
trait ManagesLoops
|
||||
{
|
||||
@@ -22,7 +22,9 @@ trait ManagesLoops
|
||||
*/
|
||||
public function addLoop($data)
|
||||
{
|
||||
$length = is_array($data) || $data instanceof Countable ? count($data) : null;
|
||||
$length = is_countable($data) && ! $data instanceof LazyCollection
|
||||
? count($data)
|
||||
: null;
|
||||
|
||||
$parent = Arr::last($this->loopsStack);
|
||||
|
||||
|
||||
@@ -23,6 +23,13 @@ class CompilerEngine extends PhpEngine
|
||||
*/
|
||||
protected $lastCompiled = [];
|
||||
|
||||
/**
|
||||
* The view paths that were compiled or are not expired, keyed by the path.
|
||||
*
|
||||
* @var array<string, true>
|
||||
*/
|
||||
protected $compiledOrNotExpired = [];
|
||||
|
||||
/**
|
||||
* Create a new compiler engine instance.
|
||||
*
|
||||
@@ -51,14 +58,31 @@ class CompilerEngine extends PhpEngine
|
||||
// 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)) {
|
||||
if (! isset($this->compiledOrNotExpired[$path]) && $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);
|
||||
|
||||
try {
|
||||
$results = $this->evaluatePath($this->compiler->getCompiledPath($path), $data);
|
||||
} catch (ViewException $e) {
|
||||
if (! str($e->getMessage())->contains(['No such file or directory', 'File does not exist at path'])) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (! isset($this->compiledOrNotExpired[$path])) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->compiler->compile($path);
|
||||
|
||||
$results = $this->evaluatePath($this->compiler->getCompiledPath($path), $data);
|
||||
}
|
||||
|
||||
$this->compiledOrNotExpired[$path] = true;
|
||||
|
||||
array_pop($this->lastCompiled);
|
||||
|
||||
@@ -101,4 +125,14 @@ class CompilerEngine extends PhpEngine
|
||||
{
|
||||
return $this->compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache of views that were compiled or not expired.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function forgetCompiledOrNotExpired()
|
||||
{
|
||||
$this->compiledOrNotExpired = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Contracts\View\Factory as FactoryContract;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use Illuminate\View\Engines\EngineResolver;
|
||||
use InvalidArgumentException;
|
||||
@@ -17,6 +16,7 @@ class Factory implements FactoryContract
|
||||
use Macroable,
|
||||
Concerns\ManagesComponents,
|
||||
Concerns\ManagesEvents,
|
||||
Concerns\ManagesFragments,
|
||||
Concerns\ManagesLayouts,
|
||||
Concerns\ManagesLoops,
|
||||
Concerns\ManagesStacks,
|
||||
@@ -231,7 +231,7 @@ class Factory implements FactoryContract
|
||||
// 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|')
|
||||
$result = str_starts_with($empty, 'raw|')
|
||||
? substr($empty, 4)
|
||||
: $this->make($empty)->render();
|
||||
}
|
||||
@@ -321,7 +321,7 @@ class Factory implements FactoryContract
|
||||
$extensions = array_keys($this->extensions);
|
||||
|
||||
return Arr::first($extensions, function ($value) use ($path) {
|
||||
return Str::endsWith($path, '.'.$value);
|
||||
return str_ends_with($path, '.'.$value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -482,6 +482,7 @@ class Factory implements FactoryContract
|
||||
$this->flushSections();
|
||||
$this->flushStacks();
|
||||
$this->flushComponents();
|
||||
$this->flushFragments();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -145,9 +145,7 @@ class FileViewFinder implements ViewFinderInterface
|
||||
*/
|
||||
protected function getPossibleViewFiles($name)
|
||||
{
|
||||
return array_map(function ($extension) use ($name) {
|
||||
return str_replace('.', '/', $name).'.'.$extension;
|
||||
}, $this->extensions);
|
||||
return array_map(fn ($extension) => str_replace('.', '/', $name).'.'.$extension, $this->extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ use Closure;
|
||||
use Illuminate\Contracts\Support\DeferringDisplayableValue;
|
||||
use Illuminate\Support\Enumerable;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
class InvokableComponentVariable implements DeferringDisplayableValue, IteratorAggregate
|
||||
{
|
||||
@@ -43,8 +44,7 @@ class InvokableComponentVariable implements DeferringDisplayableValue, IteratorA
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
$result = $this->__invoke();
|
||||
|
||||
|
||||
@@ -77,6 +77,19 @@ class View implements ArrayAccess, Htmlable, ViewContract
|
||||
$this->data = $data instanceof Arrayable ? $data->toArray() : (array) $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the evaluated contents of a given fragment.
|
||||
*
|
||||
* @param string $fragment
|
||||
* @return string
|
||||
*/
|
||||
public function fragment($fragment)
|
||||
{
|
||||
return $this->render(function () use ($fragment) {
|
||||
return $this->factory->getFragment($fragment);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string contents of the view.
|
||||
*
|
||||
@@ -112,7 +125,7 @@ class View implements ArrayAccess, Htmlable, ViewContract
|
||||
*/
|
||||
protected function renderContents()
|
||||
{
|
||||
// We will keep track of the amount of views being rendered so we can flush
|
||||
// We will keep track of the number 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();
|
||||
@@ -122,7 +135,7 @@ class View implements ArrayAccess, Htmlable, ViewContract
|
||||
$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
|
||||
// so that each section gets flushed out next time a view is created and
|
||||
// no old sections are staying around in the memory of an environment.
|
||||
$this->factory->decrementRender();
|
||||
|
||||
@@ -306,8 +319,7 @@ class View implements ArrayAccess, Htmlable, ViewContract
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($key)
|
||||
public function offsetExists($key): bool
|
||||
{
|
||||
return array_key_exists($key, $this->data);
|
||||
}
|
||||
@@ -318,8 +330,7 @@ class View implements ArrayAccess, Htmlable, ViewContract
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
public function offsetGet($key): mixed
|
||||
{
|
||||
return $this->data[$key];
|
||||
}
|
||||
@@ -331,8 +342,7 @@ class View implements ArrayAccess, Htmlable, ViewContract
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($key, $value)
|
||||
public function offsetSet($key, $value): void
|
||||
{
|
||||
$this->with($key, $value);
|
||||
}
|
||||
@@ -343,8 +353,7 @@ class View implements ArrayAccess, Htmlable, ViewContract
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($key)
|
||||
public function offsetUnset($key): void
|
||||
{
|
||||
unset($this->data[$key]);
|
||||
}
|
||||
@@ -409,7 +418,7 @@ class View implements ArrayAccess, Htmlable, ViewContract
|
||||
return $this->macroCall($method, $parameters);
|
||||
}
|
||||
|
||||
if (! Str::startsWith($method, 'with')) {
|
||||
if (! str_starts_with($method, 'with')) {
|
||||
throw new BadMethodCallException(sprintf(
|
||||
'Method %s::%s does not exist.', static::class, $method
|
||||
));
|
||||
|
||||
@@ -28,7 +28,7 @@ class ViewException extends ErrorException
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return \Illuminate\Http\Response|null
|
||||
*/
|
||||
public function render($request)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ class ViewName
|
||||
{
|
||||
$delimiter = ViewFinderInterface::HINT_PATH_DELIMITER;
|
||||
|
||||
if (strpos($name, $delimiter) === false) {
|
||||
if (! str_contains($name, $delimiter)) {
|
||||
return str_replace('/', '.', $name);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ class ViewServiceProvider extends ServiceProvider
|
||||
$this->registerViewFinder();
|
||||
$this->registerBladeCompiler();
|
||||
$this->registerEngineResolver();
|
||||
|
||||
$this->app->terminating(static function () {
|
||||
Component::flushCache();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,6 +52,10 @@ class ViewServiceProvider extends ServiceProvider
|
||||
|
||||
$factory->share('app', $app);
|
||||
|
||||
$app->terminating(static function () {
|
||||
Component::forgetFactory();
|
||||
});
|
||||
|
||||
return $factory;
|
||||
});
|
||||
}
|
||||
@@ -85,7 +93,13 @@ class ViewServiceProvider extends ServiceProvider
|
||||
public function registerBladeCompiler()
|
||||
{
|
||||
$this->app->singleton('blade.compiler', function ($app) {
|
||||
return tap(new BladeCompiler($app['files'], $app['config']['view.compiled']), function ($blade) {
|
||||
return tap(new BladeCompiler(
|
||||
$app['files'],
|
||||
$app['config']['view.compiled'],
|
||||
$app['config']->get('view.relative_hash', false) ? $app->basePath() : '',
|
||||
$app['config']->get('view.cache', true),
|
||||
$app['config']->get('view.compiled_extension', 'php'),
|
||||
), function ($blade) {
|
||||
$blade->component('dynamic-component', DynamicComponent::class);
|
||||
});
|
||||
});
|
||||
@@ -147,7 +161,13 @@ class ViewServiceProvider extends ServiceProvider
|
||||
public function registerBladeEngine($resolver)
|
||||
{
|
||||
$resolver->register('blade', function () {
|
||||
return new CompilerEngine($this->app['blade.compiler'], $this->app['files']);
|
||||
$compiler = new CompilerEngine($this->app['blade.compiler'], $this->app['files']);
|
||||
|
||||
$this->app->terminating(static function () use ($compiler) {
|
||||
$compiler->forgetCompiledOrNotExpired();
|
||||
});
|
||||
|
||||
return $compiler;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"php": "^8.0.2",
|
||||
"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"
|
||||
"illuminate/collections": "^9.0",
|
||||
"illuminate/container": "^9.0",
|
||||
"illuminate/contracts": "^9.0",
|
||||
"illuminate/events": "^9.0",
|
||||
"illuminate/filesystem": "^9.0",
|
||||
"illuminate/macroable": "^9.0",
|
||||
"illuminate/support": "^9.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "8.x-dev"
|
||||
"dev-master": "9.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
|
||||
Reference in New Issue
Block a user