Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough

Instantiate all classes within a namespace in Symfony and Drupal

Parent Feed: 

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. class PluginLoader {

  3. /**

  4.   * Loads all plugins.

  5.   *

  6.   * @param string $namespace

  7.   * Namespace required for a class to be considered a plugin.

  8.   * @param string $search_root_path

  9.   * Search classes recursively starting from this folder.

  10.   * The default is the folder this here class resides in.

  11.   *

  12.   * @return object[]

  13.   * Array of instantiated plugins

  14.   */

  15. public static function loadPlugins(string $namespace, string $search_root_path = __DIR__): array {
  16. $finder = new Finder();

  17. $finder->files()->in($search_root_path)->name('*.php');

  18. foreach ($finder as $file) {

  19. $class_name = rtrim($namespace, '\\') . '\\' . $file->getFilenameWithoutExtension();
  20. try {

  21. $plugins[] = new $class_name();

  22. }

  23. catch (\Throwable $e) {

  24. continue;

  25. }

  26. }

  27. }

  28. return $plugins ?? [];

  29. }

  30. }

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!

Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web