ValidatorManager not calling custom validator factory

As Matthew kindly requested in Slack thread here is an example of ValidatorManager not calling custom validator factory.
Same goes for FormElementManager not calling form factory.
Simply comment out any (or both) of two defined delegators.

Just a few initial comments, as I had to dive a bit further into code to understand what’s being wired and where.

  • In config/config.php, add entries for each of the ConfigProvider classes of the ZF components installed (e.g., Zend\Filter\ConfigProvider::class), instead of adding config/autoload/<component>.global.php files that import and invoke them. It’s simpler to see which components are registered, plays nicer with zend-component-installer, and follows the conventions of default Expressive applications.
  • One problem highlighted is that App\Form\Form composes App\InputFilter\InputFilter, and the latter is not being injected with the custom validator App\Validator\Validator, which is provided via validators configuration in App\ConfigProvider.

I have update the example to reflect your suggestion and slightly modified Form and FormFactory so that commenting out FormElementDelegator will cause an exception to be thrown because FormFactory is not called

I’ve done the following:

  • Cloned your repository
  • Run composer install. When I did, it prompted me to inject the ConfigProvider from zend-filter, which I did.
  • I then ran php -S 0:8080 -t public/ public/index.php to initialize a web server.
  • I navigated to https://localhost:8080/test to test the form.

As expected, this worked fine.

I then did as you suggested, and commented out first the FormElementManager delegator in App\ConfigProvider, and then the ValidatorManager delegator. This continued to work.

And then I remembered to enable development mode, to ensure the configuration cache was cleared: composer development-enable. Once I did this, I repeated the steps above, refreshing the page at each step. At the step where I commented out the FormElementManager delegator, I ran into the problem.

whoops reports that the issue is within App\Form\Form::init(), at the line that tests if !$this->something, which triggers an exception. This demonstrates that the App\Form\FormFactory is not being used to instantiate the form instance.

If I instead comment out the ValidatorManager entry, I do not see an error until after submission, at which point App\Validator\Validator::isValid() raises an exception because its first argument, assigned to the property $test, is null — indicating that its factory was not invoked.

So, at this point, I have something reproducible. I’ll see what I can find from here.

Okay, I figured out the issue, and it affects pretty much all of these AbstractPluginManager implementations.

In zend-mvc applications, we tie into zend-modulemanager, and have integration in the Module class of each that informs Zend\ModuleManager\Listener\ServiceListener of configuration keys associated with each plugin manager. Once bootstrap is over, it merges configuration under that key in order to configure the associated plugin manager.

What this means is that the various keys such as validators, form_elements, etc. have meaning in zend-mvc applications, but not currently in Expressive applications.

This is clearly a serious issue, and I’m not 100% sure how to manage it. I think we may be able to do it via the factory for each plugin manager, however. They could look for the config service and relevant config key, and, if found use the value to configure the plugin manager before returning it.

To be on the safe side, we’d check to see if the ServiceListener service is present before doing so, as we would not want to double-inject. (That said, this approach may also mean we no longer need to use that functionality, which could be fantastic.)

I’ll keep this post updated with links to pull requests as I make them.

So glad you could reproduce the problem! Really looking forward to the bugfix. Thanks for the patience for understanding the problem and your time looking into this case - I’m sure Expressive will profit a lot in sense of ZF component usage.

First patch is for zend-validator: https://github.com/zendframework/zend-validator/pull/168

Second patch is for zend-form: https://github.com/zendframework/zend-form/pull/164

Third patch is for zend-filter: https://github.com/zendframework/zend-filter/pull/56

Fourth patch is for zend-inputfilter: https://github.com/zendframework/zend-inputfilter/pull/137

Fifth patch is for zend-hydrator: https://github.com/zendframework/zend-hydrator/pull/59

Sixth patch is for zend-i18n: https://github.com/zendframework/zend-i18n/pull/74

Seventh and final patch is for zend-log: https://github.com/zendframework/zend-log/pull/74

All releases are done!

Pin to ^<version> in your composer.json files (or run composer require "zendframework/<component>:^<version>"; alternately, just run composer update), and you should now have something working!

I’ve just tried that with the sample app you provided, commenting out the two delegator factories you provided, and all is working now!

Works like a charm :slight_smile:

Great job guys! Looking forward to test that myself…

Quite curious if this addresses some of the issues you’ve raised in the past, @RalfEggert!

Now I just need to work on the navigation stuff with @froschdesign sometime in the near future so we can get that sorted!

Yes, it does solve my issues. My /config/container.php file looked like this before the update:

use Zend\Filter\FilterPluginManager;
use Zend\Form\FormElementManager\FormElementManagerV3Polyfill;
use Zend\Hydrator\HydratorPluginManager;
use Zend\InputFilter\InputFilterPluginManager;
use Zend\ServiceManager\Config;
use Zend\ServiceManager\ServiceManager;
use Zend\Validator\ValidatorPluginManager;

// Load configuration
$config = require __DIR__ . '/config.php';

// Build container
$container = new ServiceManager();
(new Config($config['dependencies']))->configureServiceManager($container);

// Inject config
$container->setService('config', $config);

/** @var HydratorPluginManager $hydratorManager */
$hydratorManager = $container->get('HydratorManager');
$hydratorManager->configure($config['hydrators']);

/** @var FilterPluginManager $filterManager */
$filterManager = $container->get('FilterManager');
$filterManager->configure($config['filters']);

/** @var ValidatorPluginManager $validatorManager */
$validatorManager = $container->get('ValidatorManager');
$validatorManager->configure($config['validators']);

/** @var InputFilterPluginManager $inputFilterManager */
$inputFilterManager = $container->get('InputFilterManager');
$inputFilterManager->configure($config['input_filters']);

/** @var FormElementManagerV3Polyfill $formElementManager */
$formElementManager = $container->get('FormElementManager');
$formElementManager->configure($config['form_elements']);

return $container;

Now I can shorten it to this:

use Zend\Filter\FilterPluginManager;
use Zend\Form\FormElementManager\FormElementManagerV3Polyfill;
use Zend\Hydrator\HydratorPluginManager;
use Zend\InputFilter\InputFilterPluginManager;
use Zend\ServiceManager\Config;
use Zend\ServiceManager\ServiceManager;
use Zend\Validator\ValidatorPluginManager;

// Load configuration
$config = require __DIR__ . '/config.php';

// Build container
$container = new ServiceManager();
(new Config($config['dependencies']))->configureServiceManager($container);

// Inject config
$container->setService('config', $config);

return $container;

Without these extra lines all custom form elements, validators, and stuff weren’t found. After upgrading, they are.

:+1:

little late, but wanted to reply on this again. Updated packages, removed my workaround and it worked like expected for custom filters and validators. Really great!

I think this was a huge step forward for using these packages inside Expressive context and I’m really happy to see the benefits Matthew pointed out in his blog post about this. From a question inside slack to a complex series of releases, that fix the problem. And many people being involved to get the problem solved.

So let me thank you @matthew for working on the patches/release, @metalinspired for providing the example and all others I missed that were part of this.

Working now ~10 years with ZF. ZF2 and ModuleManager was a huge step forward, but all the work @matthew did in relation to standards and Expressive - it feels just right. Working now 4 weeks with Expressive2 and there was no situation I didn’t liked it. No magic needed, so flexible to use - v2 just nailed it. :bow: