I try to migrate from ZF2 to ZF3 but many viewHelpers and validators do not work. But only the ones who overwrite ZendFrameworks viewhelpers / validators do not work...
I want f.e. to overwrite the CSRF validator to allow a higher timeout by default.
I have the following application configuration:
$config = array(
// This should be an array of module namespaces used in the application.
'modules' => array(
'Zend\Cache',
'Zend\Db',
'Zend\Log',
'Zend\Mail',
'Zend\Mvc\Console',
'Zend\Mvc\I18n',
'Zend\I18n',
'Zend\Mvc\Plugin\FilePrg',
'Zend\Form',
'Zend\Hydrator',
'Zend\InputFilter',
'zend\Form',
'Zend\Filter',
'Zend\Mvc\Plugin\FlashMessenger',
'Zend\Mvc\Plugin\Identity',
'Zend\Mvc\Plugin\Prg',
'Zend\Navigation',
'Zend\Paginator',
'Zend\Serializer',
'Zend\ServiceManager\Di',
'Zend\Session',
'Zend\Router',
'Zend\Validator',
'DoctrineModule',
'DoctrineORMModule',
'TwbBundle',
'AssetManager',
#'Reliv\ElFinder',
'ZfcUser', //https://github.com/ZF-Commons/ZfcUser
'ZfcUserDoctrineORM',
'BjyAuthorize', // https://github.com/bjyoungblood/BjyAuthorize
'Base',
'Product',
'Blog',
'Admin'
),
// These are various options for the listeners attached to the ModuleManager
'module_listener_options' => array(
// This should be an array of paths in which modules reside.
// If a string key is provided, the listener will consider that a module
// namespace, the value of that key the specific path to that module's
// Module class.
'module_paths' => array(
'./module',
'./vendor',
),
// An array of paths from which to glob configuration files after
// modules are loaded. These effectively override configuration
// provided by modules themselves. Paths may use GLOB_BRACE notation.
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
// Whether or not to enable a configuration cache.
// If enabled, the merged configuration will be cached and used in
// subsequent requests.
// 'config_cache_enabled' => true,
// The key used to create the configuration cache file name.
//'config_cache_key' => $stringKey,
// Whether or not to enable a module class map cache.
// If enabled, creates a module class map cache which will be used
// by in future requests, to reduce the autoloading process.
// 'module_map_cache_enabled' => true,
// The key used to create the class map cache file name.
#'module_map_cache_key' => $stringKey,
// The path in which to cache merged configuration.
'cache_dir' => "data/cache/",
// Whether or not to enable modules dependency checking.
// Enabled by default, prevents usage of modules that depend on other modules
// that weren't loaded.
// 'check_dependencies' => true,
),
// Used to create an own service manager. May contain one or more child arrays.
//'service_listener_options' => array(
// array(
// 'service_manager' => $stringServiceManagerName,
// 'config_key' => $stringConfigKey,
// 'interface' => $stringOptionalInterface,
// 'method' => $stringRequiredMethodName,
// ),
// )
// Initial configuration with which to seed the ServiceManager.
// Should be compatible with Zend\ServiceManager\Config.
// 'service_manager' => array(),
);
Module config of Base module:
namespace Base;
...
return [
...
'validators' => array(
'invokables' => [
\Zend\Validator\Csrf::class => Validator\Csrf::class
]
)
...
]
Base\Validator\Csrf:
<?php
namespace Base\Validator;
class Csrf extends \Zend\Validator\Csrf
{
protected $timeout = 1;
public function __construct($options = [])
{
parent::__construct($options);
die("THIS DOES NOT GETTING PRINTED! NOR DOES THE BREAKPOINT HIT.");
}
}
EDIT: Added autoload config
composer.json:
"autoload": {
"psr-4": {
"Base\\": "module/Base/src/"
}
}
EDIT 2: Possible bug in implementation of \Zend\Form\Element\Csrf ?
Interesting, the CsrfValidator just gets directly instantiated here...
/**
* Get CSRF validator
*
* @return CsrfValidator
*/
public function getCsrfValidator()
{
if (null === $this->csrfValidator) {
$csrfOptions = $this->getCsrfValidatorOptions();
$csrfOptions = array_merge($csrfOptions, ['name' => $this->getName()]);
$this->setCsrfValidator(new CsrfValidator($csrfOptions));
}
return $this->csrfValidator;
}
Stacktrace (breakpoint in \Zend\Validator\Csrf __construct())
StaticPage
is another module of mine.
I also debugged with xdebug and set a break point in the CsrfFactory (return statement) to see, if it is used (but it isn't). I thought I can overwrite services / validators etc. easily in ZF3... Did I miss something?
You can use a delegator to change the validator attached to the form element when the element is instantiated. Essentially a delegator allows you (in this case) to modify the form element after it has been constructed - the idea is explained well here.
In your case you would create a class:
<?php
namespace Base\Delegator;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\DelegatorFactoryInterface;
use Zend\Validator\Csrf as CsrfValidator;
class CsrfDelegatorFactory implements DelegatorFactoryInterface
{
public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback)
{
// construct the Csrf form element
$element = call_user_func($callback);
// set the validator with chosen timeout and other options
$element->setCsrfValidator(new CsrfValidator(
[
// ...
'timeout' => 10000
]
));
return $element;
}
}
Then map the delegator to Zend\Form\Element\Csrf
in your application module.config.php:
'form_elements' => [
// ...
'delegators' => [
\Zend\Form\Element\Csrf::class => [
0 => \Base\Delegator\CsrfDelegatorFactory::class
],
]
],
Note that this is only changing the validator assigned by default to a Zend\Form\Element\Csrf
and a csrf validator obtained by other means will not be affected.