Giter Club home page Giter Club logo

zend-inputfilter's Introduction

Logo

Welcome to the Zend Framework 3.0 Release!

RELEASE INFORMATION

Zend Framework 3.0.1dev

This is the first maintenance release for the Zend Framework 3 series.

DD MMM YYYY

UPDATES IN 3.0.1

Please see CHANGELOG.md.

SYSTEM REQUIREMENTS

Zend Framework 3 requires PHP 5.6 or later; we recommend using the latest PHP version whenever possible.

INSTALLATION

We no longer recommend installing this package directly. The package is a metapackage that aggregates all components (and/or integrations) originally shipped with Zend Framework; in most cases, you will want a subset, and these may be installed separately; see https://docs.zendframework.com/ for a list of available packages and installation instructions for each.

We recommend using either the zend-mvc skeleton application:

$ composer create-project zendframework/skeleton-application project

or the Expressive skeleton application:

$ composer create-project zendframework/zend-expressive-skeleton project

The primary use case for installing the entire framework is when upgrading from a version 2 release.

If you decide you still want to install the entire framework:

$ composer require zendframework/zendframework

GETTING STARTED

A great place to get up-to-speed quickly is the Zend Framework QuickStart.

The QuickStart covers some of the most commonly used components of ZF. Since Zend Framework is designed with a use-at-will architecture and components are loosely coupled, you can select and use only those components that are needed for your project.

MIGRATION

For detailed information on migration from v2 to v3, please read our Migration Guide.

COMPONENTS

This package is a metapackage aggregating the following components:

CONTRIBUTING

If you wish to contribute to Zend Framework, please read the CONTRIBUTING.md and CODE_OF_CONDUCT.md files.

QUESTIONS AND FEEDBACK

Online documentation can be found at https://docs.zendframework.com/. Questions that are not addressed in the manual should be directed to the relevant repository, as linked above.

If you find code in this release behaving in an unexpected manner or contrary to its documented behavior, please create an issue with the relevant repository, as linked above.

Reporting Potential Security Issues

If you have encountered a potential security vulnerability in Zend Framework, please report it to us at [email protected]. We will work with you to verify the vulnerability and patch it.

When reporting issues, please provide the following information:

  • Component(s) affected
  • A description indicating how to reproduce the issue
  • A summary of the security vulnerability and impact

We request that you contact us via the email address above and give the project contributors a chance to resolve the vulnerability and issue a new release prior to any public exposure; this helps protect Zend Framework users and provides them with a chance to upgrade and/or update in order to protect their applications.

For sensitive email communications, please use our PGP key.

LICENSE

The files in this archive are released under the Zend Framework license. You can find a copy of this license in LICENSE.md.

ACKNOWLEDGEMENTS

The Zend Framework team would like to thank all the contributors to the Zend Framework project; our corporate sponsor, Zend Technologies / Rogue Wave Software; and you, the Zend Framework user.

Please visit us sometime soon at http://framework.zend.com.

zend-inputfilter's People

Contributors

akrabat avatar bakura10 avatar brettminnie avatar cgmartin avatar dasprid avatar davidwindell avatar demichl68 avatar evandotpro avatar ezimuel avatar fabiocarneiro avatar freeaqingme avatar froschdesign avatar hschletz avatar kokspflanze avatar koopzington avatar maks3w avatar marc-mabe avatar michalbundyra avatar mikaelkael avatar mwillbanks avatar ocramius avatar ralphschindler avatar rkeet avatar samsonasik avatar sgehrig avatar slamdunk avatar thinkscape avatar veewee avatar weierophinney avatar zburnham avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zend-inputfilter's Issues

Input with filter inside construct

In my code i have input construct code:

use Zend\InputFilter\Input;

class Senha extends Input
{

public function __construct() {
    parent::__construct('senha');
    $this->setRequired(true);
    $this->getFilterChain()->attachByName('StringTrim')
                           ->attachByName('StringToUpper')
                           ->attachByName('PandoraBase\Filter\Md5');
}       

}

in pull:
#2

this break, because factory overwrite filters

Boolean Filter causes input to be required

When configuring and Input like:

[
    'name' => 'isSomeValue',
    'required' => false,
    'filters' => [
        ['name' => 'Zend\Filter\Boolean'],
    ],
];

\Zend\InputFilter\Input::isValid() method returns false.

It has something to do with the 'notEmptyValidator'.

If I change \Zend\InputFilter\Input line ~341 from:

    if (! $allowEmpty && ! $continueIfEmpty) {
        $this->injectNotEmptyValidator();
    }

To:

    if (! $allowEmpty && ! $continueIfEmpty && $required) {
        $this->injectNotEmptyValidator();
    }

It works as expected, but I am not sure of the impact.

2.5.1 requires PHP >= 5.3, 2.5.2 requires PHP >= 5.5

We've just noticed that zendframework/zend-inputfilter has bumped the minimum required version in a minor release !

Is it possible de change that please ? that's a huge BC break !

I guess it is related to the use of ClassName::class in some part of the code.

Check All Headers In Documentation

Check headers are correct

TLDR; Headers should use the #, ## etc to format different levels of headers, and not be underlines using ===== or ``-----`, or be psuedo header using bold

Check all headers on the documentation - headers should use the hash style of declaration rather then be underlined with equals or dashes. The more hashes, the more of a subheading. Eg:

  • # is equal to <h1>
  • ## is equal to <h2>
  • ### is equal to <h3>
  • #### is equal to <h4>
  • ##### is equal to <h5>

Headings should be appropriate for their level in the documentation.

Psuedo headers using bold tags ** should be replaced with appropriate level of heading tag.

Check For Blockquotes In Docs

Check the document for bad blockquoutes

TLDR; Check blockquotes are formatted correctly using > and check headings in blockquotes are using ###

Blockquotes are donated by a single greater than character and then a space. Make sure all the blockquotes in every doc file are correctly formatted. Headings in blockquotes should use ### and not bold.

Any paragraph spacing in blockquotes should be marked using a single greater than, then a space.

'Value is required' message should be localizable

We had the issue that 'Value is required' message can not be localized to Chinese as it is hardcoded in the framework code. The custom-message NotEmpty validator cannot override this error message in our case. Please help to make the hardcoded message localizable.

Class Zend\ServiceManager\AbstractPluginManager not found

When you run zendframework/zend-inputfilter as a stand alone library you get Class Zend\ServiceManager\AbstractPluginManager not found with the following code

$filter = new \Zend\InputFilter\InputFilter();
$input = new \Zend\InputFilter\Input('test');
$filter->add($input);

$filter->setData([]);

$filter->isValid();
$filter->getMessages();

This is due to the fact of \Zend\InputFilter\Input line 500, where the plugin chain is used. Which in turn uses the ValidatorPluginManager, which extends the Zend\ServiceManager\AbstractPluginManager. This class is not loaded because it is not part of the dependency.

To fix this issue zendframework/zend-servicemanager needs to be a dependency, and not dev only.

[WIP] [RFC] Control flow descriptors

Allow empty and continue if empty are currently flags which allow a small amount of control over the flow of validation; unfortunately their implementation has given rise to a number of hard to fix issues. As a proposed solution, I suggest moving this functionality to validators and making it more generic.

A validator can return either true or false. We should give a developer a tool to allow them to decide what to do next depending on the return value. Possible options are:

Continue running further validators
Continue running further validators but mark the input as having failed validation
Stop running validators, mark the input as having failed validation
Stop running validators, mark the input as having passed validation

and possibly:
Run this alternative set of validators

This would replace the allow empty, continue if empty and break chain on failure options from the input class

TODO: spec this out; design interfaces and show use cases.

Check Documentation Tables

Check the tables in a document

TLDR; All tables should be in the format of GHFM using | and - as horizontal and vertical separators respectively

Check all tables are in the correct format. Please don't use leading and trailing | - more information on github flavoured markdown tables can be found here.

ZF3 - type issue field for fieldsets or nested input filter

This issue describes a problem with the ZF2 input filtering that I would like to be fixed in the future ZF3 solution for input filtering.

