-
-
Notifications
You must be signed in to change notification settings - Fork 185
feat: Plugin extension autoloader #7035
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop-minor
Are you sure you want to change the base?
Changes from all commits
511fbf0
5787d39
56b9599
011d532
27d7387
96694ee
c81b09c
e5291be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| use Kirby\Image\Image; | ||
| use Kirby\Plugin\License; | ||
| use Kirby\Plugin\Plugin; | ||
| use Kirby\Plugin\Autoloader; | ||
| use Kirby\Text\KirbyTag; | ||
| use Kirby\Toolkit\A; | ||
| use Kirby\Toolkit\Collection as ToolkitCollection; | ||
|
|
@@ -871,18 +872,34 @@ public static function plugin( | |
| string|null $root = null, | ||
| string|null $version = null, | ||
| Closure|string|array|null $license = null, | ||
| bool|string $autoloader = false | ||
| ): Plugin|null { | ||
| if ($extends === null) { | ||
|
|
||
|
|
||
| if ($extends === null && $autoloader === false) { | ||
| return static::$plugins[$name] ?? null; | ||
| } | ||
|
|
||
| $root ??= $extends['root'] ?? dirname(debug_backtrace()[0]['file']); | ||
|
|
||
| if ($autoloader) { | ||
|
|
||
| //Allow to apply custom Autoloader | ||
| $autolader_class = is_bool($autoloader) ? Autoloader::class : $autoloader; | ||
|
|
||
| $extends = A::merge($autolader_class::load( | ||
| name: $name, | ||
| root: $root | ||
| ), $extends ?? []); | ||
| } | ||
|
|
||
| $plugin = new Plugin( | ||
| name: $name, | ||
| name: $name, | ||
| extends: $extends, | ||
| info: $info, | ||
| info: $info, | ||
| license: $license, | ||
| // TODO: Remove fallback to $extends in v7 | ||
| root: $root ?? $extends['root'] ?? dirname(debug_backtrace()[0]['file']), | ||
| root: $root, | ||
| version: $version | ||
| ); | ||
|
|
||
|
|
@@ -925,6 +942,11 @@ public function plugins(array|null $plugins = null): array | |
| return static::$plugins; | ||
| } | ||
|
|
||
| public function allowedExtensionsKeys(): array | ||
| { | ||
| return array_keys($this->extensions); | ||
| } | ||
|
|
||
|
Comment on lines
+945
to
+949
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to base the logic on the available extensions. It might seem like a good idea to make it dynamic based on this, but some verbosity will go a long way here for understanding and maintaining this (even with a bit more effort). Look at classes like |
||
| /** | ||
| * Loads all plugins from site/plugins | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,290 @@ | ||
| <?php | ||
|
|
||
| namespace Kirby\Plugin; | ||
|
|
||
| /** | ||
| * Autoloader | ||
| */ | ||
|
|
||
| use Closure; | ||
| use Kirby\Filesystem\Dir; | ||
| use Kirby\Toolkit\A; | ||
| use Kirby\Data\Data; | ||
| use Kirby\Exception\Exception; | ||
| use Kirby\Exception\InvalidArgumentException; | ||
| use Kirby\Filesystem\F; | ||
| use Kirby\Toolkit\Str; | ||
| use Throwable; | ||
|
|
||
| class Autoloader | ||
| { | ||
|
|
||
| public array $data = []; | ||
|
|
||
| /** | ||
| * @param string $name Plugin name | ||
| * @param string $root The root path of the plugin | ||
| * @param array $data Predefined extension data to extend | ||
| * @return void|$this | ||
| */ | ||
| public function __construct( | ||
| public string $name, | ||
| public string $root | ||
| ) { | ||
|
|
||
| //Load classes before everything else | ||
| $classfolder = $root . '/classes/'; | ||
|
|
||
| if (Dir::exists($classfolder)) { | ||
| $this->loadClasses($classfolder); | ||
| } | ||
|
|
||
| $folders = [ | ||
| 'autoload' => 'loadAutoload', | ||
| 'blueprints' => 'loadBlueprints', | ||
| 'i18n' => 'loadTranslations', | ||
| 'fields' => 'loadFields', | ||
| 'sections' => 'loadSections', | ||
| 'snippets' => 'loadSnippets', | ||
| 'templates' => 'loadTemplates' | ||
| ]; | ||
|
|
||
| foreach (Dir::dirs($this->root) as $dir) { | ||
| if (array_key_exists($dir, $folders)) { | ||
| $method = $folders[$dir]; | ||
| $this->$method($this->root . '/' . $dir . '/'); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Autoload autoload | ||
| * @param string $root | ||
| * @return void | ||
| */ | ||
| public function loadAutoload(string $root): void | ||
| { | ||
| foreach (Dir::files($root) as $path) { | ||
|
|
||
| $key = F::name($path); | ||
| $file = $root . $path; | ||
| $this->data[$key] = Data::read($file); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Autoload blueprints | ||
| * @param string $root | ||
| * @return void | ||
| */ | ||
| public function loadBlueprints(string $root): void | ||
| { | ||
| foreach (Dir::index($root) as $path) { | ||
|
|
||
| $file = $root . $path; | ||
|
|
||
| //Skip folder and file/folder thats starts with '_' | ||
| if (F::exists($file) === false || Str::contains($path, '/_')) { | ||
| continue; | ||
| } | ||
|
|
||
| //Has no Subfolder | ||
| if (($dirname = F::dirname($path)) === '.') { | ||
| $key = F::name($path); | ||
| } | ||
|
|
||
| $key ??= $dirname . '/' . F::name($path); | ||
|
|
||
| $this->data['blueprints'][$key] = Data::read($file); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Load classes | ||
| * @param string $root | ||
| * @return void | ||
| */ | ||
| public function loadClasses(string $root): void | ||
| { | ||
|
|
||
| $classes = []; | ||
|
|
||
| foreach (Dir::index($root) as $path) { | ||
|
|
||
| $file = $root . $path; | ||
|
|
||
| //Skip folder and file/folder thats starts with '_' | ||
| if (F::exists($file) === false || Str::contains($path, '/_')) { | ||
| continue; | ||
| } | ||
|
|
||
| //Has no Subfolder | ||
| if (($dirname = F::dirname($path)) === '.') { | ||
| $key = F::name($path); | ||
| } | ||
|
|
||
| $key ??= $dirname . '/' . F::name($path); | ||
|
|
||
| $prefix = array_map('ucfirst', explode('/', $this->name)); | ||
| $classname = A::merge($prefix, explode('/', $key)); | ||
| $classes[implode('\\', $classname)] = $file; | ||
| }; | ||
|
|
||
| F::loadClasses($classes); | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Autoload translations | ||
| * @param string $root | ||
| * @return void | ||
| */ | ||
| public function loadTranslations(string $root): void | ||
| { | ||
| foreach (Dir::index($root) as $path) { | ||
|
|
||
| $file = $root . $path; | ||
|
|
||
| //Skip folder and file/folder thats starts with '_' | ||
| if (F::exists($file) === false || Str::contains($path, '/_')) { | ||
| continue; | ||
| } | ||
|
|
||
| //Has no Subfolder | ||
| if (($dirname = F::dirname($path)) === '.') { | ||
| $key = F::name($path); | ||
| } | ||
|
|
||
| $key ??= $dirname . '/' . F::name($path); | ||
|
|
||
| $this->data['translations'][$key] = Data::read($file); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Autoload fields | ||
| * @param string $root | ||
| * @return void | ||
| */ | ||
| public function loadFields(string $root): void | ||
| { | ||
| foreach (Dir::index($root) as $path) { | ||
|
|
||
| $file = $root . $path; | ||
|
|
||
| //Skip folder and file/folder thats starts with '_' | ||
| if (F::exists($file) === false || Str::contains($path, '/_')) { | ||
| continue; | ||
| } | ||
|
|
||
| //Has no Subfolder | ||
| if (($dirname = F::dirname($path)) === '.') { | ||
| $key = F::name($path); | ||
| } | ||
|
|
||
| $key ??= $dirname . '/' . F::name($path); | ||
|
|
||
| $this->data['fields'][$key] = Data::read($file); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Autoload sections | ||
| * @param string $root | ||
| * @return void | ||
| */ | ||
| public function loadSections(string $root): void | ||
| { | ||
| foreach (Dir::index($root) as $path) { | ||
|
|
||
| $file = $root . $path; | ||
|
|
||
| //Skip folder and file/folder thats starts with '_' | ||
| if (F::exists($file) === false || Str::contains($path, '/_')) { | ||
| continue; | ||
| } | ||
|
|
||
| //Has no Subfolder | ||
| if (($dirname = F::dirname($path)) === '.') { | ||
| $key = F::name($path); | ||
| } | ||
|
|
||
| $key ??= $dirname . '/' . F::name($path); | ||
|
|
||
| $this->data['sections'][$key] = Data::read($file); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Autoload snippets | ||
| * @param string $root | ||
| * @return void | ||
| */ | ||
| public function loadSnippets(string $root): void | ||
| { | ||
| foreach (Dir::index($root) as $path) { | ||
|
|
||
| $file = $root . $path; | ||
|
|
||
| //Skip folder and file/folder thats starts with '_' | ||
| if (F::exists($file) === false || Str::contains($path, '/_')) { | ||
| continue; | ||
| } | ||
|
|
||
| //Has no Subfolder | ||
| if (($dirname = F::dirname($path)) === '.') { | ||
| $key = F::name($path); | ||
| } | ||
|
|
||
| $key ??= $dirname . '/' . F::name($path); | ||
|
|
||
| $this->data['snippets'][$key] = $file; | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Autoload templates | ||
| * @param string $root | ||
| * @return void | ||
| */ | ||
| public function loadTemplates(string $root): void | ||
| { | ||
| foreach (Dir::index($root) as $path) { | ||
|
|
||
| $file = $root . $path; | ||
|
|
||
| //Skip folder and file/folder thats starts with '_' | ||
| if (F::exists($file) === false || Str::contains($path, '/_')) { | ||
| continue; | ||
| } | ||
|
|
||
| //Has no Subfolder | ||
| if (($dirname = F::dirname($path)) === '.') { | ||
| $key = F::name($path); | ||
| } | ||
|
|
||
| $key ??= $dirname . '/' . F::name($path); | ||
|
|
||
| $this->data['templates'][$key] = $file; | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Run autoloader and return the results | ||
| * @param mixed ...$params | ||
| * @return array | ||
| */ | ||
| public static function load(...$params): array | ||
| { | ||
| $self = new self(...$params); | ||
| return $self->toArray(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the autoloaded data | ||
| * @return array */ | ||
| public function toArray(): array | ||
| { | ||
| return $this->data; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question:What for would one use a custom autoloader class?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example: In one of my cases i used it to extend the
$this->extend()with data from a Class (Which is loading in the autoloader). So i made a custom Autoloader class and modified thetoArray()method.An other case could be, that you need to load snippets from another folder than snippets.