-
-
Notifications
You must be signed in to change notification settings - Fork 53
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
Drop module manager and add support for config aggregator #56
Drop module manager and add support for config aggregator #56
Conversation
02f72d5
to
b53dfe8
Compare
b53dfe8
to
86bcde3
Compare
This PR does not concern itself with code quality and intentionally avoids any changes that are not a bare necessity |
Signed-off-by: Aleksei Khudiakov <[email protected]>
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.
Overall, I think it's a nice step!
I think the one thing missing for me is a write-up in the patch description detailing:
- your proposal for where the config and/or container are built
- AND what each of those files look like
- what the
public/index.php
looks like
As somebody familiar with Mezzio (clearly!) those details seem obvious, but for somebody not familiar with them, having that information documented here will help them understand how we are evolving the MVC, and what it will look like once complete.
public function bootstrap(array $listeners = []) | ||
public function bootstrap() | ||
{ | ||
$serviceManager = $this->serviceManager; | ||
$events = $this->events; | ||
|
||
// Setup default listeners | ||
$listeners = array_unique(array_merge($this->defaultListeners, $listeners)); | ||
|
||
foreach ($listeners as $listener) { | ||
$serviceManager->get($listener)->attach($events); | ||
} |
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.
Why are you removing the ability to pass additional listeners to bootstrap()
?
To be clear, I'm fine with it (I don't think we document it anywhere), but you'll need to detail it as a feature removal in the changelog as well.
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.
Extra listeners are moved to the application factory since Application::init()
is going to be dropped.
real change here is that extra listeners to register would migrate from the application.config.php
to the merged config
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.
👍
/** | ||
* Static method for quick and easy initialization of the Application. | ||
* | ||
* If you use this init() method, you cannot specify a service with the | ||
* name of 'ApplicationConfig' in your service manager config. This name is | ||
* reserved to hold the array from application.config.php. | ||
* | ||
* The following services can only be overridden from application.config.php: | ||
* | ||
* - ModuleManager | ||
* - SharedEventManager | ||
* - EventManager & Laminas\EventManager\EventManagerInterface | ||
* | ||
* All other services are configured after module loading, thus can be | ||
* overridden by modules. | ||
* | ||
* @param array $configuration | ||
* @return Application | ||
*/ | ||
public static function init($configuration = []) | ||
{ | ||
// Prepare the service manager | ||
$smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : []; | ||
$smConfig = new Service\ServiceManagerConfig($smConfig); | ||
|
||
$serviceManager = new ServiceManager(); | ||
$smConfig->configureServiceManager($serviceManager); | ||
$serviceManager->setService('ApplicationConfig', $configuration); | ||
|
||
// Load modules | ||
$serviceManager->get('ModuleManager')->loadModules(); | ||
|
||
// Prepare list of listeners to bootstrap | ||
$listenersFromAppConfig = isset($configuration['listeners']) ? $configuration['listeners'] : []; | ||
$config = $serviceManager->get('config'); | ||
$listenersFromConfigService = isset($config['listeners']) ? $config['listeners'] : []; | ||
|
||
$listeners = array_unique(array_merge($listenersFromConfigService, $listenersFromAppConfig)); | ||
|
||
return $serviceManager->get('Application')->bootstrap($listeners); | ||
} | ||
|
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.
Why are you removing this method?
Removal of this MUST be called out in both the CHANGELOG and the documentation, as our own skeleton has consistently used this method in order to initialize and bootstrap the application from aggregated configuration. If it goes away, users need to know what they need to change in their public/index.php
.
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.
This method no longer provides utility it once did.
With the the module manager dropped, responsibility to setup the application pushed completely out of the scope of this package.
It is now entirely up to skeleton application to provide the default wiring. As initially outlined in the Zend\Mvc v4 RFC the goal is to move towards current mezzio skeleton application setup. Config+container proved to be flexible enough.
At this time I am working on a new feature that would somewhat approximate laminas-httphanderrunner and would affect how public/index.php
needs to be changed.
On a side note, I have no immediate intention to provide update for the laminas-modulemanager. I can easily see it retired unless there is a demand for the different set of features not available with the config aggregator.
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.
👍
'EventManagerAwareInitializer' => static function ($first, $second) { | ||
if ($first instanceof ContainerInterface) { | ||
$container = $first; | ||
$instance = $second; | ||
} else { | ||
$container = $second; | ||
$instance = $first; | ||
} | ||
|
||
if (! $instance instanceof EventManagerAwareInterface) { | ||
return; | ||
} | ||
|
||
$eventManager = $instance->getEventManager(); | ||
|
||
// If the instance has an EM WITH an SEM composed, do nothing. | ||
if ($eventManager instanceof EventManagerInterface | ||
&& $eventManager->getSharedManager() instanceof SharedEventManagerInterface | ||
) { | ||
return; | ||
} | ||
|
||
$instance->setEventManager($container->get('EventManager')); | ||
}, |
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.
We can simplify this by only allowing laminas-servicemanager v3+ and laminas-eventmanager v3+. Personally, I'd do that as part of this patch to simplify this part of the ConfigProvider.
Alternately, move that code into a dedicated method, and use Closure::fromCallable()
to provide it as an initializer.
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.
This change would be outside of the scope for the module manager removal.
I do not see a reason to keep EventManager initializer and expect to drop any initializers entirely as a part of the move towards generic PSR containers. I will need to double check container integrations supported by mezzio, I am not sure if it is something supported by all them.
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.
👍
$listeners = $container->get('config')[Application::class]['listeners'] ?? []; | ||
foreach ($listeners as $listener) { | ||
$container->get($listener)->attach($em); | ||
} |
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.
This answers part of my question about the changes to Application::bootstrap()
. However, you need to document this in the changelog and the manual.
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.
This is meant more of a placeholder at this point that is meant to preserve the functionality. Event system would need to have a more detailed revision and potential utilization of PSR-14 would change this part entirely.
One of the reasons why I opted for skipping documenting the changes in CHANGELOG and providing updated documentation by this PR.
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.
👍
{ | ||
// Configure URL view helper | ||
$urlFactory = $this->createUrlHelperFactory($services); | ||
$plugins->setFactory(ViewHelper\Url::class, $urlFactory); | ||
$plugins->setFactory('laminasviewhelperurl', $urlFactory); | ||
$managerConfig['aliases']['laminasviewhelperurl'] = ViewHelper\Url::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.
I think we can drop these "normalized" aliases - users should only be using the FCQN or the class-name-minus-namespace when retrieving helpers.
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.
That change would be outside of the scope of this PR. Too easy to lose track of small details like that during review.
I plan to drop all obsolete aliases in another PR.
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.
👍
…config Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
Signed-off-by: Aleksei Khudiakov <[email protected]>
e4e6b6c
to
0f36590
Compare
Application listeners should be registered by the time `Application` service is retrieved from container. Previously they were registered at the time of `Application::bootstrap()` call. Signed-off-by: Aleksei <[email protected]>
Migration path for existing users would need to be provided via separate package providing necessary bindings between module manager and mvc. |
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.
This looks good.
The only feedback I have is around usage of the laminas-servicemanager FactoryInterface
; I think we could drop explicitly implementing that interface, and just doing invokables, to help reduce ties to laminas-servicemanager. However, that change is likely out of scope for this PR.
public function bootstrap(array $listeners = []) | ||
public function bootstrap() | ||
{ | ||
$serviceManager = $this->serviceManager; | ||
$events = $this->events; | ||
|
||
// Setup default listeners | ||
$listeners = array_unique(array_merge($this->defaultListeners, $listeners)); | ||
|
||
foreach ($listeners as $listener) { | ||
$serviceManager->get($listener)->attach($events); | ||
} |
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.
👍
/** | ||
* Static method for quick and easy initialization of the Application. | ||
* | ||
* If you use this init() method, you cannot specify a service with the | ||
* name of 'ApplicationConfig' in your service manager config. This name is | ||
* reserved to hold the array from application.config.php. | ||
* | ||
* The following services can only be overridden from application.config.php: | ||
* | ||
* - ModuleManager | ||
* - SharedEventManager | ||
* - EventManager & Laminas\EventManager\EventManagerInterface | ||
* | ||
* All other services are configured after module loading, thus can be | ||
* overridden by modules. | ||
* | ||
* @param array $configuration | ||
* @return Application | ||
*/ | ||
public static function init($configuration = []) | ||
{ | ||
// Prepare the service manager | ||
$smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : []; | ||
$smConfig = new Service\ServiceManagerConfig($smConfig); | ||
|
||
$serviceManager = new ServiceManager(); | ||
$smConfig->configureServiceManager($serviceManager); | ||
$serviceManager->setService('ApplicationConfig', $configuration); | ||
|
||
// Load modules | ||
$serviceManager->get('ModuleManager')->loadModules(); | ||
|
||
// Prepare list of listeners to bootstrap | ||
$listenersFromAppConfig = isset($configuration['listeners']) ? $configuration['listeners'] : []; | ||
$config = $serviceManager->get('config'); | ||
$listenersFromConfigService = isset($config['listeners']) ? $config['listeners'] : []; | ||
|
||
$listeners = array_unique(array_merge($listenersFromConfigService, $listenersFromAppConfig)); | ||
|
||
return $serviceManager->get('Application')->bootstrap($listeners); | ||
} | ||
|
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.
👍
'request' => 'Request', | ||
'response' => 'Response', |
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.
I don't think the request and response should be services anymore in v4, but I'm unsure if that's in-scope for this patch.
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.
They definitely do not belong to services.
I plan to drop them not only from container but also from bootstrap event. New event would need to be introduced to do request aware bootstap before routing. I think I did that work in zend-mvc but abandoned when we migrated to laminas.
We discussed it long time ago, and it should be on the old rfc: it would make sense to inject them to the run method with laminas-httphandlerrunner when mvc is migrated to PSR and something similar before that.
'EventManagerAwareInitializer' => static function ($first, $second) { | ||
if ($first instanceof ContainerInterface) { | ||
$container = $first; | ||
$instance = $second; | ||
} else { | ||
$container = $second; | ||
$instance = $first; | ||
} | ||
|
||
if (! $instance instanceof EventManagerAwareInterface) { | ||
return; | ||
} | ||
|
||
$eventManager = $instance->getEventManager(); | ||
|
||
// If the instance has an EM WITH an SEM composed, do nothing. | ||
if ($eventManager instanceof EventManagerInterface | ||
&& $eventManager->getSharedManager() instanceof SharedEventManagerInterface | ||
) { | ||
return; | ||
} | ||
|
||
$instance->setEventManager($container->get('EventManager')); | ||
}, |
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.
👍
$listeners = $container->get('config')[Application::class]['listeners'] ?? []; | ||
foreach ($listeners as $listener) { | ||
$container->get($listener)->attach($em); | ||
} |
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.
👍
use Laminas\View\Helper as ViewHelper; | ||
use Laminas\View\HelperPluginManager; | ||
|
||
class ViewHelperManagerFactory extends AbstractPluginManagerFactory | ||
class ViewHelperManagerFactory implements FactoryInterface |
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.
Honestly, I'd drop the implements FactoryInterface
part. Factories only need to be invokable,and it's another explicit binding to laminas-servicemanager instead of a generic PSR-11 implementation.
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.
I agree.
Factories look for extra input parameters that are only supported by service manager. the $options
specifically, while for the $name
we do provide decorators for others containers iirc.
I will have a pass dedicated to factories. I think quite a few of them can be safely dropped and replaced by factories provided by components.
{ | ||
// Configure URL view helper | ||
$urlFactory = $this->createUrlHelperFactory($services); | ||
$plugins->setFactory(ViewHelper\Url::class, $urlFactory); | ||
$plugins->setFactory('laminasviewhelperurl', $urlFactory); | ||
$managerConfig['aliases']['laminasviewhelperurl'] = ViewHelper\Url::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.
👍
Hi @froschdesign When do you plan to release version 4.0.*? |
This PR introduces changes necessary to cut out module manager from MVC application.
Usage originally envisioned for module manager is no longer relevant:
-Most used remaining features are a config provider and
onBootstrap()
convenience listener.As proven by Mezzio, much simpler laminas-config-aggregator coupled with Container is more than sufficient.
Changes:
Laminas\Mvc\Application::init()
is removed in favor ofLaminas\Mvc\ConfigProvider
for laminas-config-aggregatorLaminas\Mvc\Service\ConfigFactory
removed asconfig
can now be merged and provided to Container before Container is created$config[\Laminas\Mvc\Application::class]['listeners']
keyLaminas\ModuleManager\Listener\ServiceListener
into each respective plugin manager factoryonBootstrap()
convenience listener is not replaced by any alternative at this time.Documentation tracking issue: #58