From 8643bd67165dac7a6e67fa2f2aa5eb575175cbd5 Mon Sep 17 00:00:00 2001 From: Fernando Gutierrez Date: Tue, 30 Jul 2024 18:37:37 -0600 Subject: [PATCH] 5.3.0 - Added hooks - Added Blade facade - Cleaned classes - Refactored classes --- composer.json | 2 +- extensions/hooks.php | 19 +++++ extensions/options.php | 11 +++ index.php | 13 ++-- src/Facades/Blade.php | 14 ++++ src/KirbyBlade/Blade.php | 10 +-- src/Snippet.php | 12 +++- src/Template.php | 47 ++++++++----- src/View/AnonymousComponent.php | 18 ----- src/View/Compiler/BladeCompiler.php | 16 +++++ src/View/Compiler/ComponentTagCompiler.php | 82 +++------------------- src/View/Concerns/ManagesLayouts.php | 51 -------------- src/View/DynamicComponent.php | 63 ++--------------- src/View/Factory.php | 48 ------------- src/View/ViewServiceProvider.php | 13 ++-- 15 files changed, 130 insertions(+), 289 deletions(-) create mode 100644 extensions/hooks.php create mode 100644 extensions/options.php create mode 100644 src/Facades/Blade.php delete mode 100644 src/View/AnonymousComponent.php delete mode 100644 src/View/Concerns/ManagesLayouts.php delete mode 100644 src/View/Factory.php diff --git a/composer.json b/composer.json index 86ad504..239f67d 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "keywords": [ "kirby", "kirby-3", "kirby-4", "blade", "view", "template" ], - "version": "5.2.1", + "version": "5.3.0", "type": "kirby-plugin", "license": "MIT", "authors": [ diff --git a/extensions/hooks.php b/extensions/hooks.php new file mode 100644 index 0000000..c3a6f44 --- /dev/null +++ b/extensions/hooks.php @@ -0,0 +1,19 @@ + function () { + $container = new BladeContainer; + Container::setInstance($container); + + new Blade( + Template::getPathTemplates(), + Template::getPathViews(), + $container + ); + }, +]; diff --git a/extensions/options.php b/extensions/options.php new file mode 100644 index 0000000..7176d09 --- /dev/null +++ b/extensions/options.php @@ -0,0 +1,11 @@ + function (): string { + return App::instance()->roots()->cache().'/views'; + }, + 'directives' => [], + 'ifs' => [], +]; diff --git a/index.php b/index.php index 05009e8..992a3a5 100644 --- a/index.php +++ b/index.php @@ -1,20 +1,17 @@ [ - 'views' => function () { - return kirby()->roots()->cache().'/views'; - }, - 'directives' => [], - 'ifs' => [], - ], + 'options' => require_once __DIR__.'/extensions/options.php', + 'hooks' => require_once __DIR__.'/extensions/hooks.php', + 'components' => [ 'template' => function (Kirby $kirby, string $name, ?string $contentType = null) { if (Str::endsWith($kirby->request()->url(), '.php')) { diff --git a/src/Facades/Blade.php b/src/Facades/Blade.php new file mode 100644 index 0000000..7611d28 --- /dev/null +++ b/src/Facades/Blade.php @@ -0,0 +1,14 @@ +get('blade.compiler'); + } +} diff --git a/src/KirbyBlade/Blade.php b/src/KirbyBlade/Blade.php index 54b0430..a149687 100644 --- a/src/KirbyBlade/Blade.php +++ b/src/KirbyBlade/Blade.php @@ -19,12 +19,14 @@ class Blade extends BladeProvider public function __construct($viewPaths, string $cachePath, ?ContainerInterface $container = null) { - $this->container = $container ?: new Container; - $this->setupContainer((array) $viewPaths, $cachePath); - - (new ViewServiceProvider($this->container))->register(); + $this->container = $container ?: Container::getInstance(); Container::setInstance($this->container); + if (! $this->container->has('blade.compiler') || ! $this->container->has('view')) { + $this->setupContainer((array) $viewPaths, $cachePath); + (new ViewServiceProvider($this->container))->register(); + } + $this->factory = $this->container->get('view'); $this->compiler = $this->container->get('blade.compiler'); } diff --git a/src/Snippet.php b/src/Snippet.php index d8702c9..486945e 100644 --- a/src/Snippet.php +++ b/src/Snippet.php @@ -9,12 +9,12 @@ class Snippet extends Template { - protected $snippet; + protected string $snippet; public function __construct(Kirby $kirby, string $name, string $type = 'html', string $defaultType = 'html') { - $this->template = $kirby->roots()->snippets(); - $this->views = $this->getPathViews(); + $this->template = static::getPathSnippets(); + $this->views = static::getPathViews(); $this->snippet = $this->template.'/'.$name.'.php'; $blade = $this->template.'/'.$name.'.'.$this->bladeExtension(); @@ -38,6 +38,7 @@ public function render(array $data = []): string $this->views, new Container ); + $this->setDirectives(); $this->setIfStatements(); @@ -46,4 +47,9 @@ public function render(array $data = []): string return Tpl::load($this->snippet, $data); } } + + public static function getPathSnippets(): string + { + return Kirby::instance()->roots()->snippets(); + } } diff --git a/src/Template.php b/src/Template.php index 3b5c6f2..1ba41ec 100644 --- a/src/Template.php +++ b/src/Template.php @@ -2,9 +2,11 @@ namespace Beebmx; -use Beebmx\Blade\Container; +use Beebmx\Blade\Container as KirbyContainer; use Beebmx\KirbyBlade\Blade; use Exception; +use Illuminate\Container\Container; +use Illuminate\Contracts\Container\Container as ContainerInterface; use Kirby\Cms\App as Kirby; use Kirby\Filesystem\Dir; use Kirby\Filesystem\F; @@ -15,24 +17,22 @@ class Template extends KirbyTemplate { protected Blade $blade; - protected $views; + protected string $views; protected string $defaultType; protected string $name; - protected $template; + protected string $template; protected string $type; - protected Container $app; - public static array $data = []; public function __construct(Kirby $kirby, string $name, string $type = 'html', string $defaultType = 'html') { - $this->template = $kirby->roots()->templates(); - $this->views = $this->getPathViews(); + $this->template = static::getPathTemplates(); + $this->views = static::getPathViews(); $this->name = strtolower($name); $this->type = $type; @@ -76,18 +76,22 @@ public function file(): ?string public function render(array $data = []): string { if ($this->isBlade()) { - $this->app = new Container; + $application = $this->getContainer( + Container::getInstance() + ); + $this->blade = new Blade( $this->template, $this->views, - $this->app + $application ); + $this->setDirectives(); $this->setIfStatements(); if ($this->hasDefaultType() === true) { - return tap($this->blade->make($this->name, $data), function () { - $this->app->terminate(); + return tap($this->blade->make($this->name, $data), function () use ($application) { + $application->terminate(); }); } } @@ -270,11 +274,6 @@ public function isBlade(): bool return (bool) file_exists($this->template.'/'.$this->name().'.'.$this->bladeExtension()); } - public function app(): ?Container - { - return $this->app ?? null; - } - /** * Returns the expected template file extension */ @@ -283,13 +282,25 @@ public function bladeExtension(): string return 'blade.php'; } - protected function getPathViews() + public static function getPathTemplates(): string { - $path = option('beebmx.kirby-blade.views'); + return Kirby::instance()->roots()->templates(); + } + + public static function getPathViews(): string + { + $path = Kirby::instance()->option('beebmx.kirby-blade.views'); if (is_callable($path)) { return $path(); } return $path; } + + protected function getContainer(ContainerInterface $container): ContainerInterface + { + return method_exists($container, 'terminate') + ? $container + : new KirbyContainer; + } } diff --git a/src/View/AnonymousComponent.php b/src/View/AnonymousComponent.php deleted file mode 100644 index 073d79c..0000000 --- a/src/View/AnonymousComponent.php +++ /dev/null @@ -1,18 +0,0 @@ -attributes = $this->attributes ?: new ComponentAttributeBag; - - return $this->data + ['attributes' => $this->attributes]; - } -} diff --git a/src/View/Compiler/BladeCompiler.php b/src/View/Compiler/BladeCompiler.php index b71f009..8c81840 100644 --- a/src/View/Compiler/BladeCompiler.php +++ b/src/View/Compiler/BladeCompiler.php @@ -2,6 +2,7 @@ namespace Beebmx\View\Compiler; +use Illuminate\Container\Container; use Illuminate\View\Compilers\BladeCompiler as Compiler; class BladeCompiler extends Compiler @@ -59,4 +60,19 @@ public static function sanitizeComponentAttribute($value): mixed ? _e($value) : $value; } + + public function anonymousComponentPath(string $path, ?string $prefix = null): void + { + $prefixHash = md5($prefix ?: $path); + + $this->anonymousComponentPaths[] = [ + 'path' => $path, + 'prefix' => $prefix, + 'prefixHash' => $prefixHash, + ]; + + Container::getInstance() + ->get('view') + ->addNamespace($prefixHash, $path); + } } diff --git a/src/View/Compiler/ComponentTagCompiler.php b/src/View/Compiler/ComponentTagCompiler.php index 30969c4..39444ec 100644 --- a/src/View/Compiler/ComponentTagCompiler.php +++ b/src/View/Compiler/ComponentTagCompiler.php @@ -2,8 +2,6 @@ namespace Beebmx\View\Compiler; -use Beebmx\View\AnonymousComponent; -use Beebmx\View\DynamicComponent; use Illuminate\Container\Container; use Illuminate\Support\Str; use Illuminate\View\Compilers\ComponentTagCompiler as TagCompiler; @@ -11,40 +9,6 @@ class ComponentTagCompiler extends TagCompiler { - /** - * Compile the Blade component string for the given component and attributes. - * - * - * @throws \InvalidArgumentException - */ - protected function componentString(string $component, array $attributes): string - { - $class = $this->componentClass($component); - - [$data, $attributes] = $this->partitionDataAndAttributes($class, $attributes); - - $data = $data->mapWithKeys(function ($value, $key) { - return [Str::camel($key) => $value]; - }); - - // If the component doesn't exists as a class we'll assume it's a class-less - // component and pass the component as a view parameter to the data so it - // can be accessed within the component and we can render out the view. - if (! class_exists($class)) { - $parameters = [ - 'view' => "'$class'", - 'data' => '['.$this->attributesToString($data->all(), $escapeBound = false).']', - ]; - - $class = AnonymousComponent::class; - } else { - $parameters = $data->all(); - } - - return "##BEGIN-COMPONENT-CLASS##@component('{$class}', '{$component}', [".$this->attributesToString($parameters, $escapeBound = false).']) -withAttributes(['.$this->attributesToString($attributes->all(), $escapeAttributes = $class !== DynamicComponent::class).']); ?>'; - } - /** * Get the component class for a given component alias. * @@ -69,49 +33,21 @@ public function componentClass(string $component): string ); } - $guess = 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; - } - }); + if ($class = $this->findClassByComponent($component)) { + return $class; + } - if (! is_null($guess)) { + 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}]." ); } - - /** - * Convert an array of attributes to a string. - * - * @param bool $escapeBound - */ - protected function attributesToString(array $attributes, $escapeBound = true): string - { - return collect($attributes) - ->map(function (string $value, string $attribute) use ($escapeBound) { - return $escapeBound && isset($this->boundAttributes[$attribute]) && $value !== 'true' && ! is_numeric($value) - ? "'{$attribute}' => \Beebmx\View\Compiler\BladeCompiler::sanitizeComponentAttribute({$value})" - : "'{$attribute}' => {$value}"; - }) - ->implode(','); - } } diff --git a/src/View/Concerns/ManagesLayouts.php b/src/View/Concerns/ManagesLayouts.php deleted file mode 100644 index f889d39..0000000 --- a/src/View/Concerns/ManagesLayouts.php +++ /dev/null @@ -1,51 +0,0 @@ -sectionStack[] = $section; - } - } else { - $this->extendSection($section, $content instanceof View ? $content : b($content)); - } - } - - /** - * Get the string contents of a section. - * - * @param string $section - * @param string $default - */ - public function yieldContent($section, $default = ''): string - { - $sectionContent = $default instanceof View ? $default : b($default); - if (isset($this->sections[$section])) { - $sectionContent = $this->sections[$section]; - } - $sectionContent = str_replace('@@parent', '--parent--holder--', $sectionContent); - - return str_replace( - '--parent--holder--', - '@parent', - str_replace(static::parentPlaceholder($section), '', $sectionContent) - ); - } -} diff --git a/src/View/DynamicComponent.php b/src/View/DynamicComponent.php index d0b332c..12092e1 100644 --- a/src/View/DynamicComponent.php +++ b/src/View/DynamicComponent.php @@ -3,76 +3,21 @@ namespace Beebmx\View; use Beebmx\View\Compiler\ComponentTagCompiler; -use Closure; use Illuminate\Container\Container; -use Illuminate\View\Compilers\BladeTagCompiler; use Illuminate\View\DynamicComponent as Dynamic; class DynamicComponent extends Dynamic { - public function render(): Closure - { - $template = <<<'EOF' -getAttributes())->mapWithKeys(function ($value, $key) { return [Illuminate\Support\Str::camel(str_replace([':', '.'], ' ', $key)) => $value]; })->all(), EXTR_SKIP); ?> -{{ props }} - -{{ slots }} -{{ defaultSlot }} - -EOF; - - return function ($data) use ($template) { - $bindings = $this->bindings($class = $this->classForComponent()); - - return str_replace( - [ - '{{ component }}', - '{{ props }}', - '{{ bindings }}', - '{{ attributes }}', - '{{ slots }}', - '{{ defaultSlot }}', - ], - [ - $this->component, - $this->compileProps($bindings), - $this->compileBindings($bindings), - class_exists($class) ? '{{ $attributes }}' : '', - '{{ $slot ?? "" }}', - ], - $template - ); - }; - } - - protected function compiler(): ComponentTagCompiler|BladeTagCompiler + protected function compiler(): ComponentTagCompiler { if (! static::$compiler) { static::$compiler = new ComponentTagCompiler( - Container::getInstance()->get('blade.compiler')->getClassComponentAliases(), - Container::getInstance()->get('blade.compiler')->getClassComponentNamespaces(), - Container::getInstance()->get('blade.compiler') + Container::getInstance()->make('blade.compiler')->getClassComponentAliases(), + Container::getInstance()->make('blade.compiler')->getClassComponentNamespaces(), + Container::getInstance()->make('blade.compiler') ); } return static::$compiler; } - - protected function createBladeViewFromString($factory, $contents): string - { - $factory->addNamespace( - '__components', - $directory = Container::getInstance()->get('config')['view.compiled'] - ); - - if (! is_file($viewFile = $directory.'/'.sha1($contents).'.blade.php')) { - if (! is_dir($directory)) { - mkdir($directory, 0755, true); - } - - file_put_contents($viewFile, $contents); - } - - return '__components::'.basename($viewFile, '.blade.php'); - } } diff --git a/src/View/Factory.php b/src/View/Factory.php deleted file mode 100644 index 5eb9823..0000000 --- a/src/View/Factory.php +++ /dev/null @@ -1,48 +0,0 @@ -finder = $finder; - $this->events = $events; - $this->engines = $engines; - - $this->share('__env', $this); - } - - /** - * Get the data for the given component. - */ - protected function componentData(): array - { - $defaultSlot = new HtmlString(trim(ob_get_clean())); - - $slots = array_merge([ - '__default' => $defaultSlot, - ], $this->slots[count($this->componentStack)]); - - return array_merge( - $this->componentData[count($this->componentStack)], - ['slot' => $defaultSlot], - $this->slots[count($this->componentStack)], - ); - } -} diff --git a/src/View/ViewServiceProvider.php b/src/View/ViewServiceProvider.php index fb7e43e..164bf48 100644 --- a/src/View/ViewServiceProvider.php +++ b/src/View/ViewServiceProvider.php @@ -7,18 +7,19 @@ class ViewServiceProvider extends ViewProvider { - protected function createFactory($resolver, $finder, $events): Factory - { - return new Factory($resolver, $finder, $events); - } - /** * Register the Blade compiler implementation. */ public function registerBladeCompiler(): void { $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); }); });