v1.0.0 initial release

This commit is contained in:
samy
2025-06-13 10:48:20 -10:00
commit 0c8f70bca5
3333 changed files with 189946 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+148
View File
@@ -0,0 +1,148 @@
<?php
namespace Illuminate\View\Compilers;
use ErrorException;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use InvalidArgumentException;
abstract class Compiler
{
/**
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;
/**
* 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';
/**
* Indicates if view cache timestamps should be checked.
*
* @var bool
*/
protected $shouldCheckTimestamps;
/**
* Create a new compiler instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @param string $cachePath
* @param string $basePath
* @param bool $shouldCache
* @param bool $shouldCheckTimestamps
* @param string $compiledExtension
*
* @throws \InvalidArgumentException
*/
public function __construct(
Filesystem $files,
$cachePath,
$basePath = '',
$shouldCache = true,
$compiledExtension = 'php',
$shouldCheckTimestamps = true,
) {
if (! $cachePath) {
throw new InvalidArgumentException('Please provide a valid cache path.');
}
$this->files = $files;
$this->cachePath = $cachePath;
$this->basePath = $basePath;
$this->shouldCache = $shouldCache;
$this->compiledExtension = $compiledExtension;
$this->shouldCheckTimestamps = $shouldCheckTimestamps;
}
/**
* Get the path to the compiled version of a view.
*
* @param string $path
* @return string
*/
public function getCompiledPath($path)
{
return $this->cachePath.'/'.hash('xxh128', 'v2'.Str::after($path, $this->basePath)).'.'.$this->compiledExtension;
}
/**
* Determine if the view at the given path is expired.
*
* @param string $path
* @return bool
*
* @throws \ErrorException
*/
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
// 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;
}
if (! $this->shouldCheckTimestamps) {
return false;
}
try {
return $this->files->lastModified($path) >=
$this->files->lastModified($compiled);
} catch (ErrorException $exception) {
if (! $this->files->exists($compiled)) {
return true;
}
throw $exception;
}
}
/**
* Create the compiled file directory if necessary.
*
* @param string $path
* @return void
*/
protected function ensureCompiledDirectoryExists($path)
{
if (! $this->files->exists(dirname($path))) {
$this->files->makeDirectory(dirname($path), 0777, true, true);
}
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace Illuminate\View\Compilers;
interface CompilerInterface
{
/**
* Get the path to the compiled version of a view.
*
* @param string $path
* @return string
*/
public function getCompiledPath($path);
/**
* Determine if the given view is expired.
*
* @param string $path
* @return bool
*/
public function isExpired($path);
/**
* Compile the view at the given path.
*
* @param string $path
* @return void
*/
public function compile($path);
}
@@ -0,0 +1,813 @@
<?php
namespace Illuminate\View\Compilers;
use Illuminate\Container\Container;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Illuminate\View\AnonymousComponent;
use Illuminate\View\DynamicComponent;
use Illuminate\View\ViewFinderInterface;
use InvalidArgumentException;
use ReflectionClass;
/**
* @author Spatie bvba <info@spatie.be>
* @author Taylor Otwell <taylor@laravel.com>
*/
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 a new component tag compiler.
*
* @param array $aliases
* @param array $namespaces
* @param \Illuminate\View\Compilers\BladeCompiler|null $blade
*/
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\-\:\.]*)
(?<attributes>
(?:
\s+
(?:
(?:
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
@(?:style)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
)
|
(?:
(\:\\\$)(\w+)
)
|
(?:
[\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*
(?<attributes>
(?:
\s+
(?:
(?:
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
@(?:style)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
)
|
(?:
(\:\\\$)(\w+)
)
|
(?:
[\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##END-COMPONENT-CLASS##";
}, $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 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' => $view,
'data' => '['.$this->attributesToString($data->all(), $escapeBound = false).']',
];
$class = AnonymousComponent::class;
} else {
$parameters = $data->all();
}
return "##BEGIN-COMPONENT-CLASS##@component('{$class}', '{$component}', [".$this->attributesToString($parameters, $escapeBound = false).'])
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag): ?>
<?php $attributes = $attributes->except(\\'.$class.'::ignoredParameterNames()); ?>
<?php endif; ?>
<?php $component->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 (class_exists($class = $class.'\\'.Str::afterLast($class, '\\'))) {
return $class;
}
if (! is_null($guess = $this->guessAnonymousComponentUsingNamespaces($viewFactory, $component)) ||
! is_null($guess = $this->guessAnonymousComponentUsingPaths($viewFactory, $component))) {
return $guess;
}
if (Str::startsWith($component, 'mail::')) {
return $component;
}
throw new InvalidArgumentException(
"Unable to locate a class or view for component [{$component}]."
);
}
/**
* 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,
$viewFactory->exists($guess = $path['prefixHash'].$delimiter.$formattedComponent.'.'.Str::afterLast($formattedComponent, '.')) => $guess,
default => null,
})) {
return $guess;
}
} catch (InvalidArgumentException) {
//
}
}
}
/**
* 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 (new Collection($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;
}
$lastViewSegment = Str::afterLast(Str::afterLast($componentName, '.'), ':');
if ($viewFactory->exists($view = $this->guessViewName($componentName, $directory).'.'.$lastViewSegment)) {
return $view;
}
});
}
/**
* 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], $segments[1])) {
return;
}
if (class_exists($class = $this->namespaces[$prefix].'\\'.$this->formatClassName($segments[1]))) {
return $class;
}
if (class_exists($class = $class.'\\'.Str::afterLast($class, '\\'))) {
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
* @param string $prefix
* @return string
*/
public function guessViewName($name, $prefix = 'components.')
{
if (! Str::endsWith($prefix, '.')) {
$prefix .= '.';
}
$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 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)) {
return [new Collection($attributes), new Collection($attributes)];
}
$constructor = (new ReflectionClass($class))->getConstructor();
$parameterNames = $constructor
? (new Collection($constructor->getParameters()))->map->getName()->all()
: [];
return (new Collection($attributes))
->partition(fn ($value, $key) => 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##END-COMPONENT-CLASS##', $value);
}
/**
* Compile the slot tags within the given string.
*
* @param string $value
* @return string
*/
public function compileSlots(string $value)
{
$pattern = "/
<
\s*
x[\-\:]slot
(?:\:(?<inlineName>\w+(?:-\w+)*))?
(?:\s+name=(?<name>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+)))?
(?:\s+\:name=(?<boundName>(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+)))?
(?<attributes>
(?:
\s+
(?:
(?:
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
@(?:style)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
)
|
(?:
[\w\-:.@]+
(
=
(?:
\\\"[^\\\"]*\\\"
|
\'[^\']*\'
|
[^\'\\\"=<>]+
)
)?
)
)
)*
\s*
)
(?<![\/=\-])
>
/x";
$value = preg_replace_callback($pattern, function ($matches) {
$name = $this->stripQuotes($matches['inlineName'] ?: $matches['name'] ?: $matches['boundName']);
if (Str::contains($name, '-') && ! empty($matches['inlineName'])) {
$name = Str::camel($name);
}
// If the name was given as a simple string, we will wrap it in quotes as if it was bound for convenience...
if (! empty($matches['inlineName']) || ! empty($matches['name'])) {
$name = "'{$name}'";
}
$this->boundAttributes = [];
$attributes = $this->getAttributesFromAttributeString($matches['attributes']);
// If an inline name was provided and a name or bound name was *also* provided, we will assume the name should be an attribute...
if (! empty($matches['inlineName']) && (! empty($matches['name']) || ! empty($matches['boundName']))) {
$attributes = ! empty($matches['name'])
? array_merge($attributes, $this->getAttributesFromAttributeString('name='.$matches['name']))
: array_merge($attributes, $this->getAttributesFromAttributeString(':name='.$matches['boundName']));
}
return " @slot({$name}, null, [".$this->attributesToString($attributes).']) ';
}, $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->parseShortAttributeSyntax($attributeString);
$attributeString = $this->parseAttributeBag($attributeString);
$attributeString = $this->parseComponentTagClassStatements($attributeString);
$attributeString = $this->parseComponentTagStyleStatements($attributeString);
$attributeString = $this->parseBindAttributes($attributeString);
$pattern = '/
(?<attribute>[\w\-:.@%]+)
(
=
(?<value>
(
\"[^\"]+\"
|
\\\'[^\\\']+\\\'
|
[^\s>]+
)
)
)?
/x';
if (! preg_match_all($pattern, $attributeString, $matches, PREG_SET_ORDER)) {
return [];
}
return (new Collection($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_starts_with($attribute, 'bind:')) {
$attribute = Str::after($attribute, 'bind:');
$this->boundAttributes[$attribute] = true;
} else {
$value = "'".$this->compileAttributeEchos($value)."'";
}
if (str_starts_with($attribute, '::')) {
$attribute = substr($attribute, 1);
}
return [$attribute => $value];
})->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.
*
* @param string $attributeString
* @return string
*/
protected function parseAttributeBag(string $attributeString)
{
$pattern = "/
(?:^|\s+) # start of the string or whitespace between attributes
\{\{\s*(\\\$attributes(?:[^}]+?(?<!\s))?)\s*\}\} # exact match of attributes variable being echoed
/x";
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 @style statements in a given attribute string into their fully-qualified syntax.
*
* @param string $attributeString
* @return string
*/
protected function parseComponentTagStyleStatements(string $attributeString)
{
return preg_replace_callback(
'/@(style)(\( ( (?>[^()]+) | (?2) )* \))/x', function ($match) {
if ($match[1] === 'style') {
$match[2] = str_replace('"', "'", $match[2]);
return ":style=\"\Illuminate\Support\Arr::toCssStyles{$match[2]}\"";
}
return $match[0];
}, $attributeString
);
}
/**
* Parse the "bind" attributes in a given attribute string into their fully-qualified syntax.
*
* @param string $attributeString
* @return string
*/
protected function parseBindAttributes(string $attributeString)
{
$pattern = "/
(?:^|\s+) # start of the string or whitespace between attributes
:(?!:) # attribute needs to start with a single colon
([\w\-:.@]+) # match the actual attribute name
= # only match attributes that have a value
/xm";
return preg_replace($pattern, ' bind:$1=', $attributeString);
}
/**
* Compile any Blade echo statements that are present in the attribute string.
*
* These echo statements need to be converted to string concatenation statements.
*
* @param string $attributeString
* @return string
*/
protected function compileAttributeEchos(string $attributeString)
{
$value = $this->blade->compileEchos($attributeString);
$value = $this->escapeSingleQuotesOutsideOfPhpBlocks($value);
$value = str_replace('<?php echo ', '\'.', $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 (new Collection(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 (new Collection($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;
}
}
@@ -0,0 +1,102 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesAuthorizations
{
/**
* Compile the can statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileCan($expression)
{
return "<?php if (app(\Illuminate\\Contracts\\Auth\\Access\\Gate::class)->check{$expression}): ?>";
}
/**
* Compile the cannot statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileCannot($expression)
{
return "<?php if (app(\Illuminate\\Contracts\\Auth\\Access\\Gate::class)->denies{$expression}): ?>";
}
/**
* Compile the canany statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileCanany($expression)
{
return "<?php if (app(\Illuminate\\Contracts\\Auth\\Access\\Gate::class)->any{$expression}): ?>";
}
/**
* Compile the else-can statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElsecan($expression)
{
return "<?php elseif (app(\Illuminate\\Contracts\\Auth\\Access\\Gate::class)->check{$expression}): ?>";
}
/**
* Compile the else-cannot statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElsecannot($expression)
{
return "<?php elseif (app(\Illuminate\\Contracts\\Auth\\Access\\Gate::class)->denies{$expression}): ?>";
}
/**
* Compile the else-canany statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElsecanany($expression)
{
return "<?php elseif (app(\Illuminate\\Contracts\\Auth\\Access\\Gate::class)->any{$expression}): ?>";
}
/**
* Compile the end-can statements into valid PHP.
*
* @return string
*/
protected function compileEndcan()
{
return '<?php endif; ?>';
}
/**
* Compile the end-cannot statements into valid PHP.
*
* @return string
*/
protected function compileEndcannot()
{
return '<?php endif; ?>';
}
/**
* Compile the end-canany statements into valid PHP.
*
* @return string
*/
protected function compileEndcanany()
{
return '<?php endif; ?>';
}
}
@@ -0,0 +1,19 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesClasses
{
/**
* Compile the conditional class statement into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileClass($expression)
{
$expression = is_null($expression) ? '([])' : $expression;
return "class=\"<?php echo \Illuminate\Support\Arr::toCssClasses{$expression}; ?>\"";
}
}
@@ -0,0 +1,19 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesComments
{
/**
* Compile Blade comments into an empty string.
*
* @param string $value
* @return string
*/
protected function compileComments($value)
{
$pattern = sprintf('/%s--(.*?)--%s/s', $this->contentTags[0], $this->contentTags[1]);
return preg_replace($pattern, '', $value);
}
}
@@ -0,0 +1,222 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString;
use Illuminate\Support\Str;
use Illuminate\View\AnonymousComponent;
use Illuminate\View\ComponentAttributeBag;
trait CompilesComponents
{
/**
* The component name hash stack.
*
* @var array
*/
protected static $componentHashStack = [];
/**
* Compile the component statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileComponent($expression)
{
[$component, $alias, $data] = str_contains($expression, ',')
? array_map(trim(...), explode(',', trim($expression, '()'), 3)) + ['', '', '']
: [trim($expression, '()'), '', ''];
$component = trim($component, '\'"');
$hash = static::newComponentHash(
$component === AnonymousComponent::class ? $component.':'.trim($alias, '\'"') : $component
);
if (Str::contains($component, ['::class', '\\'])) {
return static::compileClassComponentOpening($component, $alias, $data, $hash);
}
return "<?php \$__env->startComponent{$expression}; ?>";
}
/**
* Get a new component hash for a component name.
*
* @param string $component
* @return string
*/
public static function newComponentHash(string $component)
{
static::$componentHashStack[] = $hash = hash('xxh128', $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", [
'<?php if (isset($component)) { $__componentOriginal'.$hash.' = $component; } ?>',
'<?php if (isset($attributes)) { $__attributesOriginal'.$hash.' = $attributes; } ?>',
'<?php $component = '.$component.'::resolve('.($data ?: '[]').' + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>',
'<?php $component->withName('.$alias.'); ?>',
'<?php if ($component->shouldRender()): ?>',
'<?php $__env->startComponent($component->resolveView(), $component->data()); ?>',
]);
}
/**
* Compile the end-component statements into valid PHP.
*
* @return string
*/
protected function compileEndComponent()
{
return '<?php echo $__env->renderComponent(); ?>';
}
/**
* Compile the end-component statements into valid PHP.
*
* @return string
*/
public function compileEndComponentClass()
{
$hash = array_pop(static::$componentHashStack);
return $this->compileEndComponent()."\n".implode("\n", [
'<?php endif; ?>',
'<?php if (isset($__attributesOriginal'.$hash.')): ?>',
'<?php $attributes = $__attributesOriginal'.$hash.'; ?>',
'<?php unset($__attributesOriginal'.$hash.'); ?>',
'<?php endif; ?>',
'<?php if (isset($__componentOriginal'.$hash.')): ?>',
'<?php $component = $__componentOriginal'.$hash.'; ?>',
'<?php unset($__componentOriginal'.$hash.'); ?>',
'<?php endif; ?>',
]);
}
/**
* Compile the slot statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileSlot($expression)
{
return "<?php \$__env->slot{$expression}; ?>";
}
/**
* Compile the end-slot statements into valid PHP.
*
* @return string
*/
protected function compileEndSlot()
{
return '<?php $__env->endSlot(); ?>';
}
/**
* Compile the component-first statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileComponentFirst($expression)
{
return "<?php \$__env->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 "<?php \$attributes ??= new \\Illuminate\\View\\ComponentAttributeBag;
\$__newAttributes = [];
\$__propNames = \Illuminate\View\ComponentAttributeBag::extractPropNames({$expression});
foreach (\$attributes->all() as \$__key => \$__value) {
if (in_array(\$__key, \$__propNames)) {
\$\$__key = \$\$__key ?? \$__value;
} else {
\$__newAttributes[\$__key] = \$__value;
}
}
\$attributes = new \Illuminate\View\ComponentAttributeBag(\$__newAttributes);
unset(\$__propNames);
unset(\$__newAttributes);
foreach (array_filter({$expression}, 'is_string', ARRAY_FILTER_USE_KEY) as \$__key => \$__value) {
\$\$__key = \$\$__key ?? \$__value;
}
\$__defined_vars = get_defined_vars();
foreach (\$attributes->all() as \$__key => \$__value) {
if (array_key_exists(\$__key, \$__defined_vars)) unset(\$\$__key);
}
unset(\$__defined_vars); ?>";
}
/**
* Compile the aware statement into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileAware($expression)
{
return "<?php foreach ({$expression} as \$__key => \$__value) {
\$__consumeVariable = is_string(\$__key) ? \$__key : \$__value;
\$\$__consumeVariable = is_string(\$__key) ? \$__env->getConsumableComponentData(\$__key, \$__value) : \$__env->getConsumableComponentData(\$__value);
} ?>";
}
/**
* Sanitize the given component attribute value.
*
* @param mixed $value
* @return mixed
*/
public static function sanitizeComponentAttribute($value)
{
if ($value instanceof CanBeEscapedWhenCastToString) {
return $value->escapeWhenCastingToString();
}
return is_string($value) ||
(is_object($value) && ! $value instanceof ComponentAttributeBag && method_exists($value, '__toString'))
? e($value)
: $value;
}
}
@@ -0,0 +1,420 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
use Illuminate\Support\Str;
trait CompilesConditionals
{
/**
* Identifier for the first case in the switch statement.
*
* @var bool
*/
protected $firstCaseInSwitch = true;
/**
* Compile the if-auth statements into valid PHP.
*
* @param string|null $guard
* @return string
*/
protected function compileAuth($guard = null)
{
$guard = is_null($guard) ? '()' : $guard;
return "<?php if(auth()->guard{$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 "<?php elseif(auth()->guard{$guard}->check()): ?>";
}
/**
* Compile the end-auth statements into valid PHP.
*
* @return string
*/
protected function compileEndAuth()
{
return '<?php endif; ?>';
}
/**
* Compile the env statements into valid PHP.
*
* @param string $environments
* @return string
*/
protected function compileEnv($environments)
{
return "<?php if(app()->environment{$environments}): ?>";
}
/**
* Compile the end-env statements into valid PHP.
*
* @return string
*/
protected function compileEndEnv()
{
return '<?php endif; ?>';
}
/**
* Compile the production statements into valid PHP.
*
* @return string
*/
protected function compileProduction()
{
return "<?php if(app()->environment('production')): ?>";
}
/**
* Compile the end-production statements into valid PHP.
*
* @return string
*/
protected function compileEndProduction()
{
return '<?php endif; ?>';
}
/**
* 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 "<?php if(auth()->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 "<?php elseif(auth()->guard{$guard}->guest()): ?>";
}
/**
* Compile the end-guest statements into valid PHP.
*
* @return string
*/
protected function compileEndGuest()
{
return '<?php endif; ?>';
}
/**
* Compile the has-section statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileHasSection($expression)
{
return "<?php if (! empty(trim(\$__env->yieldContent{$expression}))): ?>";
}
/**
* Compile the section-missing statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileSectionMissing($expression)
{
return "<?php if (empty(trim(\$__env->yieldContent{$expression}))): ?>";
}
/**
* Compile the if statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileIf($expression)
{
return "<?php if{$expression}: ?>";
}
/**
* Compile the unless statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileUnless($expression)
{
return "<?php if (! {$expression}): ?>";
}
/**
* Compile the else-if statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElseif($expression)
{
return "<?php elseif{$expression}: ?>";
}
/**
* Compile the else statements into valid PHP.
*
* @return string
*/
protected function compileElse()
{
return '<?php else: ?>';
}
/**
* Compile the end-if statements into valid PHP.
*
* @return string
*/
protected function compileEndif()
{
return '<?php endif; ?>';
}
/**
* Compile the end-unless statements into valid PHP.
*
* @return string
*/
protected function compileEndunless()
{
return '<?php endif; ?>';
}
/**
* Compile the if-isset statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileIsset($expression)
{
return "<?php if(isset{$expression}): ?>";
}
/**
* Compile the end-isset statements into valid PHP.
*
* @return string
*/
protected function compileEndIsset()
{
return '<?php endif; ?>';
}
/**
* Compile the switch statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileSwitch($expression)
{
$this->firstCaseInSwitch = true;
return "<?php switch{$expression}:";
}
/**
* Compile the case statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileCase($expression)
{
if ($this->firstCaseInSwitch) {
$this->firstCaseInSwitch = false;
return "case {$expression}: ?>";
}
return "<?php case {$expression}: ?>";
}
/**
* Compile the default statements in switch case into valid PHP.
*
* @return string
*/
protected function compileDefault()
{
return '<?php default: ?>';
}
/**
* Compile the end switch statements into valid PHP.
*
* @return string
*/
protected function compileEndSwitch()
{
return '<?php endswitch; ?>';
}
/**
* Compile a once block into valid PHP.
*
* @param string|null $id
* @return string
*/
protected function compileOnce($id = null)
{
$id = $id ? $this->stripParentheses($id) : "'".(string) Str::uuid()."'";
return '<?php if (! $__env->hasRenderedOnce('.$id.')): $__env->markAsRenderedOnce('.$id.'); ?>';
}
/**
* Compile an end-once block into valid PHP.
*
* @return string
*/
public function compileEndOnce()
{
return '<?php endif; ?>';
}
/**
* Compile a boolean value into a raw true / false value for embedding into HTML attributes or JavaScript.
*
* @param bool $condition
* @return string
*/
protected function compileBool($condition)
{
return "<?php echo ($condition ? 'true' : 'false'); ?>";
}
/**
* 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 a selected block into valid PHP.
*
* @param string $condition
* @return string
*/
protected function compileSelected($condition)
{
return "<?php if{$condition}: echo 'selected'; 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 else-if push statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElsePushIf($expression)
{
$parts = explode(',', $this->stripParentheses($expression), 2);
return "<?php \$__env->stopPush(); elseif({$parts[0]}): \$__env->startPush({$parts[1]}); ?>";
}
/**
* Compile the else push statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElsePush($expression)
{
return "<?php \$__env->stopPush(); else: \$__env->startPush{$expression}; ?>";
}
/**
* Compile the end-push statements into valid PHP.
*
* @return string
*/
protected function compileEndPushIf()
{
return '<?php $__env->stopPush(); endif; ?>';
}
}
@@ -0,0 +1,171 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
use Closure;
use Illuminate\Support\Stringable;
trait CompilesEchos
{
/**
* Custom rendering callbacks for stringable objects.
*
* @var array
*/
protected $echoHandlers = [];
/**
* Add a handler to be executed before echoing a given class.
*
* @param string|callable $class
* @param callable|null $handler
* @return void
*/
public function stringable($class, $handler = null)
{
if ($class instanceof Closure) {
[$class, $handler] = [$this->firstClosureParameterType($class), $class];
}
$this->echoHandlers[$class] = $handler;
}
/**
* Compile Blade echos into valid PHP.
*
* @param string $value
* @return string
*/
public function compileEchos($value)
{
foreach ($this->getEchoMethods() 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)
: "<?php echo {$this->wrapInEchoHandler($matches[2])}; ?>{$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, $this->wrapInEchoHandler($matches[2]));
return $matches[1] ? substr($matches[0], 1) : "<?php echo {$wrapped}; ?>{$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]
: "<?php echo e({$this->wrapInEchoHandler($matches[2])}); ?>{$whitespace}";
};
return preg_replace_callback($pattern, $callback, $value);
}
/**
* Add an instance of the blade echo handler to the start of the compiled string.
*
* @param string $result
* @return string
*/
protected function addBladeCompilerVariable($result)
{
return "<?php \$__bladeCompiler = app('blade.compiler'); ?>".$result;
}
/**
* Wrap the echoable value in an echo handler if applicable.
*
* @param string $value
* @return string
*/
protected function wrapInEchoHandler($value)
{
$value = (new Stringable($value))
->trim()
->when(str_ends_with($value, ';'), function ($str) {
return $str->beforeLast(';');
});
return empty($this->echoHandlers) ? $value : '$__bladeCompiler->applyEchoHandler('.$value.')';
}
/**
* Apply the echo handler for the value if it exists.
*
* @param string $value
* @return string
*/
public function applyEchoHandler($value)
{
if (is_object($value) && isset($this->echoHandlers[get_class($value)])) {
return call_user_func($this->echoHandlers[get_class($value)], $value);
}
if (is_iterable($value) && isset($this->echoHandlers['iterable'])) {
return call_user_func($this->echoHandlers['iterable'], $value);
}
return $value;
}
}
@@ -0,0 +1,37 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesErrors
{
/**
* Compile the error statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileError($expression)
{
$expression = $this->stripParentheses($expression);
return '<?php $__errorArgs = ['.$expression.'];
$__bag = $errors->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 '<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>';
}
}
@@ -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(); ?>';
}
}
@@ -0,0 +1,78 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
use Illuminate\Foundation\Vite;
trait CompilesHelpers
{
/**
* Compile the CSRF statements into valid PHP.
*
* @return string
*/
protected function compileCsrf()
{
return '<?php echo csrf_field(); ?>';
}
/**
* Compile the "dd" statements into valid PHP.
*
* @param string $arguments
* @return string
*/
protected function compileDd($arguments)
{
return "<?php dd{$arguments}; ?>";
}
/**
* Compile the "dump" statements into valid PHP.
*
* @param string $arguments
* @return string
*/
protected function compileDump($arguments)
{
return "<?php dump{$arguments}; ?>";
}
/**
* Compile the method statements into valid PHP.
*
* @param string $method
* @return string
*/
protected function compileMethod($method)
{
return "<?php echo method_field{$method}; ?>";
}
/**
* Compile the "vite" statements into valid PHP.
*
* @param string|null $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(); ?>";
}
}
@@ -0,0 +1,82 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesIncludes
{
/**
* Compile the each statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEach($expression)
{
return "<?php echo \$__env->renderEach{$expression}; ?>";
}
/**
* Compile the include statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileInclude($expression)
{
$expression = $this->stripParentheses($expression);
return "<?php echo \$__env->make({$expression}, array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>";
}
/**
* Compile the include-if statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileIncludeIf($expression)
{
$expression = $this->stripParentheses($expression);
return "<?php if (\$__env->exists({$expression})) echo \$__env->make({$expression}, array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>";
}
/**
* Compile the include-when statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileIncludeWhen($expression)
{
$expression = $this->stripParentheses($expression);
return "<?php echo \$__env->renderWhen($expression, array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1])); ?>";
}
/**
* Compile the include-unless statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileIncludeUnless($expression)
{
$expression = $this->stripParentheses($expression);
return "<?php echo \$__env->renderUnless($expression, array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1])); ?>";
}
/**
* Compile the include-first statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileIncludeFirst($expression)
{
$expression = $this->stripParentheses($expression);
return "<?php echo \$__env->first({$expression}, array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>";
}
}
@@ -0,0 +1,23 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesInjections
{
/**
* Compile the inject statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileInject($expression)
{
$segments = explode(',', preg_replace("/[\(\)]/", '', $expression));
$variable = trim($segments[0], " '\"");
$service = trim($segments[1]);
return "<?php \${$variable} = app({$service}); ?>";
}
}
@@ -0,0 +1,22 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
use Illuminate\Support\Js;
trait CompilesJs
{
/**
* Compile the "@js" directive into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileJs(string $expression)
{
return sprintf(
"<?php echo \%s::from(%s)->toHtml() ?>",
Js::class, $this->stripParentheses($expression)
);
}
}
@@ -0,0 +1,30 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesJson
{
/**
* The default JSON encoding options.
*
* @var int
*/
private $encodingOptions = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT;
/**
* Compile the JSON statement into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileJson($expression)
{
$parts = explode(',', $this->stripParentheses($expression));
$options = isset($parts[1]) ? trim($parts[1]) : $this->encodingOptions;
$depth = isset($parts[2]) ? trim($parts[2]) : 512;
return "<?php echo json_encode($parts[0], $options, $depth) ?>";
}
}
@@ -0,0 +1,133 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesLayouts
{
/**
* The name of the last section that was started.
*
* @var string
*/
protected $lastSection;
/**
* Compile the extends statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileExtends($expression)
{
$expression = $this->stripParentheses($expression);
$echo = "<?php echo \$__env->make({$expression}, array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->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 = "<?php echo \$__env->first({$expression}, array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->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 "<?php \$__env->startSection{$expression}; ?>";
}
/**
* Replace the @parent directive to a placeholder.
*
* @return string
*/
protected function compileParent()
{
$escapedLastSection = strtr($this->lastSection, ['\\' => '\\\\', "'" => "\\'"]);
return "<?php echo \Illuminate\View\Factory::parentPlaceholder('{$escapedLastSection}'); ?>";
}
/**
* Compile the yield statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileYield($expression)
{
return "<?php echo \$__env->yieldContent{$expression}; ?>";
}
/**
* Compile the show statements into valid PHP.
*
* @return string
*/
protected function compileShow()
{
return '<?php echo $__env->yieldSection(); ?>';
}
/**
* Compile the append statements into valid PHP.
*
* @return string
*/
protected function compileAppend()
{
return '<?php $__env->appendSection(); ?>';
}
/**
* Compile the overwrite statements into valid PHP.
*
* @return string
*/
protected function compileOverwrite()
{
return '<?php $__env->stopSection(true); ?>';
}
/**
* Compile the stop statements into valid PHP.
*
* @return string
*/
protected function compileStop()
{
return '<?php $__env->stopSection(); ?>';
}
/**
* Compile the end-section statements into valid PHP.
*
* @return string
*/
protected function compileEndsection()
{
return '<?php $__env->stopSection(); ?>';
}
}
@@ -0,0 +1,194 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
use Illuminate\Contracts\View\ViewCompilationException;
trait CompilesLoops
{
/**
* Counter to keep track of nested forelse statements.
*
* @var int
*/
protected $forElseCounter = 0;
/**
* Compile the for-else statements into valid PHP.
*
* @param string|null $expression
* @return string
*
* @throws \Illuminate\Contracts\View\ViewCompilationException
*/
protected function compileForelse($expression)
{
$empty = '$__empty_'.++$this->forElseCounter;
preg_match('/\( *(.+) +as +(.+)\)$/is', $expression ?? '', $matches);
if (count($matches) === 0) {
throw new ViewCompilationException('Malformed @forelse statement.');
}
$iteratee = trim($matches[1]);
$iteration = trim($matches[2]);
$initLoop = "\$__currentLoopData = {$iteratee}; \$__env->addLoop(\$__currentLoopData);";
$iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();';
return "<?php {$empty} = true; {$initLoop} foreach(\$__currentLoopData as {$iteration}): {$iterateLoop} {$empty} = false; ?>";
}
/**
* Compile the for-else-empty and empty statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEmpty($expression)
{
if ($expression) {
return "<?php if(empty{$expression}): ?>";
}
$empty = '$__empty_'.$this->forElseCounter--;
return "<?php endforeach; \$__env->popLoop(); \$loop = \$__env->getLastLoop(); if ({$empty}): ?>";
}
/**
* Compile the end-for-else statements into valid PHP.
*
* @return string
*/
protected function compileEndforelse()
{
return '<?php endif; ?>';
}
/**
* Compile the end-empty statements into valid PHP.
*
* @return string
*/
protected function compileEndEmpty()
{
return '<?php endif; ?>';
}
/**
* Compile the for statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileFor($expression)
{
return "<?php for{$expression}: ?>";
}
/**
* Compile the for-each statements into valid PHP.
*
* @param string|null $expression
* @return string
*
* @throws \Illuminate\Contracts\View\ViewCompilationException
*/
protected function compileForeach($expression)
{
preg_match('/\( *(.+) +as +(.*)\)$/is', $expression ?? '', $matches);
if (count($matches) === 0) {
throw new ViewCompilationException('Malformed @foreach statement.');
}
$iteratee = trim($matches[1]);
$iteration = trim($matches[2]);
$initLoop = "\$__currentLoopData = {$iteratee}; \$__env->addLoop(\$__currentLoopData);";
$iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();';
return "<?php {$initLoop} foreach(\$__currentLoopData as {$iteration}): {$iterateLoop} ?>";
}
/**
* 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 ? '<?php break '.max(1, $matches[1]).'; ?>' : "<?php if{$expression} break; ?>";
}
return '<?php break; ?>';
}
/**
* 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 ? '<?php continue '.max(1, $matches[1]).'; ?>' : "<?php if{$expression} continue; ?>";
}
return '<?php continue; ?>';
}
/**
* Compile the end-for statements into valid PHP.
*
* @return string
*/
protected function compileEndfor()
{
return '<?php endfor; ?>';
}
/**
* Compile the end-for-each statements into valid PHP.
*
* @return string
*/
protected function compileEndforeach()
{
return '<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>';
}
/**
* Compile the while statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileWhile($expression)
{
return "<?php while{$expression}: ?>";
}
/**
* Compile the end-while statements into valid PHP.
*
* @return string
*/
protected function compileEndwhile()
{
return '<?php endwhile; ?>';
}
}
@@ -0,0 +1,32 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesRawPhp
{
/**
* Compile the raw PHP statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compilePhp($expression)
{
if ($expression) {
return "<?php {$expression}; ?>";
}
return '@php';
}
/**
* Compile the unset statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileUnset($expression)
{
return "<?php unset{$expression}; ?>";
}
}
@@ -0,0 +1,37 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesSessions
{
/**
* Compile the session statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileSession($expression)
{
$expression = $this->stripParentheses($expression);
return '<?php $__sessionArgs = ['.$expression.'];
if (session()->has($__sessionArgs[0])) :
if (isset($value)) { $__sessionPrevious[] = $value; }
$value = session()->get($__sessionArgs[0]); ?>';
}
/**
* Compile the endsession statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndsession($expression)
{
return '<?php unset($value);
if (isset($__sessionPrevious) && !empty($__sessionPrevious)) { $value = array_pop($__sessionPrevious); }
if (isset($__sessionPrevious) && empty($__sessionPrevious)) { unset($__sessionPrevious); }
endif;
unset($__sessionArgs); ?>';
}
}
@@ -0,0 +1,117 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
use Illuminate\Support\Str;
trait CompilesStacks
{
/**
* Compile the stack statements into the content.
*
* @param string $expression
* @return string
*/
protected function compileStack($expression)
{
return "<?php echo \$__env->yieldPushContent{$expression}; ?>";
}
/**
* Compile the push statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compilePush($expression)
{
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.
*
* @return string
*/
protected function compileEndpush()
{
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.
*
* @param string $expression
* @return string
*/
protected function compilePrepend($expression)
{
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.
*
* @return string
*/
protected function compileEndprepend()
{
return '<?php $__env->stopPrepend(); ?>';
}
/**
* Compile the end-prepend-once statements into valid PHP.
*
* @return string
*/
protected function compileEndprependOnce()
{
return '<?php $__env->stopPrepend(); endif; ?>';
}
}
@@ -0,0 +1,19 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesStyles
{
/**
* Compile the conditional style statement into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileStyle($expression)
{
$expression = is_null($expression) ? '([])' : $expression;
return "style=\"<?php echo \Illuminate\Support\Arr::toCssStyles{$expression} ?>\"";
}
}
@@ -0,0 +1,44 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesTranslations
{
/**
* Compile the lang statements into valid PHP.
*
* @param string|null $expression
* @return string
*/
protected function compileLang($expression)
{
if (is_null($expression)) {
return '<?php $__env->startTranslation(); ?>';
} elseif ($expression[1] === '[') {
return "<?php \$__env->startTranslation{$expression}; ?>";
}
return "<?php echo app('translator')->get{$expression}; ?>";
}
/**
* Compile the end-lang statements into valid PHP.
*
* @return string
*/
protected function compileEndlang()
{
return '<?php echo $__env->renderTranslation(); ?>';
}
/**
* Compile the choice statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileChoice($expression)
{
return "<?php echo app('translator')->choice{$expression}; ?>";
}
}
@@ -0,0 +1,46 @@
<?php
namespace Illuminate\View\Compilers\Concerns;
trait CompilesUseStatements
{
/**
* Compile the use statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileUse($expression)
{
$expression = trim(preg_replace('/[()]/', '', $expression), " '\"");
// Isolate alias...
if (str_contains($expression, '{')) {
$pathWithOptionalModifier = $expression;
$aliasWithLeadingSpace = '';
} else {
$segments = explode(',', $expression);
$pathWithOptionalModifier = trim($segments[0], " '\"");
$aliasWithLeadingSpace = isset($segments[1])
? ' as '.trim($segments[1], " '\"")
: '';
}
// Split modifier and path...
if (str_starts_with($pathWithOptionalModifier, 'function ')) {
$modifierWithTrailingSpace = 'function ';
$path = explode(' ', $pathWithOptionalModifier, 2)[1] ?? $pathWithOptionalModifier;
} elseif (str_starts_with($pathWithOptionalModifier, 'const ')) {
$modifierWithTrailingSpace = 'const ';
$path = explode(' ', $pathWithOptionalModifier, 2)[1] ?? $pathWithOptionalModifier;
} else {
$modifierWithTrailingSpace = '';
$path = $pathWithOptionalModifier;
}
$path = ltrim($path, '\\');
return "<?php use {$modifierWithTrailingSpace}\\{$path}{$aliasWithLeadingSpace}; ?>";
}
}