Skip to content
This repository was archived by the owner on Jan 30, 2020. It is now read-only.

Commit 4b43b82

Browse files
committed
Merge branch 'hotfix/155-inputfilter-precedence' into develop
Conflicts: CHANGELOG.md
2 parents 8b57c60 + a6ba46a commit 4b43b82

6 files changed

+187
-29
lines changed

CHANGELOG.md

+27
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ All notable changes to this project will be documented in this file, in reverse
99
- [#142](https://github.com/zendframework/zend-inputfilter/pull/142) adds
1010
support for PHP 7.2.
1111

12+
### Changed
13+
14+
- Nothing.
15+
1216
### Deprecated
1317

1418
- Nothing.
@@ -22,6 +26,29 @@ All notable changes to this project will be documented in this file, in reverse
2226

2327
- Nothing.
2428

29+
## 2.7.6 - 2017-12-04
30+
31+
### Added
32+
33+
- Nothing.
34+
35+
### Deprecated
36+
37+
- Nothing.
38+
39+
### Removed
40+
41+
- Nothing.
42+
43+
### Fixed
44+
45+
- [#156](https://github.com/zendframework/zend-inputfilter/pull/156) fixes an
46+
issue introduced in 2.7.5 whereby the filter and validator chains composed in
47+
inputs pulled from the `InputFilterPluginManager` were not receiving the
48+
default filter and validator plugin manager instances. A solution was created
49+
that preserves the original behavior as well as the bugfix that created the
50+
regression.
51+
2552
## 2.7.5 - 2017-11-07
2653

2754
### Added

src/CollectionInputFilter.php

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ public function setData($data)
166166

167167
/**
168168
* {@inheritdoc}
169+
* @param mixed $context Ignored, but present to retain signature compatibility.
169170
*/
170171
public function isValid($context = null)
171172
{

src/Factory.php

+46-6
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,9 @@ public function createInput($inputSpecification)
189189
));
190190
}
191191

192-
if (! $managerInstance && $this->defaultFilterChain) {
193-
$input->setFilterChain(clone $this->defaultFilterChain);
194-
}
195-
if (! $managerInstance && $this->defaultValidatorChain) {
196-
$input->setValidatorChain(clone $this->defaultValidatorChain);
197-
}
192+
$managerInstance
193+
? $this->injectFilterAndValidatorChainsWithPluginManagers($input)
194+
: $this->injectDefaultFilterAndValidatorChains($input);
198195

199196
foreach ($inputSpecification as $key => $value) {
200197
switch ($key) {
@@ -434,4 +431,47 @@ protected function populateValidators(ValidatorChain $chain, $validators)
434431
);
435432
}
436433
}
434+
435+
/**
436+
* Inject the default filter and validator chains into the input, if present.
437+
*
438+
* This ensures custom plugins are made available to the input instance.
439+
*
440+
* @param InputInterface $input
441+
* @return void
442+
*/
443+
protected function injectDefaultFilterAndValidatorChains(InputInterface $input)
444+
{
445+
if ($this->defaultFilterChain) {
446+
$input->setFilterChain(clone $this->defaultFilterChain);
447+
}
448+
449+
if ($this->defaultValidatorChain) {
450+
$input->setValidatorChain(clone $this->defaultValidatorChain);
451+
}
452+
}
453+
454+
/**
455+
* Inject filter and validator chains with the plugin managers from
456+
* the default chains, if present.
457+
*
458+
* This ensures custom plugins are made available to the input instance.
459+
*
460+
* @param InputInterface $input
461+
* @return void
462+
*/
463+
protected function injectFilterAndValidatorChainsWithPluginManagers(InputInterface $input)
464+
{
465+
if ($this->defaultFilterChain) {
466+
$input->getFilterChain()
467+
? $input->getFilterChain()->setPluginManager($this->defaultFilterChain->getPluginManager())
468+
: $input->setFilterChain(clone $this->defaultFilterChain);
469+
}
470+
471+
if ($this->defaultValidatorChain) {
472+
$input->getValidatorChain()
473+
? $input->getValidatorChain()->setPluginManager($this->defaultValidatorChain->getPluginManager())
474+
: $input->setValidatorChain(clone $this->defaultValidatorChain);
475+
}
476+
}
437477
}

test/CollectionInputFilterTest.php

+21-23
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
use Zend\InputFilter\Exception\RuntimeException;
2020
use Zend\InputFilter\Input;
2121
use Zend\InputFilter\InputFilter;
22-
use Zend\Validator\EmailAddress;
22+
use Zend\Validator\Digits;
2323
use Zend\Validator\NotEmpty;
2424

2525
/**
@@ -512,10 +512,10 @@ public function testCollectionValidationDoesNotReuseMessagesBetweenInputs()
512512
{
513513
$inputFilter = new InputFilter();
514514
$inputFilter->add([
515-
'name' => 'email',
515+
'name' => 'phone',
516516
'required' => true,
517517
'validators' => [
518-
['name' => EmailAddress::class],
518+
['name' => Digits::class],
519519
['name' => NotEmpty::class],
520520
],
521521
]);
@@ -535,7 +535,7 @@ public function testCollectionValidationDoesNotReuseMessagesBetweenInputs()
535535
'name' => 'Tom',
536536
],
537537
[
538-
'email' => 'tom@tom',
538+
'phone' => 'tom@tom',
539539
'name' => 'Tom',
540540
],
541541
]);
@@ -547,27 +547,25 @@ public function testCollectionValidationDoesNotReuseMessagesBetweenInputs()
547547
$this->assertFalse($isValid);
548548
$this->assertCount(2, $messages);
549549

550-
$this->assertArrayHasKey('email', $messages[0]);
551-
$this->assertCount(1, $messages[0]['email']);
552-
$this->assertContains('Value is required and can\'t be empty', $messages[0]['email']);
550+
$this->assertArrayHasKey('phone', $messages[0]);
551+
$this->assertCount(1, $messages[0]['phone']);
552+
$this->assertContains('Value is required and can\'t be empty', $messages[0]['phone']);
553553

554-
$this->assertArrayHasKey('email', $messages[1]);
555-
$this->assertCount(3, $messages[1]['email']);
556-
$this->assertNotContains('Value is required and can\'t be empty', $messages[1]['email']);
557-
$this->assertContains('\'tom\' is not a valid hostname for the email address', $messages[1]['email']);
558-
$this->assertContains('The input does not match the expected structure for a DNS hostname', $messages[1]['email']);
559-
$this->assertContains('The input appears to be a local network name but local network names are not allowed', $messages[1]['email']);
554+
$this->assertArrayHasKey('phone', $messages[1]);
555+
$this->assertCount(1, $messages[1]['phone']);
556+
$this->assertNotContains('Value is required and can\'t be empty', $messages[1]['phone']);
557+
$this->assertContains('The input must contain only digits', $messages[1]['phone']);
560558
// @codingStandardsIgnoreEnd
561559
}
562560

563561
public function testCollectionValidationUsesCustomInputErrorMessages()
564562
{
565563
$inputFilter = new InputFilter();
566564
$inputFilter->add([
567-
'name' => 'email',
565+
'name' => 'phone',
568566
'required' => true,
569567
'validators' => [
570-
['name' => EmailAddress::class],
568+
['name' => Digits::class],
571569
['name' => NotEmpty::class],
572570
],
573571
'error_message' => 'CUSTOM ERROR MESSAGE',
@@ -588,7 +586,7 @@ public function testCollectionValidationUsesCustomInputErrorMessages()
588586
'name' => 'Tom',
589587
],
590588
[
591-
'email' => 'tom@tom',
589+
'phone' => 'tom@tom',
592590
'name' => 'Tom',
593591
],
594592
]);
@@ -600,14 +598,14 @@ public function testCollectionValidationUsesCustomInputErrorMessages()
600598
$this->assertFalse($isValid);
601599
$this->assertCount(2, $messages);
602600

603-
$this->assertArrayHasKey('email', $messages[0]);
604-
$this->assertCount(1, $messages[0]['email']);
605-
$this->assertContains('CUSTOM ERROR MESSAGE', $messages[0]['email']);
606-
$this->assertNotContains('Value is required and can\'t be empty', $messages[0]['email']);
601+
$this->assertArrayHasKey('phone', $messages[0]);
602+
$this->assertCount(1, $messages[0]['phone']);
603+
$this->assertContains('CUSTOM ERROR MESSAGE', $messages[0]['phone']);
604+
$this->assertNotContains('Value is required and can\'t be empty', $messages[0]['phone']);
607605

608-
$this->assertArrayHasKey('email', $messages[1]);
609-
$this->assertCount(1, $messages[1]['email']);
610-
$this->assertContains('CUSTOM ERROR MESSAGE', $messages[1]['email']);
606+
$this->assertArrayHasKey('phone', $messages[1]);
607+
$this->assertCount(1, $messages[1]['phone']);
608+
$this->assertContains('CUSTOM ERROR MESSAGE', $messages[1]['phone']);
611609
}
612610

613611
public function testDuplicatedErrorMessages()

test/FactoryTest.php

+20
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPUnit_Framework_MockObject_MockObject as MockObject;
1414
use PHPUnit\Framework\TestCase;
1515
use Prophecy\Argument;
16+
use ReflectionProperty;
1617
use Zend\Filter;
1718
use Zend\InputFilter\CollectionInputFilter;
1819
use Zend\InputFilter\Exception\InvalidArgumentException;
@@ -999,9 +1000,11 @@ public function testClearDefaultValidatorChain()
9991000

10001001
/**
10011002
* @see https://github.com/zendframework/zend-inputfilter/issues/8
1003+
* @see https://github.com/zendframework/zend-inputfilter/issues/155
10021004
*/
10031005
public function testWhenCreateInputPullsInputFromThePluginManagerItMustNotOverwriteFilterAndValidatorChains()
10041006
{
1007+
10051008
$input = $this->prophesize(InputInterface::class);
10061009
$input->setFilterChain(Argument::any())->shouldNotBeCalled();
10071010
$input->setValidatorChain(Argument::any())->shouldNotBeCalled();
@@ -1015,6 +1018,23 @@ public function testWhenCreateInputPullsInputFromThePluginManagerItMustNotOverwr
10151018

10161019
$factory = new Factory($pluginManager->reveal());
10171020

1021+
$r = new ReflectionProperty($factory, 'defaultFilterChain');
1022+
$r->setAccessible(true);
1023+
$defaultFilterChain = $r->getValue($factory);
1024+
1025+
$filterChain = $this->prophesize(Filter\FilterChain::class);
1026+
$filterChain->setPluginManager($defaultFilterChain->getPluginManager())->shouldBeCalled();
1027+
1028+
$r = new ReflectionProperty($factory, 'defaultValidatorChain');
1029+
$r->setAccessible(true);
1030+
$defaultValidatorChain = $r->getValue($factory);
1031+
1032+
$validatorChain = $this->prophesize(Validator\ValidatorChain::class);
1033+
$validatorChain->setPluginManager($defaultValidatorChain->getPluginManager())->shouldBeCalled();
1034+
1035+
$input->getFilterChain()->will([$filterChain, 'reveal'])->shouldBeCalled();
1036+
$input->getValidatorChain()->will([$validatorChain, 'reveal'])->shouldBeCalled();
1037+
10181038
$this->assertSame($input->reveal(), $factory->createInput($spec));
10191039
}
10201040

