Instantiate all classes within a namespace in Symfony and Drupal

03 Juni 2021

Occasionally I find myself needing plugin-like functionality, where users/downstream can throw a class into a folder and expect it to work. My script is supposed to find and instantiate these plugins during runtime without keeping track of their existence.

In a regular Drupal module, one would usually use the plugin architecture, but that comes with its overhead of boilerplate code and may not be the solution for the simplest of use cases.

Many class finder libraries rely on get_declared_classes() which may not be helpful, as the classes in question may not have been declared yet.

If you are on a Drupal 8/9 installation and want to use components already available to you, the Symfony (file) Finder can be an alternative for finding classes in a given namespace.

Installing dependencies

Ouside of Drupal 8/9, you may need to require this library in your application:

  1. composer require symfony/finder

A simple example

  1. use Symfony\Component\Finder\Finder;
  2.  
  3. class PluginLoader {
  4.  
  5. /**
  6.   * Loads all plugins.
  7.   *
  8.   * @param string $namespace
  9.   * Namespace required for a class to be considered a plugin.
  10.   * @param string $search_root_path
  11.   * Search classes recursively starting from this folder.
  12.   * The default is the folder this here class resides in.
  13.   *
  14.   * @return object[]
  15.   * Array of instantiated plugins
  16.   */
  17. public static function loadPlugins(string $namespace, string $search_root_path = __DIR__): array {
  18. $finder = new Finder();
  19. $finder->files()->in($search_root_path)->name('*.php');
  20. foreach ($finder as $file) {
  21. $class_name = rtrim($namespace, '\\') . '\\' . $file->getFilenameWithoutExtension();
  22. if (class_exists($class_name)) {
  23. try {
  24. $plugins[] = new $class_name();
  25. }
  26. catch (\Throwable $e) {
  27. continue;
  28. }
  29. }
  30. }
  31.  
  32. return $plugins ?? [];
  33. }
  34.  
  35. }

Usage

  1. $plugin_instances = PluginLoader::loadPlugins('\Some\Namespace');

This is just an abstract catch-all example with a couple of obvious problems which can be circumvented when using more specific code.

In the above example, the finder looks for all files with the .php extension within all folders in a given path. If it finds a class, it tries to instantiate it. The try-catch block is for it to not fail when trying to instantiate non-instantiatable classes, interfaces and similar.

The above can be improved upon by making assumptions about the class name (one could be looking for class files named *Plugin.php) and examining the file content (which the Finder component is capable of as well).

Let me know of other simple ways of tackling this problem!

Neuen Kommentar hinzufügen

Der Inhalt dieses Feldes wird nicht öffentlich zugänglich angezeigt.

Restricted HTML

  • Erlaubte HTML-Tags: <a href hreflang target> <em> <strong> <cite> <blockquote cite> <pre> <ul type> <ol start type> <li> <dl> <dt> <dd> <h4 id> <h5 id> <h6 id>
  • Zeilenumbrüche und Absätze werden automatisch erzeugt.
  • Website- und E-Mail-Adressen werden automatisch in Links umgewandelt.

Angebot innerhalb von 24 Stunden

Ob ein großes kommerzielles System, oder eine kleine Business Seite, wir schicken ein Angebot ab innerhalb von 24 Stunden nachdem Sie diese Taste drücken: Angebot anfordern