You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This proposal aims to spark discussion for a revision of the current state of modules. The main goal is to create 2 primary packages:
one for module-providing packages
one for module-consumer packages
Additionally, 2 optional utility packages are also being proposed.
2. Motivation
(a) To have reusable modules requires: (b) Modules to be box-able requires: (c) Services to expose their dependencies requires: (d) Ditching the service provider spec
Modules currently cannot be reused by applications. This is solely due to the fact that a module would conflict with itself. The solution is to namespace each module such that different instances of the same module class each exist within their own namespace, or "box".
(b) Module Boxing
The term "module boxing" refers to the process of prefixing a module's factory and extension keys in order to namespace them. The result of this process is referred to as a "boxed module".
... then the $c->get() calls inside each factory or extension will fail. In the above example, $c->get('name') will fail because the corresponding service was renamed to prefix/name.
Therefore, each service's dependencies must also be prefixed.
(c) Exposed Dependencies
If a service's dependencies are exposed by the service itself, a consumer algorithm could manipulate the module's factories and extensions to produce new ones. This can be achieved by giving each service a getDependencies() method and a withDependencies() method.
Consider the interface ServiceInterface that provides the aforementioned methods ...
However, the current module standard is dependent on the service provider spec, which only guarantees services to be callable. This means that a boxing algorithm would need to perform instanceof ServiceInterface checks to determine if a service is capable of exposing its dependencies and being cloned, which makes module boxing be unreliable and non-standard.
(d) Ditching Service Provider
If a module is not forced to return a service provider from setup(), but instead provide factories and extensions through its own methods, then module boxing would be possible.
Furthermore, while the standard would not directly implement the service provider spec, it would still be compatible with it. Since a ServiceInterface is invocable, each service qualifies as a callable, meaning modules can be transformed into service providers via a simple decorator:
Module boilerplate code could be drastically reduced simply thanks to the fact that commonly duplicated logic could be extracted into specialized ServiceInterface instances. The below are a few examples which I hope are fairly self-explanatory:
[
// Call a constructor and inject services as arguments'cron/interval' => newValue(60),
'cron/job' => newConstructor(CronJob::class, ['interval']),
// Reference to a service outside this module, with a default'logger' => newExternal('app/logger', function () {
returnnewNullLogger();
}),
// Switch between two services, based on the value of a third'is_dev' => newValue(false),
'log_level' => newTernaryValue('is_dev', 'ALL', 'ERROR'),
// Builds a string using services'admin' => newValue('Bob Page'),
'greeting' => newFormattedString("Hello {name}", [
'name' => 'users/admin',
]),
// Returns the function instead of calling it'callback' => newInvocable(['greeting'], function($greeting) {
echo"<p>$greeting</p>";
}),
// Compile services into arrays'db/host' => newValue('localhost'),
'db/port' => newValue(3306),
'db' => newArrayValue([
'host' => 'db/host'
'port' => 'db/port'
'cron' => [
'interval' => 'cron/interval'
]
]),
]
(f) Module static analysis
One of the benefits of exposing dependencies for each service is the ability to create service dependency graphs for one or more modules. This would allow an analysis algorithm to theoretically be able to do the following:
Detect references to services that do not exist
Detect direct references to services outside of the insepected module
Detect services that are not being used
Detect circular referencing
Produce service heat maps
Produce dependency graphs
Perhaps even use the dependency graph to add IDE integration
3. Proposed Packages
Module Standard
The package that provides the module standard. This can be considered the minimum requirement for packages that provide modules.
Package: dhii/modules
Contents:
ModuleInterface
ServiceInterface
Factory
Extension
Module System
A fully working and standalone module system for packages that consume modules.
1. Introduction
This proposal aims to spark discussion for a revision of the current state of modules. The main goal is to create 2 primary packages:
Additionally, 2 optional utility packages are also being proposed.
2. Motivation
(a) To have reusable modules
requires:
(b) Modules to be box-able
requires:
(c) Services to expose their dependencies
requires:
(d) Ditching the service provider spec
Bonus consequences:
(e) Less module boilerplate code
(f) Module static analysis
(a) Reusable modules
Modules currently cannot be reused by applications. This is solely due to the fact that a module would conflict with itself. The solution is to namespace each module such that different instances of the same module class each exist within their own namespace, or "box".
(b) Module Boxing
The term "module boxing" refers to the process of prefixing a module's factory and extension keys in order to namespace them. The result of this process is referred to as a "boxed module".
However, if a module such as the one below ...
... is prefixed as illustrated below ...
... then the
$c->get()
calls inside each factory or extension will fail. In the above example,$c->get('name')
will fail because the corresponding service was renamed toprefix/name
.Therefore, each service's dependencies must also be prefixed.
(c) Exposed Dependencies
If a service's dependencies are exposed by the service itself, a consumer algorithm could manipulate the module's factories and extensions to produce new ones. This can be achieved by giving each service a
getDependencies()
method and awithDependencies()
method.Consider the interface
ServiceInterface
that provides the aforementioned methods ...... and a
Factory
class that implements this interface:A boxing algorithm could then prefix the above services and yield:
However, the current module standard is dependent on the service provider spec, which only guarantees services to be
callable
. This means that a boxing algorithm would need to performinstanceof ServiceInterface
checks to determine if a service is capable of exposing its dependencies and being cloned, which makes module boxing be unreliable and non-standard.(d) Ditching Service Provider
If a module is not forced to return a service provider from
setup()
, but instead provide factories and extensions through its own methods, then module boxing would be possible.Furthermore, while the standard would not directly implement the service provider spec, it would still be compatible with it. Since a
ServiceInterface
is invocable, each service qualifies as acallable
, meaning modules can be transformed into service providers via a simple decorator:(e) Less Module Boilerplate Code
Module boilerplate code could be drastically reduced simply thanks to the fact that commonly duplicated logic could be extracted into specialized
ServiceInterface
instances. The below are a few examples which I hope are fairly self-explanatory:(f) Module static analysis
One of the benefits of exposing dependencies for each service is the ability to create service dependency graphs for one or more modules. This would allow an analysis algorithm to theoretically be able to do the following:
3. Proposed Packages
Module Standard
The package that provides the module standard. This can be considered the minimum requirement for packages that provide modules.
Package:
dhii/modules
Contents:
ModuleInterface
ServiceInterface
Factory
Extension
Module System
A fully working and standalone module system for packages that consume modules.
Package:
dhii/module-system
Contents:
CompositeModule
ModuleContainer
ModuleServiceProvider
BoxedModule
(for decorating modules, allowing multi-boxing)ModuleLoader
(for storing a list of modules in static files)Module Helpers
Optional collection of helpers for packages that provide modules.
Package:
dhii/module-helpers
Contents:
A plethora of
ServiceInterface
implementations.Module CLI
Optional tool for packages that provide multiple modules or consume modules.
Package:
dhii/modules-cli
Contents:
A variety of CLI tools for module static analysis, managing module lists, generating dependency graphs, etc.
The text was updated successfully, but these errors were encountered: