From 17d7bdd76616b02889a8e53bf5beeea1e33722d1 Mon Sep 17 00:00:00 2001 From: COQUARD Cyrille Date: Thu, 22 May 2025 10:05:37 +0200 Subject: [PATCH] Added a first draft to give an overlook of how providers could be introduced. --- basic-scaffold.php | 89 +------------- configs/providers.php | 5 + src/BasicScaffoldPlugin.php | 140 ++++++++++++---------- src/BasicScaffoldPluginFactory.php | 4 +- src/Infrastructure/Provider.php | 15 +++ src/Infrastructure/ServiceBasedPlugin.php | 4 +- src/SampleSubsystem/Provider.php | 37 ++++++ src/boot.php | 93 ++++++++++++++ 8 files changed, 231 insertions(+), 156 deletions(-) create mode 100644 configs/providers.php create mode 100644 src/Infrastructure/Provider.php create mode 100644 src/SampleSubsystem/Provider.php create mode 100644 src/boot.php diff --git a/basic-scaffold.php b/basic-scaffold.php index 866e821..4123c0b 100644 --- a/basic-scaffold.php +++ b/basic-scaffold.php @@ -25,26 +25,6 @@ * License: MIT * License URI: https://opensource.org/licenses/MIT */ - -declare( strict_types=1 ); - -namespace MWPD\BasicScaffold; - -/* - * This is the plugin's bootstrap file. It serves three main purposes: - * 1. Provide the plugin meta information that WordPress needs; - * 2. Prepare the environment so that it is ready to execute our OOP code; - * 3. Instantiate and kick off our "composition root" (our 'Plugin' class). - * - * The bootstrap file should not do anything else, so that we have a clean - * separation between a.) code that needs to be run sequentially and produces - * side-effects and b.) declarations that can be taken out of contexts for - * testing and reuse and have no side-effects. - * - * Anything past this bootstrap file should be autoloadable classes, interfaces - * or traits without any side-effects. - */ - /* * As this is the only PHP file having side effects, we need to provide a * safeguard, so we want to make sure this file is only run from within @@ -54,71 +34,6 @@ die(); } +require_once __DIR__ . '/src/boot.php'; - -/*------------------------------------------------------------------------------ - *-- 2. Prepare the environment so that it is ready to execute our OOP code. -- - *----------------------------------------------------------------------------*/ - -/* - * We try to load the Composer if it exists. - * If it doesn't exist, we fall back to a basic bundled autoloader - * implementation. This allows us to just use the plugin as-is without requirin - * the 'composer install' step. - * Note: If you use Composer not only for autoloading, but also including - * dependencies needed in production, the 'composer install' becomes mandatory - * and the fallback autoloader should probably be removed. - */ -$composer_autoloader = __DIR__ . '/vendor/autoload.php'; - -if ( is_readable ( $composer_autoloader ) ) { - require $composer_autoloader; -} - -if ( ! class_exists( __NAMESPACE__ . '\\PluginFactory' ) ) { - // Composer autoloader apparently was not found, so fall back to our bundled - // autoloader. - require_once __DIR__ . '/src/Infrastructure/Autoloader.php'; - - ( new Infrastructure\Autoloader() ) - ->add_namespace( __NAMESPACE__, __DIR__ . '/src' ) - ->register(); -} - - - -/*------------------------------------------------------------------------------ - *-- 3. Instantiate and kick off our "composition root" (our "Plugin" class). -- - *----------------------------------------------------------------------------*/ - -/* - * We use a factory to instantiate the actual plugin. - * - * The factory keeps the object as a shared instance, so that you can also - * get outside access to that same plugin instance through the factory. - * - * This is similar to a Singleton, but without all the drawbacks the Singleton - * anti-pattern brings along. - * - * For more information on why to avoid a Singleton, - * @see https://www.alainschlesser.com/singletons-shared-instances/ - */ -$plugin = BasicScaffoldPluginFactory::create(); - -/* - * We register activation and deactivation hooks by using closures, as these - * need static access to work correctly. - */ -\register_activation_hook( __FILE__, function () use ( $plugin ) { - $plugin->activate(); -} ); - -\register_deactivation_hook( __FILE__, function () use ( $plugin ) { - $plugin->deactivate(); -} ); - -/* - * Finally, we run the plugin's register method to Hook the plugin into the - * WordPress request lifecycle. - */ -$plugin->register(); +\MWPD\BasicScaffold\boot(__DIR__); \ No newline at end of file diff --git a/configs/providers.php b/configs/providers.php new file mode 100644 index 0000000..42641e1 --- /dev/null +++ b/configs/providers.php @@ -0,0 +1,5 @@ +providers = $providers; + } /** * Get the list of services to register. @@ -55,12 +62,21 @@ final class BasicScaffoldPlugin extends ServiceBasedPlugin { * names or callables. */ protected function get_service_classes(): array { - return [ - self::SAMPLE_BACKEND_SERVICE_ID => SampleBackendService::class, - self::SAMPLE_LOOP_SERVICE_ID => SampleLoopService::class, - // Add your service definitions here. - ]; + $services = []; + + foreach ($this->providers as $provider) { + /** + * @var Provider $provider + */ + $provider = new $provider(); + $services = array_merge( + $services, + $provider->get_service_classes(), + ); + } + + return $services; } /* @@ -88,12 +104,20 @@ protected function get_service_classes(): array { * callables. */ protected function get_bindings(): array { - return [ - // Map the ViewFactory interface to a concrete implementation. - ViewFactory::class => TemplatedViewFactory::class, + $bindings = []; - // Add your bindings here. - ]; + foreach ($this->providers as $provider) { + /** + * @var Provider $provider + */ + $provider = new $provider(); + $bindings = array_merge( + $bindings, + $provider->get_bindings(), + ); + } + + return $bindings; } /** @@ -108,15 +132,20 @@ protected function get_bindings(): array { * argument names to argument values. */ protected function get_arguments(): array { - return [ + $arguments = []; - /* - * Example - add a scalar value to an argument for SampleService: - * SampleService::class => [ 'argument_name' => 'value' ], + foreach ($this->providers as $provider) { + /** + * @var Provider $provider */ - - // Add your argument mappings here. - ]; + $provider = new $provider(); + $arguments = array_merge( + $arguments, + $provider->get_arguments(), + ); + } + + return $arguments; } /** @@ -132,15 +161,20 @@ protected function get_arguments(): array { * @return array Array of fully qualified class names. */ protected function get_shared_instances(): array { - return [ + $shared_instances = []; - /* - * Example - make SampleService be shared amongst instantiations: - * SampleService::class, + foreach ($this->providers as $provider) { + /** + * @var Provider $provider */ - - // Add your shared instances here. - ]; + $provider = new $provider(); + $shared_instances = array_merge( + $shared_instances, + $provider->get_shared_instances(), + ); + } + + return $shared_instances; } /** @@ -155,43 +189,19 @@ protected function get_shared_instances(): array { * @return array Associative array of callables. */ protected function get_delegations(): array { - return [ + $delegations = []; - /* - * Add a factory for instantiating WP_Post objects: - * \WP_Post::class => function () { - * return \get_post( \get_the_ID() ); }, + foreach ($this->providers as $provider) { + /** + * @var Provider $provider */ - - // Add your delegations here. - ]; + $provider = new $provider(); + $delegations = array_merge( + $delegations, + $provider->get_delegations(), + ); + } + + return $delegations; } - - /* - * -------------------------------------------------------------------------- - * -- 3. Define prefixes and identifiers for outside access. -- - * -------------------------------------------------------------------------- - */ - - /* - * Prefixes to use. - * - * These are provided so that if multiple plugins use the same boilerplate - * code, there hooks and service identifiers are scoped and don't clash. - */ - protected const HOOK_PREFIX = 'mwpd.basic_scaffold.'; - - protected const SERVICE_PREFIX = 'mwpd.basic_scaffold.'; - - /* - * Service identifiers we know about. - * - * These can be used from outside code as well to directly refer to a - * service when talking to the service container. - */ - public const VIEW_FACTORY_ID = self::SERVICE_PREFIX . 'view-factory'; - - public const SAMPLE_BACKEND_SERVICE_ID = self::SERVICE_PREFIX . 'sample-subsystem.sample-backend-service'; - - public const SAMPLE_LOOP_SERVICE_ID = self::SERVICE_PREFIX . 'sample-subsystem.sample-loop-service'; } diff --git a/src/BasicScaffoldPluginFactory.php b/src/BasicScaffoldPluginFactory.php index a044d6f..f73f38a 100644 --- a/src/BasicScaffoldPluginFactory.php +++ b/src/BasicScaffoldPluginFactory.php @@ -35,7 +35,7 @@ final class BasicScaffoldPluginFactory { * * @return Plugin Plugin instance. */ - public static function create(): Plugin { + public static function create(array $providers): Plugin { /** * We use a static variable to ensure that the plugin is only instantiated * once. This is important for performance reasons and to ensure that the @@ -49,7 +49,7 @@ public static function create(): Plugin { static $plugin = null; if ( null === $plugin ) { - $plugin = new BasicScaffoldPlugin(); + $plugin = new BasicScaffoldPlugin( $providers ); } return $plugin; diff --git a/src/Infrastructure/Provider.php b/src/Infrastructure/Provider.php new file mode 100644 index 0000000..979f65b --- /dev/null +++ b/src/Infrastructure/Provider.php @@ -0,0 +1,15 @@ + SampleBackendService::class, + 'mwpd.basic_scaffold.sample-subsystem.sample-loop-service' => SampleLoopService::class, + ]; + } +} diff --git a/src/boot.php b/src/boot.php new file mode 100644 index 0000000..40df9df --- /dev/null +++ b/src/boot.php @@ -0,0 +1,93 @@ +add_namespace( __NAMESPACE__, $plugin_directory . '/src' ) + ->register(); + } + + + + /*------------------------------------------------------------------------------ + *-- 3. Instantiate and kick off our "composition root" (our "Plugin" class). -- + *----------------------------------------------------------------------------*/ + + + $providers = require $plugin_directory . DIRECTORY_SEPARATOR . 'configs' . DIRECTORY_SEPARATOR . 'providers.php'; + + /* + * We use a factory to instantiate the actual plugin. + * + * The factory keeps the object as a shared instance, so that you can also + * get outside access to that same plugin instance through the factory. + * + * This is similar to a Singleton, but without all the drawbacks the Singleton + * anti-pattern brings along. + * + * For more information on why to avoid a Singleton, + * @see https://www.alainschlesser.com/singletons-shared-instances/ + */ + $plugin = BasicScaffoldPluginFactory::create($providers); + + /* + * We register activation and deactivation hooks by using closures, as these + * need static access to work correctly. + */ + \register_activation_hook( $plugin_file, function () use ( $plugin ) { + $plugin->activate(); + } ); + + \register_deactivation_hook( $plugin_file, function () use ( $plugin ) { + $plugin->deactivate(); + } ); + + /* + * Finally, we run the plugin's register method to Hook the plugin into the + * WordPress request lifecycle. + */ + $plugin->register(); +}