Our preferred solution for building input filters in our application is by using config arrays and generating them through the input filter factory class. We often have nested input filters (for validating field-sets or nested resources). To reuse input filters inside other configs, when using field-sets or when handling complex nested resources it is necessary to add a type field to the config as follows:

$config = array(
    'resource' => array(
        'property_one' => array(
            'name' => 'property_one',
            'required' => false
        ),
        'property_two' => array(
            'name' => 'property_two',
            'required' => false
        ),
        'property_three' => array(
            'name' => 'property_three',
            'required' => false
        ),
        // type key necessary for nested input filter
        'type' => 'Zend\InputFilter\InputFilter'
    )
);

The factory will recognize the type key and sets it as the $class to use during building of the InputFilter. So now we can make our input filter like this:

$inputFilter = $factory->createInputFilter($config);

You can even another class as long as the class implements the InputFilterInterface

Now comes the issue...
It is not uncommon to use the field type for data, in our case we have type as a property for several of our resources. This breaks the whole pattern of configuring our input filters with arrays. If we use the type field for input we can no longer set the input filter class in the config...

I think it is a design mistake to use a key with a common name like type at the same level as the input fields (properties) for setting the class in the config array.
A solution could be to use a more specific key to make it less likely to have conflicts with input field names, for example input_class, or maybe even input_type.
Probably it would even better to move the type field one level up in the config, so conflicts will never be an issue. The config could for example look like this:

$config = array(
    'resource' => array(
        'name' => 'resource',
        'class' => 'Zend\InputFilter\InputFilter', // declaring type
        'config' => array(
            'property_one' => array(
                'name' => 'property_one',
                'required' => false
            ),
            'property_two' => array(
                'name' => 'property_two',
                'required' => false
            ),
            'property_three' => array(
                'name' => 'property_three',
                'required' => false
            )
        )
    )
);

I have no idea about what the design for ZF3 input filtering looks like, but I just want to make sure that this issue would be solved in case it might be done similarly in ZF3.

Intended behaviour on nested input filters and empty POST data?

I have the following classes defined

  • fieldset with some regular inputs and a collection element
  • input filter for it that has a collection input filter for the collection element

Let's call them:
fieldset = project
collection = disciplines

Then a form having the fieldset set as the base fieldset, a csrf, submit etc. and the input filter associated
And a validation group that has only the fieldset=>collection pair, something like
setValidationGroup([project => [
disciplines => [
id, name
]
]]);

if I submit the form with some elements in the collection
form->bind(object)
data = get POST
form->setData(data)
form->isValid()
are working as expected

when I submit the form with 0 elements in the collection, the POST is missing the project=>discipline array structure, it has only the form input(csrf and so on...)
if i feed this into the form, the validation fails, but not as I expect(by the way, the collection input filter has the is required flag set to true)

The returned error messages are for the entire fieldset inputs, like it does no take the validation group into account and the collection's isRequired flag.

The shortest workaround for me was to normalize the POST data and make sure that even if empty, the POST is following the correct array structure(like project=>[disciplines=>[//empty array...], csrf, submit], but it looks hackish.

This might be related to other issues that I found related to emptying the collection, but not sure if it was fixed, or this is the intended behavior...
Also can't follow the code completely, not sure if this is input filter related or form/fieldset related

Validation group not applied correctly when validating a CollectionInputFilter

While debugging a problem when validating a complex form containing collections and validation groups to limit the fields that are being validated I notice the following code in CollectionInputFilter:

        foreach ($this->data as $key => $data) {
            // ....
            if (null !== $this->validationGroup) {
                $inputFilter->setValidationGroup($this->validationGroup[$key]);
            }

If the CollectionInputFilter has a validation group then the validation group is indexed using the same key as the data. A validation group is assigned like this:

$collection = new CollectionInputFilter(...);
$collection->setValidationGroup(array(
    'a', 'b', 'c'
));

Now, for the first element in the list only property a is validated, property b for the second, etc.

Am I missing something in the way I assign my validation group? Or should the line where the validation group is assigned read:

                $inputFilter->setValidationGroup($this->validationGroup);

I can work around this by calling $collection->getInputFilter()->setValidationGroup(...) instead, however this prevents me of performing a single setValidationGroup call at the root of my form with a single nested array for the entire form.

CollectionInputFilter throws exception on scalar input

    public function testCollectionInputFilterMustNotThrowsExceptionOnScalar()
    {
        $inputFilterManager = $this->getApplicationServiceLocator()->get('InputFilterManager');
    
        $factory = new \Zend\InputFilter\Factory($inputFilterManager);
        $inputFilter = $factory->createInputFilter([
            'type' => \Zend\InputFilter\CollectionInputFilter::class,
        ]);
    
        $inputFilter->setData('asd'); // throws Zend\InputFilter\Exception\InvalidArgumentException: Zend\InputFilter\CollectionInputFilter::setData expects an array or Traversable collection; invalid collection of type string provided
    }

That breaks to use input-filter especially where used nested collection filters.

BC-Break in 2.4.1? Really annoying NotEmpty injection by default

Hey guys, I've run into troubles at work with our InputFilters behaving very weird when updating from 2.4.0 to 2.4.2. The change that gave us trouble actually happened in 2.4.1 and it's been this one right here:

zendframework/zendframework#7474
zendframework/zendframework@ffad792#diff-eba196bb0648a9366fa5c59a0edb701b

     public function setRequired($required)
     {
         $this->required = (bool) $required;
-        $this->setAllowEmpty(!$required);

         return $this;
     }

The removal of this little snippet has led to the injection of the NotEmpty validator by default. I have created a little test to outline my problem:

public function testWtf()
{
    $data = [
        'foo' => '0'
    ];

    $inputFilter = new InputFilter();
    $inputFilter->add([
        'name'       => 'foo',
        'required'   => false,
        'filters'    => [
            ['name' => 'Boolean']
        ],
        'validators' => []
    ]);

    $inputFilter->setData($data);

    $this->assertTrue($inputFilter->isValid());
}

This is basically how I wrote the InputFilter. Now knowing the internals of the InputFilter (after doing some digging), i now know that I can simply workaround this by adding allow_empty => true to the inputFilter configuration, but I'm asking myself "why?".

2.4.1 had only Bugfixes but in my opinion this is clearly a BC Break as it is breaking existing stuff.

I'm trying to figure out the reasoning for having the NotEmpty validator be added by default. So if anyone could elaborate on this, that'd probably be a huge help to me.

Aside from this, but this may be offtopic to this component:
why is NotEmpty behaving the way it is? Why is 0 or false be considered empty be default?

Those two things combined are really tricky to get around and I'm using this framework pretty regularly ^^

[WIP] [ZF3] Refactor filter

This issue has been moved from the zendframework repository as part of the bug migration program as outlined here - http://framework.zend.com/blog/2016-04-11-issue-closures.html


Original Issue: https://api.github.com/repos/zendframework/zendframework/issues/5097
User: @bakura10
Created On: 2013-09-09T19:01:52Z
Updated At: 2014-09-28T00:34:45Z
Body
As asked by @weierophinney on IRC, I should create the PR against develop.

So let's focus for now on Filter, this is the most advanced one and mostly done (just need to find a great way for some plugin manager), and some tests were already updated.

As a reminder, the Filter refactor is mostly:

  • Code cleaning (now, all filters accept the same options array, which allow to configure a filter)
  • Options are now underscore_separated
  • Some filters were remade

Comment

User: @bakura10
Created On: 2013-09-09T19:44:33Z
Updated At: 2013-09-09T19:44:33Z
Body
For people that want to help: I've spotted some test that say the test is invalid with PHP 5.4. Why? Does this means some thing can be removed in PHP 5.4?


Comment

User: @ThomasCantonnet
Created On: 2013-09-10T18:02:40Z
Updated At: 2013-09-10T18:06:48Z
Body
@bakura10 Although some have reported that iconv's //IGNORE option is broken on PHP 5.3.10 too, it seems that it's broken in PHP5.4 <= 5.4.6 (https://bugs.php.net/bug.php?id=61484). I don't know what the status on that issue is, but that might explain this test. It appears it's a glibc issue though, not sure how PHP versions affect this behaviour.

(Edit: my bad, it seems that it's just iconv that doesn't accept this //IGNORE option anymore and it hasn't been fixed in PHP 5.3, but it's been removed in 5.4. I guess we need to fix that filter.)

Seems like @Maks3w was the one who marked this test as incompatible (bakura10/zf2@ab87188) so he might answer you on the reasons :)


Rename `continue_if_empty` to `validate_if_empty` and default to true

The configuration parameter continue_if_empty in Zend\InputFilter\Input doesn't convey much meaning. Continue doing what? The name has no apparent link to the validation process, where it is an essential parameter.

I also think it should default to true. Because of #11 I marked a field with required == false and added a NotEmpty validator instead, and later found out that the validator didn't run. What's the point of a NotEmpty validator if it doesn't run when the value is empty?

setMessages not working properly

Hello.

I tried to validate my form.
In controller I use this code

if ($formUrl != $url) {
    $form->get('url')->setMessages(array('Link is wrong'));
}

if ($form->isValid()) {
    echo 'validate';
}

If I set "setMessages" - form is validate, but form must not be validate

Input fallback_value option limitation/feature suggestion

Would be nice if possible to use fallback value on optional fields only when input not set. Current behavior is when input is not set or validation fails, but I would like to fail validation if input is not valid and use fallback only when input is not set at all. With current behavior is impossible to check if data is invalid.

example:

// create $inputFilter with 'optional_field' input and 'fallback' as fallback value

// when input is not set
$inputFilter->setData([]);
$inputFilter->isValid(); // return true
$inputFilter->getValue('optional_field'); // return 'fallback'

// when input is set but not valid
$inputFilter->setData(['optional_field' => 'invalid']);
$inputFilter->isValid(); // return false

This is not possible at the moment, but very important scenario for me. I think after #25 when required option works as 'is set' and not as 'not empty' this would make more sense. no?

I think having fallback on invalid data is bad idea. Also if data should be modified somehow to be valid there is filters for this.

Use plurals for valid/invalid input get in InputFilterInterface

InputFilterInterface::getValidInput():array and InputFilterInterface::getInvalidInput():array should be InputFilterInterface::getValidInputs():array and InputFilterInterface::getInvalidInputs():array

Both method return always a collection type (array)

How to ... in InputFilter

I use InputFilter in Apigility validation POST data. I want to make optional user object in json. However if it's appear then some fields should be required.

{
  "number": "1234",
  "user": {
    "name": "John",
    "company": {
        "name": "Company name",
        "vat_id": "00011114444",
        "address": {
          "locality": "Paris",
          "postal_code": "333",
          "country": "FR"
        }
    }
  }
}

My InputFilter config:

return [
    'input_filter_specs' => [
        'OrdersApi\V1\Rest\ShippingOrders\Validator' => [
            'number' => [
                'required' => true,
            ],
            'user' => [
                'type' => Zend\InputFilter\InputFilter::class,
                'company' => [
                    'type' => Zend\InputFilter\InputFilter::class,
                    'name' => [],
                    'vat_id' => [],
                    'address' => [
                        'type' => Zend\InputFilter\InputFilter::class,
                        'locality' => [
                           'required' => false,
                        ],
                        'postal_code' => [],
                        'country' => [],
                    ],
                ],
            ],
        ],
    ],
];

In this case I need to send all fields (only locality is not required), but I need to be able to not send whole user object.

Any advice?

ConfigProvider - registration of InputFilterAbstractServiceFactory

As of written in docs InputFilterAbstractServiceFactory has to be registered in ConfigProvider.

Correct version of invoke is imho:

    public function __invoke()
    {
        return [
            'dependencies' => $this->getDependencyConfig(),
            'input_filters' => [
                'abstract_factories' => [
                    InputFilterAbstractServiceFactory::class,
                ],
            ],
        ];
    }

[RFC] Replace Input with a chain of callbacks.

I started to think the best way of build a custom pipe of filters and validators where you can filter after validation, etc.

I propose replace Zend\InputFilter\Input with a custom chain of methods like A+ promises.

For resume A+ promises have the following main properties along others I won't detail here:

  • has the shape of then(callback success = null, callback error = null)
  • success chain is broken when an exception is raised
  • if error chain return a value then the next success callback continue with the value provided.

So basically the chain do two kind of things. Transformations of the input value and reject the chain if is invalid.

$chain = new Chain();
$chain
  ->then(
    // Tipical filter function
    function($inputValue) use ($context) {
      return $filteredValue;
    }
  )
  ->then(
    // Tipical validation function
    function($inputValue) use ($context) {
      if ( ! isValid($inputValue) {
        throw new ValidationException(); // Break the chain
      }

      // Note MUST return a value, not the isValid boolean.
      // It can return any other object like DateTime or a database Entity
      return $inputValue;
    }
  )

  ->then($hydrate)
  ->then($validate)
  ->then($filter)

  ->then(
    null,
    function ($error) use ($context) {
      if  ($error === null) {
        // chain was invoked without `$inputValue` (i.e. optional field)
        return $fallbackValue;
      }

      throw $error;
    }
  )
  ->then(
    null,
    function ($error) use ($context) {
      // Use the fallback value when input is invalid
      return $fallbackValue ? : throw $error;
    }
  ),
  ->then(
    null,
    function ($error) use ($context) {
      // A fixed error message when input is invalid
      throw new ValidationException("fixed error message");
    }
  )
;

$context = mixed;
$chain->resolve($inputValue);

Options:

  • Reject promise using a reject method callback instead throwing exceptions.

    ->then($reject($errror) {}, $inputValue);

Benefits of this design:

  • Complete customization of the pipe.
  • Avoid create two times the same object, one for validate and another one for normalize (like DateTime)

Thoughts?

/cc @weierophinney, @Ocramius, @bakura10, @zendframework/community-review-team

Roadmap for V3 and V4

Proposal of roadmap for next major versions.

V3:
V3 should silently fail if any deprecation feature is used. i.e. No file/method removal/rename is allowed.

  • Deprecate Continue If Empty & AllowEmpty
  • Silently ignore continue_if_empty / allow_empty options
  • Let Input validate unknown value (not-set) or know value.
  • Leverage InputFilter from apply complex rules for to determinate if input is valid/invalid
  • Add a resetX method for clear the class state

V4:
V4 does not (currently) restrict any bc break type.

  • Remove deprecated features
  • Fix class inheritance (subclasses must follow the api contract set by the parent) Affected: CollectionInputFilter, ArrayInput and FileInput
  • Make classes stateless
  • Adapted for zend-validator v3

Undo deprecation of AllowEmpty + ContinueIfEmpty

In the Input these comments: @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. have been added to AllowEmpty and ContinueIfEmpty. These properties should not be deprecated. Their use differs subtly to the use case for a NotEmpty validator. If you add a not empty validator, you can no longer validate optional fields.

Consider a shipping notes field which is optional but if the user provides a value, the string should be between 5 and 200 characters long.

Continue if empty is also required as this allows validating optional fields against other field context eg if you have a field on the form 'Have you had any none driving convictions in the last 5 years?' as a yes/no radio and a 'If yes please provide details' For this, you would setup a validation chain which allows empty values, but continues if empty. You would then add a (custom) validator which ensures that a value is provided if the first question has a yes answer.

zendframework/zendframework#6668 Provides some more context on how these options work and interplay with each other.

Check Documentation Code Blocks

Check code blocks are correct

TLDR; Check in all files that codeblocks are correct, in PSR-2 format and have PHP syntax highlighting applied.

Code blocks should be in the following format...

```php
 'ZEND-FRAMEWORK');

// No required options
$rendererOptions = array();
$renderer = Barcode::factory(
    'code39', 'image', $barcodeOptions, $rendererOptions
);

```

Note the three backticks then php in the opening fence, and the closing fence is just three backticks. It's common for the opening backticks to have no code type, or something like source.

Code should also have been automatically formatted into PSR-2 format, but sometimes these slip through the net.

Input Filter Factory uses a different FilterPluginManager instance

I created a custom filter which needs dependency injection by a factory. I added the filter to the config array like so:

return  [
    'filters' => [
        'factories' => [
            'ToFile' => 'Module\Factory\Filter\ToFile',
        ],
    ],
];

I am able to get a filter instance from the FilterPluginManager inside a controller:

$filter = $this->getServiceLocator()->get('FilterManager')->get('ToFile');
var_dump($filter);

Inside the init() method of a class extending Zend\Form\Form I configured an input filter using the custom filter. The form also uses custom form elements from the FormElementManager.

$this->getInputFilter()->add([
    'name' => 'file',
    'required' => false,
    'filters' => [
        [ 'name' => 'ToFile' ],
    ],
]);

Custom form elements are working fine but the custom filter always raises a ServiceNotFoundException:

Zend\Filter\FilterPluginManager::get was unable to fetch or create an instance for ToFile

I found out that the Input Filter Factory is using a different FilterPluginManager instance which has never seen the config array. The custom filter with its factory is not included in the plugin manager.

This is unexpected behaviour? Or am I wrong?

[php 7.2] incompatible prototype

Yes, quite "early" report ;)

PHP Fatal error: Declaration of Zend\InputFilter\CollectionInputFilter::isValid() must be compatible with Zend\InputFilter\BaseInputFilter::isValid($context = NULL) in ...

BC in validators context value

Before 2.5.2 context values was all inputs added to inputfilter but 2.5.2 changed this and now returns raw values passed to inputFilter. I Think this should be and is correct behavior, but unfortunately now it is BC. This should be at least mentioned in release notes or fixed.

example:

<?php

require __DIR__ . '/vendor/autoload.php';

class MyFilter extends Zend\InputFilter\InputFilter
{
    public function init()
    {
        $this->add([
            'name' => 'field1',
            'required' => true,
        ]);

        $this->add([
            'name' => 'field2',
            'required' => true,
            'validators' => [
                [
                    'name' => 'Zend\Validator\Callback',
                    'options'  => [
                        'callback' => function ($value, $context) {
                            var_dump($context);
                            return true;
                        },
                    ],
                ],
            ],
        ]);
    }
}

$app = Zend\Mvc\Application::init([
    'modules' => [
    ],

    'module_listener_options' => [
        'module_paths' => [
        ],

        'config_glob_paths' => [
        ],
    ],

    'input_filters' => [
        'invokables' => [
            'MyFilter' => 'MyFilter',
        ],
    ],
]);

$manager = $app->getServiceManager()->get('InputFilterManager');
$filter = $manager->get('MyFilter');

echo "\n";
echo "#------------------------------\n";
echo "# Test case\n";
echo "#------------------------------\n";
echo "\n";
$filter->setData([
    'field2' => 'foo',
]);

$filter->isValid();

output with versions =<2.5.1

#------------------------------
# Test case
#------------------------------

array(2) {
  'field1' =>
  NULL
  'field2' =>
  string(3) "foo"
}

output with versions =>2.5.2

#------------------------------
# Test case
#------------------------------

array(1) {
  'field2' =>
  string(3) "foo"
}

Change validator chain of collection fields

Hey there,

in some cases i used to change some validators for some fields if the value of them is equals to a given one. Like in this example i change aNotherTestField to required and add an NotEmpty validator if the raw value of testField is equals 4.

public function isValid($context = null)
{
    if ($this->has('testField')) {
        $testFieldRawValue = $this->get('testField')->getRawValue();
        if ($testFieldRawValue == 4) {
            $validatorChain = new Validator\ValidatorChain();
            $notEmptyValidator = new Validator\NotEmpty();
            $notEmptyValidator->setMessage('My Message.', Validator\NotEmpty::IS_EMPTY);
            $validatorChain->attach(
                $notEmptyValidator,
                true
            );
            $this->get('aNotherTestField')->setRequired(true);
            $this->get('aNotherTestField')->setValidatorChain($validatorChain);
        }
    }
}

This works great. But when it come to fieldsets and collections i am not able to change the validator chain of an collections field anymore.

if ($this->has('testCollection')) {
    $testCollection= $this->get('testCollection');
    if ($testCollection->has('testField')) {
        $testFieldRawValue = $testCollection->get('testField')->getRawValue();
        if ($testFieldRawValue == 4) {
            $validatorChain = new Validator\ValidatorChain();
            $notEmptyValidator = new Validator\NotEmpty();
            $notEmptyValidator->setMessage('My Message.', Validator\NotEmpty::IS_EMPTY);
            $validatorChain->attach(
                $notEmptyValidator,
                true
            );
            $testCollection->get('aNotherTestField')->setRequired(true);
            $testCollection->get('aNotherTestField')->setValidatorChain($validatorChain);
        }
    }
}

Is there something wrong with my code or is it a bug?

InputFilter::getValues() does not throw like it should

If the validation failed, according to the docblock, it should throw an exception if you try to get the filtered values anyways.

However this does not happen.

What Exception type should it throw? Should we define a new type "ValidationFailedException" or sth?

This is still a relevant question for when we get stateless InputFilters

Possible BC break for zend-inputfilter > V2.5.1

I think I've found an issue with zend-inputfilter which has prevented me from being able to upgrade successfully (first time it broke in production, second time I added some tests on validators which broke when moving to any version greater than V2.5.1.

I believe the issue was introduced with 3575ece -

I've got a few Apigility APIs which have fields that are set to required, allow_empty and continue_if_empty. The intent is that depending on the value of other fields in the API, these fields may or may not be actually required.

As a simplified example, of what used to work, suppose I have an "account_type" field which can be either "state" or "county". If the value is "county", then a "county" field must be provided with a non-empty value. If the value is "state" then the county field must be blank or not provided.

After upgrading to V2.5.2 (or V2.5.5) instead of a successful validation, I get the error message from the NotEmpty validator indicating that the value is required and cannot be empty.

Ideally, I'd like my work as it did after upgrading. Essentially if the account_type is state then I should not need to send in the county field at all. If there's a set of config changes I can make that will allow this to work, please let me know.

Ping @weierophinney and @Maks3w at Matthew's request.

Zend\Filter\FilterChain::$filters get replicated if same input filter instance is used to filter an array of data

Reference zendframework/zendframework#7645

Same filters are being applied to the data multiple times, if same InputFilter instance is used to validate/filter multiple data (array).

For instance, the data is an array like this:

$emails = [
    [
        'email' => '[email protected]',
        'id' => ' '
    ],
    [
        'email' => '[email protected]',
        'id' => ' '
    ],
    [
        'email' => '[email protected]',
        'id' => ' '
    ],
];

The EmailFilter used is:

class EmailFilter extends AbstractFilter
{

    public function init()
    {

        $this->add([
            'name' => 'email',
            'required' => true,
            'filters' => [
                ['name' => 'ToNull'],
                ['name' => 'StripTags'],
                ['name' => 'StringTrim'],
            ],
            'validators' => [
                [
                    'name' => 'EmailAddress',
                    'options' => [
                        'encoding' => 'UTF-8'
                    ]
                ],
                [
                    'name' => 'NotEmpty'
                ],
            ]
        ]);

        /* id */
        $this->add([
            'name' => 'id',
            'required' => false,
            'filters' => [
                ['name' => 'StripTags'],
                ['name' => 'StringTrim'],
            ],
            'validators' => [
                [
                    'name' => 'IsInt'
                ]
            ]
        ]);
    }

So, now I'm trying to validate each of the item of the array using the EmailFilter:

$filtered = [];
foreach ($emails as $email) {
    $filter->setData($email);
    if (!$this->isValid()) {
        // failed validation
    } else {
        $filtered[] = $filter->getValues();
    }
}

Running this code will replicate the value of FilterChain::$filters for each of these iteration.

For the first iteration, the filters applied to data i.e. returned by FilterChain->getFilters() would be:

"input: email"
"2 => Zend\Filter\ToNull"
"1 => Zend\Filter\StripTags"
"0 => Zend\Filter\StringTrim"

"input: id"
"1 => Zend\Filter\StripTags"
"0 => Zend\Filter\StringTrim"

In the second iteration, you can see the same filters are being applied for both of the inputs: email and id 2 times:

"input: email"
"5 => Zend\Filter\ToNull"
"4 => Zend\Filter\StripTags"
"3 => Zend\Filter\StringTrim"
"2 => Zend\Filter\ToNull"
"1 => Zend\Filter\StripTags"
"0 => Zend\Filter\StringTrim"

"input: id"
"3 => Zend\Filter\StripTags"
"2 => Zend\Filter\StringTrim"
"1 => Zend\Filter\StripTags"
"0 => Zend\Filter\StringTrim"

In the third iteration, the filters are being applied to the data 3 times:

"input: email"
"8 => Zend\Filter\ToNull"
"7 => Zend\Filter\StripTags"
"6 => Zend\Filter\StringTrim"
"5 => Zend\Filter\ToNull"
"4 => Zend\Filter\StripTags"
"3 => Zend\Filter\StringTrim"
"2 => Zend\Filter\ToNull"
"1 => Zend\Filter\StripTags"
"0 => Zend\Filter\StringTrim"

"input: id"
"5 => Zend\Filter\StripTags"
"4 => Zend\Filter\StringTrim"
"3 => Zend\Filter\StripTags"
"2 => Zend\Filter\StringTrim"
"1 => Zend\Filter\StripTags"
"0 => Zend\Filter\StringTrim"

Is there anything wrong here or is this an issue with the filters?

Input::prepareRequiredValidationFailureMessage return value

After upgrading to 2.5.5, Apigility validators are not showing expected error message for required fields if error_message is not set. Previously, it was showing the error message from a NotEmpty validator if present in the validator chain.
I think the newly introduced prepareRequiredValidationFailureMessage() method should return existing NotEmpty validator message, if present in the validator chain.

NotEmpty validator doesn't override the required attribute

A NotEmpty validator should override the required check on an input, but in some cases it doesn't. I'm sorry I haven't been able to pin point the exact location of the bug, but here's the code to reproduce it. In test case 1 both NotEmpty validators overrides the required attribute, which can be seen by the error messages. In test case 2 however, field2 doesn't use it's NotEmpty validator but instead falls back to it's internal check to see if the value is empty.

<?php

require __DIR__ . '/vendor/autoload.php';

class MyFilter extends Zend\InputFilter\InputFilter
{
    public function init()
    {
        $this->add([
            'name' => 'field1',
            'required' => true,
            'validators' => [
                [
                    'name' => 'Zend\Validator\NotEmpty',
                    'break_chain_on_failure' => true,
                    'options' => [
                        'messages' => [
                            'isEmpty' => "Field1 cannot be empty",
                        ],
                    ],
                ],
            ],
        ]);

        $this->add([
            'name' => 'field2',
            'required' => true,
            'validators' => [
                [
                    'name' => 'Zend\Validator\NotEmpty',
                    'break_chain_on_failure' => true,
                    'options' => [
                        'messages' => [
                            'isEmpty' => "Field2 cannot be empty",
                        ],
                    ],
                ],
            ],
        ]);
    }
}

$app = Zend\Mvc\Application::init([
    'modules' => [
    ],

    'module_listener_options' => [
        'module_paths' => [
        ],

        'config_glob_paths' => [
        ],
    ],

    'input_filters' => [
        'invokables' => [
            'MyFilter' => 'MyFilter',
        ],
    ],
]);

$manager = $app->getServiceManager()->get('InputFilterManager');
$filter = $manager->get('MyFilter');

echo "\n";
echo "#------------------------------\n";
echo "# Test case 1\n";
echo "#------------------------------\n";
echo "\n";
$filter->setData([
]);

if (!$filter->isValid()) {
    var_dump($filter->getMessages());
}

echo "\n";
echo "#------------------------------\n";
echo "# Test case 2\n";
echo "#------------------------------\n";
echo "\n";
$filter->setData([
    'field1' => [
        'test',
    ],
]);

if (!$filter->isValid()) {
    var_dump($filter->getMessages());
}

The output I get is:

#------------------------------
# Test case 1
#------------------------------

array(2) {
  'field1' =>
  array(1) {
    'isEmpty' =>
    string(22) "Field1 cannot be empty"
  }
  'field2' =>
  array(1) {
    'isEmpty' =>
    string(22) "Field2 cannot be empty"
  }
}

#------------------------------
# Test case 2
#------------------------------

array(1) {
  'field2' =>
  array(1) {
    [0] =>
    string(17) "Value is required"
  }
}

Make it possible to override the 'Value is required' message

At the moment the 'Value is required' error message is hard coded into the validateInputs() method of BaseInputFilter. It would be very handy if it was possible to override that message, e.g. in an options array like in most classes in zend-validator.

[Refactor] ZF3

Hi everyone,

Some years ago I've started a big refactor of Input Filter. You can see the initial work there: zf-fr/input-filter#1

The idea was to take advantage of stateless component, by re-using refactored validator and filter components (made stateless too). It also aimed to be much faster, as well as simplify some concepts (mostly related to continue_if_empty, allow_empty... although I don't remember the exact details as it's been a long time).

I won't work on this before we have refactored the validator and filter as it would be wasted work, but I open the thread just for refernece for people wanting to erefactor this one.

Filter to boolean and non-required input

I noticed some strange behaviour today:

$input= new \Zend\InputFilter\Input();
$input->setValue("0");
$input->getFilterChain()->attachByName('boolean');
$input->setRequired(false);

$input->isValid(); //return false

The input gets filtered to a boolean value (false) before being validated. Because both allowEmpty and continueIfEmpty are false by default the NotEmpty validator is attached to the validator chain. The value "false" gets passed to the NotEmpty validator which, correctly, marks it as "invalid".

This results in strange "This field is required...." messages after validation on non-required inputs, while boolean false is a perfectly valid, non-empty value for e.g. a checkbox element.

Can't create optional input

I need an optional input lets say I expect data like {name: "some name", age: 18} or {name: "some name"} but don't want {name: "some name" age: null} and this is impossible to do now or I can't find a way how to do it.
As an example I wrote a test for this, of course this does not pass:

class OptionalInputTest extends PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider dataProvider
     */
    public function testNewFeature($data, $isValid)
    {
        $inputFilter = new \Zend\InputFilter\InputFilter();
        $inputFilter->add([
            'name' => 'foo',
            'required' => false,
            'allow_empty' => false,
            'continue_if_empty' => true,
            'validators' => [
                ['name' => 'StringLength', 'options' => ['min' => 2]],
            ],
        ]);

        $inputFilter->setData($data);

        if ($isValid) {
            $this->assertTrue($inputFilter->isValid());
        } else {
            $this->assertFalse($inputFilter->isValid());
        }
    }

    public function dataProvider()
    {
        return [
            [['foo' => null], false],
            [['foo' => ''], false],
            [[], true],
            [['foo' => "bar"], true]
        ];
    }
}

Maybe I overthinking this?

Value is required message for optional field

// somewhere in config
    'input_filter_specs' => [
        'test_filter' => [
            'value' => [
                'required' => false
            ],
        ],

// usage
$filters = $container->get('InputFilterManager');
$filter = $filters->get('test_filter');
$filter->setData([
    'value' => false
]);
$filter->isValid(); // false
$filter->getMessages(); // 'Value is required and can't be empty' O_O

One of possible fix is #138

Avoiding the default NotEmpty::IS_EMPTY validation message.

I have a form extending Zend\Form\Form implementing the Zend\InputFilter\InputFilterProviderInterface. One of my elements in my form is a Radio with some values and in my InputFilterProvider spec I have a NotEmpty validator with a custom message. Then, in my view I am using the FormRadio view helper to render the element which of course, also renders errors.

The problem, is that if the user submits the form witout selecting a radio option, my validators are not used and instead the default isEmpty message is used from the NotEmpty validator. There is no way to bypass or customize this as the logic prevents any other use. Either, it doesn't have a value but isn't required, or it doesn't have a value, is required, and then fails with the default message. Neither works for my use case as the message is not very nice to the user.

Unless I'm going to check for the default message or manually set the error when it isn't valid in my controller, I'm not sure what the best course of action is aside from making a PR that adds some type of customization to both Input and the factory.

Check Documentation For Other Things

Check docs for other problems

TLDR; Cast your eye over the documetation for any problems not covered in the other issues

Things slip through the net, so check the documentation for other problems that have been missed. Common other problems include

  • Bullet lists (should be single * then space at the start of line)
  • Inline code - should be marked by three backticks at start and finish
  • bookdown.json file is correctly formated and has the right escaping
  • Links between documentation using RST have been stripped
  • Any other RST has been removed correctly
  • Anything and everything not covered

If you end up fixing the same problem over and over, please ping Gary Hockin - we may be able to add bespoke issue for that problem, or fix in automated capacity

CollectionInputFilter wrong result messages

Let's say I have an InputFilter

use Zend\InputFilter\InputFilter;
use Zend\Filter\StringTrim;
use Zend\Filter\StripTags;
use Zend\InputFilter\Input;
use Zend\Validator\EmailAddress;
use Zend\Validator\NotEmpty;

class MyInputFilter extends InputFilter
{
    public function __construct()
    {
        $email = new Input('email');
        $email->setRequired(true);
        $email->getFilterChain()
            ->attach(new StringTrim())
            ->attach(new StripTags());

        $email->getValidatorChain()
            ->attach(new EmailAddress())
            ->attach(new NotEmpty());
        $this->add($email);

        $name = new Input('name');
        $name->setRequired(true);
        $name->getFilterChain()
            ->attach(new StringTrim())
            ->attach(new StripTags());

        $name->getValidatorChain()
            ->attach(new NotEmpty());
        $this->add($name);
    }
}

and the logic that goes

use Zend\InputFilter\CollectionInputFilter;

$collection = new CollectionInputFilter();
$collection->setInputFilter(new MyInputFilter());
$collection->setData([
    [
        'name' => 'Tom',
    ],
    [
        'email' => 'tom@tom',
        'name' => 'Tom'
    ],
]);
var_dump($collection->isValid(), $collection->getMessages());

I get

bool(false)
array(2) {
  [0]=>
  array(1) {
    ["email"]=>
    array(1) {
      ["isEmpty"]=>
      string(36) "Value is required and can't be empty" // which is fine here
    }
  }
  [1]=>
  array(1) {
    ["email"]=>
    array(1) {
      ["isEmpty"]=>
      string(36) "Value is required and can't be empty" // which is not fine
    }
  }
}  

second's element email message should be

    array(3) {
      ["emailAddressInvalidHostname"]=>
      string(53) "'tom' is not a valid hostname for the email address"
      ["hostnameInvalidHostname"]=>
      string(66) "The input does not match the expected structure for a DNS hostname"
      ["hostnameLocalNameNotAllowed"]=>
      string(84) "The input appears to be a local network name but local network names are not allowed"
    }

looks like a bug.

Inputfilter getValues() returns the field which is not in input data

$specs =  array(
    'title' => array(
        'name'       => 'title',
        'filters' => array(
            array(
                'name' => 'Zend\Filter\StringToLower',
            ),
        ),
    ),
);

$inputFilterFactory = new \Zend\InputFilter\Factory();
$inputFilter = $inputFilterFactory->createInputFilter($specs);
$data = array(
    'title' => 'TEST',
);
$inputFilter->setData($data);
var_dump($inputFilter->getValues());

Expected Output:

array(1) {
  ["title"]=>
  string(3) "test"
}
$data = array(
    'xyz' => 'something',
);
$inputFilter->setData($data);
var_dump($inputFilter->getValues());

Unexpected (or it is intentional?) Output:

array(1) {
  ["title"]=> NULL
}

[WIP] [ZF3] Refactor input filter component

This issue has been moved from the zendframework repository as part of the bug migration program as outlined here - http://framework.zend.com/blog/2016-04-11-issue-closures.html


Original Issue: https://api.github.com/repos/zendframework/zendframework/issues/4772
User: @bakura10
Created On: 2013-07-02T12:52:46Z
Updated At: 2014-09-28T00:34:50Z
Body
Hi everyone,

This is a branch I'm working on on my free time, to refactor the input filter component for ZF 3. I mostly push this for people who want to see the code and help.

It is not yet usable because it relies on some refactoring I'm doing in both Validator and Filter components. It completely embraces the service manager, so now we should not have the VERY annoying problem of adding custom validators/filters, and not being able to use them using the short name because it uses plugin managers that were not fetched from SM.

Under the hood, the component is completely rewritten and now uses SPL RecursiveFilter. From my first experiments, this allow an around x5-8 performance improvements and a memory consumption divided by 13 for complex input filters (magic happen here: https://github.com/bakura10/zf2/blob/943abe025ed578c4068cf19df079b34726d449a3/library/Zend/InputFilter/BaseInputFilter.php#L274-L300)

What I've seen is that the input filter component got more and more bloated, and it is now slow as hell. Checking the isValid method as it is now makes it quite clear where the problem is.

Therefore, for ZF3, I suggest that the base input filter to stay as simple as possible, and people wanting to have custom behaviors to extend the class and override the isValid for their use-cases (as a more general rule, I'd like this for most components too).

To help this, it also uses internally SPL RecursiveFilter. Basically, it allows to have very powerful validation group rules. For instance, this branch features a ValidationGroupRegex filter, and usage would be as follow:

$inputFilter = new InputFilter();
// … populate the input filter with lot of fields

// You can use the as-usual validation group:
$inputFilter->setValidationGroup(array('username', 'email'));

// But also some more complex rules. Here it only validate fields that contain 'registration-'
// in their names
$validationGroupRegex = new ValidationGroupRegexFilter($inputFilter, '/registration-/i')
$inputFilter->setValidationGroupFilter($validationGroupRegex);

Those filters are really easy to write. They must extend AbstractValidationGroupFilter and implements the "accept" method (like this: https://github.com/bakura10/zf2/blob/943abe025ed578c4068cf19df079b34726d449a3/library/Zend/InputFilter/Filter/ValidationGroupRegexFilter.php)

What do you think ? :)

NOTE : not all features have been back ported yet.


Comment

User: @desem
Created On: 2013-07-02T13:33:40Z
Updated At: 2013-07-02T13:33:40Z
Body
And I like it :)
http://www.ivangospodinow.com/simple-form-validator-for-zend-framework-2-forms


Comment

User: @bakura10
Created On: 2013-07-02T13:38:22Z
Updated At: 2013-07-02T13:38:22Z
Body
Thanks :). I change a little bit the logic so now any recursive iterator filter can be specific, so you can use the RecursiveCallbackFilterIterator:

$filter = new RecursiveCallbackFilterIterator($inputFilter, function($value, $key)) {
    if ($key === 'toto') {
        return false;
    }

    return true;
}

This will only validate fields except the one different than "toto".

Pretty powerful :D.


Comment

User: @desem
Created On: 2013-07-02T13:56:33Z
Updated At: 2013-07-02T13:56:33Z
Body
It's interesting. But I would start with a common forum for zend :)


Comment

User: @texdc
Created On: 2013-07-04T19:13:03Z
Updated At: 2013-07-04T19:13:03Z
Body
Please see my notes.


Comment

User: @bakura10
Created On: 2013-07-04T19:23:45Z
Updated At: 2013-07-04T19:23:45Z
Body
I answered :).


Comment

User: @bakura10
Created On: 2013-08-09T09:51:01Z
Updated At: 2013-08-09T09:51:01Z
Body
I tried to add more exciting stuff to this refactor. Now you can add a callback recursive filter to act as a validation group:

$inputFilter = new InputFilter();
$recursiveFilter = new ValidationGroupCallbackFilter($inputFilter, function($value, $key, $iterator) {
   // This get called for each element, so you can reject easily
   if ($key === 'lol') {
       return false;
   }

   return true;
});

$inputFilter->isValid();

Comment

User: @bakura10
Created On: 2013-08-09T10:17:43Z
Updated At: 2013-08-09T10:17:43Z
Body
Based on ocramius feedback, I've renamed the InputFilter class to InputCollection, which may be clearer for people. What do you think?

The only problem is that people may be confused between the InputCollection and CollectionInputCollection (previously CollectionInputFilter). I'll try to have a better idea for this.


Comment

User: @texdc
Created On: 2013-08-09T15:06:32Z
Updated At: 2013-08-09T15:06:32Z
Body
I like the 'InputCollection' name! I've just started to use 'CollectionInputFilter', so I understand the issue. Why not just 'CollectionInput'?

On Aug 9, 2013, at 5:17 AM, Michaël Gallego [email protected] wrote:

Based on ocramius feedback, I've renamed the InputFilter class to InputCollection, which may be clearer for people. What do you think?

The only problem is that people may be confused between the InputCollection and CollectionInputCollection (previously CollectionInputFilter). I'll try to have a better idea for this.


Reply to this email directly or view it on GitHub.


Comment

User: @bakura10
Created On: 2013-08-10T08:21:41Z
Updated At: 2013-08-10T08:21:41Z
Body
@texdc , I'm not even sure the CollectionInputFilter is really needed. After all, the collection input filter does nothing more than applying the same input filter to a collection. Can't we simply run the same input filter against different set of data ? (if I managed to make the input filter stateless it's going to be even more true… but turning it stateless is really hard…)


Comment

User: @bakura10
Created On: 2013-08-10T08:39:19Z
Updated At: 2013-08-10T08:42:23Z
Body
By the way, here are some performance figures. I simply created a single input filter. Please note that my current implementation does a little more than the current one as each element are created using a factory (both input and input collection), so that now the global validator and filter plugin managers are always correctly injected (this was always hard in current implementation). Please note also that a major time during construction is instead spent on the service locator, therefore I've decided to create the element using new instead of using the plugin manager (after doing this test I realized how service manager is slow).

In order not to make the test wrong, no validator nor filters are attached to elements. This test just take into account the traversal time.

With 50 elements

ZF2 :
Input filter construction time: 0.0015020370483398 msec
Traversal time: 0.0062439441680908 msec
Memory consumption: 3 Mb

ZF3 :
Input filter construction time: 0.00066399574279785 msec
Traversal time: 0.0010840892791748 msec
Memory consumption: 2,75 Mb

With 500 elements

ZF2 :
Input filter construction time: 0.010568141937256 msec
Traversal time: 0.058469772338867 msec
Memory consumption: 6 Mb

ZF3 :
Input filter construction time: 0.0047299861907959 msec
Traversal time: 0.0099430084228516 msec
Memory consumption: 3,75 Mb

With 5000 elements

ZF2 :
Input filter construction time: 0.1059877872467 msec
Traversal time: 0.60562801361084 msec
Memory consumption: 37 Mb

ZF3 :
Input filter construction time: 0.048290014266968 msec
Traversal time: 0.10112690925598 msec
Memory consumption: 11,75 Mb

We can expect the difference to be even more when using nested input filters (which is quite common), but already, the implementation that uses RecursiveIteratorIterator, once most important features were back ported, traversal is around 6 times faster and construction 2 times faster, and consumes a lot less memory with a lot of elements.

Furthermore, all the validation group features, even with complex ones like Callback, come nearly for free with RecursiveIteratorIterator.


Comment

User: @bakura10
Created On: 2013-09-02T15:54:03Z
Updated At: 2013-09-02T15:54:03Z
Body
Hi everyone,

I've taken into account a lot of feedbacks and I've completely rewritten some parts of it.

The big news is that now input filter component is completely stateless. Both input and input collection. Because of this, I had to abandon the RecursiveIteratorIterator implementation that was not flexible enough. Now it uses IteratorIterator instead. As a consequence, perf improvements are a bit less drastic (especially in construction time).

How to use it

Now, input filter component is stateless. This means that there is no longer "isValid" method. Instead, you are expected to use the "validate" method:

// Note: the Factory dependency will be hanlded by a ZF factory
$inputCollection = new InputCollection(new Factory(new InputFilterManager()));

$inputCollection->add(array(
    'name' => 'foo'
));

$data = array('foo' => 'myValue');

$validationResult = $inputCollection->validate($data, $optionalContext);

if ($validationResult->isValid()) {
    $values = $validationResult->getValues();
    // or $rawValues = $validationResult->getRawValues()
} else {
    $error = $validationResult->getErrorMessages();
}

Because validation result is completely decoupled, it can now be serialized or converted to JSON:

json_encode($validationResult); // Output error messages in JSON

Validation result

One of the main goal of this refactor was, as I said it, to simplify it and remove a lot of crap because of everyone adding its edge cases.

Instead, I've written several methods that make it easy to extend default behaviour:

  • People can extend InputCollection and override the "buildValidationResult", if they need to construct a custom validation result that also contains unknown inputs, for instance.
  • You can also extend "buildErrorMessages", which is called for each input collection.

Also, the validation group feature got more powerful thanks to SPL filters. Iteration is not made using a simple foreach, but using an IteratorIterator:

$iterator         = $this->getValidationGroupFilter();
$iteratorIterator = new IteratorIterator($iterator);
$errorMessages    = array();

/** @var InputInterface|InputCollectionInterface $inputOrInputCollection */
foreach ($iteratorIterator as $inputOrInputCollection) {

SPL filter iterators are automatically executed for us and allow to accept or refuse a specific key.

By default, ZF attaches a "Zend\InputFilter\ValidationGroup\NoOpFilterIterator" filter that accepts all fields (it is the same as setting an empty array as validation group). But you can also attach more complex filter. For instance using an array:

$arrayFilter = new ArrayFilterIterator($inputCollection, array('foo'));
$inputCollection->setValidationGroupFilter($arrayFilter);

Now, only the "foo" input of this input collection will be validated.

It comes with other filter:

$callableFilter = new CallbackFilterIterator($inputCollection, function($key, $value) {});
$regexFilter = new RegexFilterIterator($inputCollection, '/user/');

This should open the way to very fine filtering, as an input collection can contain another input collection that contains its own validation group filter. In the future, I'll write the code to be able to specifcy an input filter through config:

$inputCollection->add(
    'type' => 'Zend\InputFilter\InputCollection',
    'name' => 'top',
    'children' => array(
        array('name' => 'bar')
    ),
    'validation_group_filter' => array(
        'type' => 'Zend\InputFilter\ValidationGroup\RegexFilterIterator',
        'options' => array(
            'regex' => '/blabla/'
        )
    )
));

Performance

I've made some performance test, once again without adding any validators/filters so that the test focus only on input filter:

ZF2:

5 elements,
3 input collections with 5 elements inside

Construction time : 0.00055599212646484 secondes
Traversal time : 0.0064549446105957 secondes
Memory: 3 Mb

50 elements,
10 input collections with 50 elements inside

Construction time : 0.0096118450164795 secondes
Traversal time : 0.11807513237 secondes
Memory: 6,5 Mb

100 elements,
25 input collections with 100 elements inside

Construction time : 0.033029079437256 secondes
Traversal time : 0.51178097724915 secondes
Memory: 19,75 Mb

ZF3

5 elements,
3 input collections with 5 elements inside

Construction time : 0.00034809112548828 secondes
Traversal time : 0.0011918544769287 secondes
Memory: 2,5 Mb

50 elements,
10 input collections with 50 elements inside

Construction time : 0.0085010528564453 secondes
Traversal time : 0.01695990562439 secondes
Memory: 2,75 Mb

100 elements,
25 input collections with 100 elements inside

Construction time : 0.031938076019287 secondes
Traversal time : 0.060458183288574 secondes
Memory: 4,25 Mb


Comment

User: @Ocramius
Created On: 2013-09-02T16:17:17Z
Updated At: 2013-09-02T16:17:17Z
Body
As discussed with @bakura10 on IRC, we should have the same interface for Input and InputFilter. An InputFilter is just a normal Input with an array or Traversable value. Otherwise, I see no difference with the Input API.

Any pluralization of method names in the API shall be removed.


Comment

User: @bakura10
Created On: 2013-09-02T16:22:36Z
Updated At: 2013-09-02T16:24:21Z
Body
Done.

So now InputCollection extends Input, and have the same "validate" method. I didn't change it yet but Input should return a ValidationResult too.

The question now is that InputFilter will receive an array of ValidationResult, so we must find an efficient way to inject the result into the main ValidationResult.

As @Ocramius suggested, I've alos removed the fallback value capability. In fact this is a Filter job. So maybe we should create a FallbackValueFilter, but this sohuld not be handled in Input Filter component.


Comment

User: @bakura10
Created On: 2013-09-02T17:47:51Z
Updated At: 2013-09-02T17:48:47Z
Body
I have integrated several new things:

  • Input collection can now have their own validators and filters. When an input collection is validated, it FIRST execute its own validators, and then execute the validators of all its children. On the other hand, when data is filtered, all children are filtered, THEN the filters attached to the collection is executed. This allow to have, for instance, a Date input collection with three inputs, and validate or filter based on this context. This should open some nice simplifications to the form component, especially with the Collection element.
  • The validation process is a bit complicatd, I'm not sure about hte best way to handle it.

Currently, whenever an input collection is validated, a new ValidationResult is built. If the input collection contains no errors, then the values are filtered. This means that in the following hiearrchy:

Input filter 1
    input 1
    input 2
    input filter 2
        input 3
        input 4

if input 3, 4 and input filter 2 are valid, then the values are filtered EVEN THOUGH input 1 failed. The filter step is "on an input collection" basis.

This may not be the most efficient way because we need to do the recursion twice... If you have any better idea, please shout!

I also need people to evaluate this: https://github.com/bakura10/zf2/blob/d0cfc2e6643732b1e5e020f6154a6617e7e4d20d/library/Zend/InputFilter/InputCollection.php#L157


Comment

User: @bakura10
Created On: 2013-09-02T19:14:05Z
Updated At: 2013-09-02T19:14:05Z
Body
I've been using IteratorAggregate, which is around 30% faster than implement IteratorIterator. We are now even faster than when using RecursiveIteratorIterator :D. Some funny benchmark with HEAVY input filters (450 inputs, and 50 sub-input collections with 450 elements inside):

ZF2:

Construction time : 0.47534704208374 secondes
Traversal time : 7.0274820327759 secondes
Memory consumption: 154 Mb

ZF3:

Construction time : 0.48701810836792 secondes
Traversal time : 0.83196616172791 secondes
Memory consumption: 18 Mb


Comment

User: @Netiul
Created On: 2013-09-03T06:58:37Z
Updated At: 2013-09-03T06:58:37Z
Body
Impressive, I must say. Keep up the great work!


Comment

User: @bakura10
Created On: 2013-09-03T09:58:39Z
Updated At: 2013-09-03T09:58:39Z
Body
Thanks @Netiul .

I've just noticed something strange, I'd like your feedback. Since this time, I've always thought that the input filters does : validation using the raw data, then filtering. However, it seems that I was wrong: https://github.com/zendframework/zf2/blob/master/library/Zend/InputFilter/Input.php#L309

The filtering is done prior to the validation. Is this something expected? Is this normal behaviour and what we want? I'm always lost about this.


Comment

User: @bakura10
Created On: 2013-09-03T12:26:28Z
Updated At: 2013-09-03T12:26:28Z
Body
Another massive performance improvement for today (around 20%), because the whole input collection is now traversed only once instead of twice.

This is possible thanks to the previous remark about filtering data, some talk with @Ocramius, and the new stateless that allows interesting optimizations.

First, the ValidationResult has been renamed to InputFilterResult. This is because I'll try to make Validator component completely stateless too. It will return ValidationResult, so it was important to make the two different by the naming.

About the performance improvement it comes from the reason of my previous comment:

I realized that current implementation of Input, the "isValid" method filter data before validating it. In fact, it makes sense (if you have an input with a "Alnum" validator and a trimming Filter, you want " abc " to be valid). The thing is that in ZF2, values were filtered twice: once when the "isValid" method of the input filter is called, and another time you do "getValues". This is useless work.

Now, when the "validate" method for an input is called, as the value is filtered anyway, it's stored directly in the ValidationResult, and aggregated by the ValidationResult of the Input Collection. So calling "getData" from the input collection does not incur another recursion.

The second thing is that as I said before, InputCollection now can have their validators and filters (which shuld remove the need of the previous CollectionInputFilter class). I'm not sure about the workflow:

Let's assume the following input collection, called "coll", which contains two inputs "a", "b".

When calling validate, the following happens:

  1. The InputCollection validators (if any) are run. The array that the validator will receive will by ["a" => ..., "b" => ...].
    1.1 If invalid, register error messages.
    1.2 If input collection is configured to break on failure, we immediately return an input filter result.
  2. Otherwise, each inputs (a and b) are filtered and validated.
  3. If everything is correct, the input filter result is built, and the input collection filters are run for the value (in this case, the filtered values of a and b).

Does the order make sense to everyone?

Another question I have is: if an input has failed (not valid), should we insert the filtered value into the input filter result ?

Remark: because InputFilter is a stateless component now, I think we can safely switch the plugin manager to share instance by default. This should be a pretty memory consumption improvment for people using a lot of input filters.


Comment

User: @dphn
Created On: 2013-09-03T12:35:53Z
Updated At: 2013-09-03T12:35:53Z
Body
Tell me, please, why do not you rename InputFilter to InputSpecification? After all, it contains both filters and validators?


Comment

User: @bakura10
Created On: 2013-09-03T12:37:35Z
Updated At: 2013-09-03T12:37:35Z
Body
??? You're mixing everything again... InputSpecifciation does not exist. No classes have been renamed to InputSpecification.

InputFilter has been however renamed to InputCollection, because it represents more what it is (a collection of inputs).


Comment

User: @dphn
Created On: 2013-09-03T12:38:54Z
Updated At: 2013-09-03T12:38:54Z
Body
Thank you, I get it. So really better.


Comment

User: @weierophinney
Created On: 2013-09-03T16:10:05Z
Updated At: 2013-09-03T16:10:05Z
Body

The filtering is done prior to the validation. Is this something expected? Is this normal behaviour and what we want? I'm always lost about this.

@bakura10 :

Yes, this is normal behavior, and has been since ZF1. The idea is that you do normalization tasks, and then validate the normalized data (this is particularly useful for things like phone numbers and CC numbers, to allow stripping non-digit input such as spaces, dots, parens, etc). I've seen some requests in the past for a post-filter process as well, but the idea has never gained traction.


Comment

User: @bakura10
Created On: 2013-09-03T16:11:22Z
Updated At: 2013-09-03T16:11:22Z
Body
Ok thanks to confirm this then :).


Comment

User: @dphn
Created On: 2013-09-05T16:25:15Z
Updated At: 2013-09-05T16:25:15Z
Body
I wonder, in the third version will remain a mass func_get_arg () in the constructor of each validator?


Comment

User: @Ocramius
Created On: 2013-09-05T16:35:37Z
Updated At: 2013-09-05T16:35:37Z
Body
@dphn not sure if that's relevant to this PR - the main focus here is moving to a stateless interface first. Otherwise we'd need an Options object for every validator, which is probably a further step for a different PR


Comment

User: @dphn
Created On: 2013-09-05T16:42:16Z
Updated At: 2013-09-05T16:42:16Z
Body
I'm sorry, I just took the most active channel of communication :) Not anymore.


Comment

User: @dphn
Created On: 2013-09-05T22:00:49Z
Updated At: 2013-09-05T22:00:49Z
Body

The filtering is done prior to the validation. Is this something expected? Is this normal behaviour and what we want? I'm always lost about this.

It would be nice if it were true. I'm trying to add a filter in the multiupload form, but it is always executed after validators.


Comment

User: @bakura10
Created On: 2013-09-05T22:02:35Z
Updated At: 2013-09-05T22:02:35Z
Body
Yes, this is expected for file upload though and won't change. For file it makes sense to first validate and then filter (which include things like file upload).


Comment

User: @dphn
Created On: 2013-09-05T22:11:39Z
Updated At: 2013-09-05T22:11:39Z
Body
I think that uploading files is not a task to be performed by the filter. If the filters and validators can be added to Form as listeners, this problem would not exist. But what can I do in the current situation?


Comment

User: @bakura10
Created On: 2013-09-05T22:13:49Z
Updated At: 2013-09-05T22:13:49Z
Body
In fact, I was a bit vague. Filters does not "upload", they rename. Eventually, they could rename and move to something else, which would upload the file to another server. But if you think at it, "moving" or "renaming" can be the job of a filter.


Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.