test/InputFilterAbstractServiceFactoryTest.php

+72
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212

1313
use PHPUnit_Framework_MockObject_MockObject as MockObject;
1414
use PHPUnit\Framework\TestCase;
15+
use Zend\Filter;
1516
use Zend\Filter\FilterPluginManager;
17+
use Zend\InputFilter\FileInput;
1618
use Zend\InputFilter\InputFilterAbstractServiceFactory;
1719
use Zend\InputFilter\InputFilterInterface;
1820
use Zend\InputFilter\InputFilterPluginManager;
1921
use Zend\ServiceManager\ServiceManager;
22+
use Zend\Validator;
2023
use Zend\Validator\ValidatorInterface;
2124
use Zend\Validator\ValidatorPluginManager;
2225

@@ -301,4 +304,73 @@ public function testAllowsPassingNonPluginManagerContainerToFactoryWithServiceMa
301304
$inputFilter = call_user_func_array([$this->factory, $create], $args);
302305
$this->assertInstanceOf(InputFilterInterface::class, $inputFilter);
303306
}
307+
308+
/**
309+
* @see https://github.com/zendframework/zend-inputfilter/issues/155
310+
*/
311+
public function testWillUseCustomFiltersWhenProvided()
312+
{
313+
$filter = $this->prophesize(Filter\FilterInterface::class)->reveal();
314+
315+
$filters = new FilterPluginManager($this->services);
316+
$filters->setService('CustomFilter', $filter);
317+
318+
$validators = new ValidatorPluginManager($this->services);
319+
320+
$this->services->setService('FilterManager', $filters);
321+
$this->services->setService('ValidatorManager', $validators);
322+
323+
$this->services->setService('config', [
324+
'input_filter_specs' => [
325+
'test' => [
326+
[
327+
'name' => 'a-file-element',
328+
'type' => FileInput::class,
329+
'required' => true,
330+
'validators' => [
331+
[
332+
'name' => Validator\File\UploadFile::class,
333+
'options' => [
334+
'breakchainonfailure' => true,
335+
],
336+
],
337+
[
338+
'name' => Validator\File\Size::class,
339+
'options' => [
340+
'breakchainonfailure' => true,
341+
'max' => '6GB',
342+
],
343+
],
344+
[
345+
'name' => Validator\File\Extension::class,
346+
'options' => [
347+
'breakchainonfailure' => true,
348+
'extension' => 'csv,zip',
349+
],
350+
],
351+
],
352+
'filters' => [
353+
['name' => 'CustomFilter'],
354+
],
355+
],
356+
],
357+
],
358+
]);
359+
360+
$this->services->get('InputFilterManager')
361+
->addAbstractFactory(InputFilterAbstractServiceFactory::class);
362+
363+
$inputFilter = $this->services->get('InputFilterManager')->get('test');
364+
$this->assertInstanceOf(InputFilterInterface::class, $inputFilter);
365+
366+
$input = $inputFilter->get('a-file-element');
367+
$this->assertInstanceOf(FileInput::class, $input);
368+
369+
$filters = $input->getFilterChain();
370+
$this->assertCount(1, $filters);
371+
372+
$callback = $filters->getFilters()->top();
373+
$this->assertInternalType('array', $callback);
374+
$this->assertSame($filter, $callback[0]);
375+
}
304376
}

0 commit comments

Comments
 (0)