Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fix for HTML5 multiple attribute on file element

  • Loading branch information...
commit e9ac49d8ad363f23c9691ef936c8a13c039d96b6 1 parent 420b811
@cgmartin authored
View
34 library/Zend/Form/Element/File.php
@@ -26,7 +26,8 @@
use Zend\Form\ElementPrepareAwareInterface;
use Zend\Form\Exception;
use Zend\InputFilter\InputProviderInterface;
-use Zend\Stdlib\ErrorHandler;
+use Zend\Validator\File\Explode as FileExplodeValidator;
+use Zend\Validator\File\Upload as FileUploadValidator;
/**
* @category Zend
@@ -47,6 +48,11 @@ class File extends Element implements InputProviderInterface, ElementPrepareAwar
);
/**
+ * @var ValidatorInterface
+ */
+ protected $validator;
+
+ /**
* Prepare the form element (mostly used for rendering purposes)
*
* @param Form $form
@@ -59,6 +65,30 @@ public function prepareElement(Form $form)
}
/**
+ * Get validator
+ *
+ * @return ValidatorInterface
+ */
+ protected function getValidator()
+ {
+ if (null === $this->validator) {
+ $validator = new FileUploadValidator();
+
+ $multiple = (isset($this->attributes['multiple']))
+ ? $this->attributes['multiple'] : null;
+
+ if (true === $multiple || 'multiple' === $multiple) {
+ $validator = new FileExplodeValidator(array(
+ 'validator' => $validator
+ ));
+ }
+
+ $this->validator = $validator;
+ }
+ return $this->validator;
+ }
+
+ /**
* Should return an array specification compatible with
* {@link Zend\InputFilter\Factory::createInput()}.
*
@@ -71,7 +101,7 @@ public function getInputSpecification()
'name' => $this->getName(),
'required' => false,
'validators' => array(
- array('name' => 'fileupload'),
+ $this->getValidator(),
),
);
}
View
28 library/Zend/Form/View/Helper/FormFile.php
@@ -57,10 +57,32 @@ protected function getType(ElementInterface $element)
*/
public function render(ElementInterface $element)
{
+ $name = $element->getName();
+ if ($name === null || $name === '') {
+ throw new Exception\DomainException(sprintf(
+ '%s requires that the element has an assigned name; none discovered',
+ __METHOD__
+ ));
+ }
+
+ $attributes = $element->getAttributes();
+ $attributes['type'] = $this->getType($element);
+ $attributes['name'] = $name;
+ if (array_key_exists('multiple', $attributes) && $attributes['multiple']) {
+ $attributes['name'] .= '[]';
+ }
+
$value = $element->getValue();
- if (is_array($value) && isset($value['name'])) {
- $element->setValue($value['name']);
+ if (is_array($value) && isset($value['name']) && !is_array($value['name'])) {
+ $attributes['value'] = $value['name'];
+ } elseif (is_string($value)) {
+ $attributes['value'] = $value;
}
- return parent::render($element);
+
+ return sprintf(
+ '<input %s%s',
+ $this->createAttributesString($attributes),
+ $this->getInlineClosingBracket()
+ );
}
}
View
6 library/Zend/InputFilter/BaseInputFilter.php
@@ -165,7 +165,11 @@ public function isValid()
if (!array_key_exists($name, $this->data)
|| (null === $this->data[$name])
|| (is_string($this->data[$name]) && strlen($this->data[$name]) === 0)
- || (isset($this->data[$name]['error']) && $this->data[$name]['error'] === UPLOAD_ERR_NO_FILE)
+ // Single and Multi File Uploads
+ || (is_array($this->data[$name])
+ && isset($this->data[$name]['error']) && $this->data[$name]['error'] === UPLOAD_ERR_NO_FILE)
+ || (is_array($this->data[$name]) && count($this->data[$name]) === 1
+ && isset($this->data[$name][0]['error']) && $this->data[$name][0]['error'] === UPLOAD_ERR_NO_FILE)
) {
if ($input instanceof InputInterface) {
// - test if input is required
View
76 library/Zend/Validator/File/Explode.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Validator
+ */
+
+namespace Zend\Validator\File;
+
+use Zend\Validator\Exception;
+use Zend\Validator\Explode as BaseExplode;
+
+/**
+ * @category Zend
+ * @package Zend_Validate
+ */
+class Explode extends BaseExplode
+{
+ const INVALID = 'fileExplodeInvalid';
+
+ /**
+ * @var array
+ */
+ protected $messageTemplates = array(
+ self::INVALID => "Invalid type given. File array expected",
+ );
+
+ /**
+ * Defined by Zend_Validate_Interface
+ *
+ * Returns true if all values validate true
+ *
+ * @param array $value
+ * @return boolean
+ * @throws Exception\RuntimeException
+ */
+ public function isValid($value)
+ {
+ if (!is_array($value)) {
+ $this->error(self::INVALID);
+ return false;
+ }
+
+ $values = $value;
+ $this->setValue($value);
+
+ $retval = true;
+ $messages = array();
+ $validator = $this->getValidator();
+
+ if (!$validator) {
+ throw new Exception\RuntimeException(sprintf(
+ '%s expects a validator to be set; none given',
+ __METHOD__
+ ));
+ }
+
+ foreach ($values as $value) {
+ if (!$validator->isValid($value)) {
+ $messages[] = $validator->getMessages();
+ $retval = false;
+
+ if ($this->isBreakOnFirstFailure()) {
+ break;
+ }
+ }
+ }
+
+ $this->abstractOptions['messages'] = $messages;
+
+ return $retval;
+ }
+}
View
1  library/Zend/Validator/ValidatorPluginManager.php
@@ -71,6 +71,7 @@ class ValidatorPluginManager extends AbstractPluginManager
'fileexcludeextension' => 'Zend\Validator\File\ExcludeExtension',
'fileexcludemimetype' => 'Zend\Validator\File\ExcludeMimeType',
'fileexists' => 'Zend\Validator\File\Exists',
+ 'fileexplode' => 'Zend\Validator\File\Explode',
'fileextension' => 'Zend\Validator\File\Extension',
'filehash' => 'Zend\Validator\File\Hash',
'fileimagesize' => 'Zend\Validator\File\ImageSize',
View
18 tests/ZendTest/Form/Element/FileTest.php
@@ -31,6 +31,24 @@ public function testProvidesDefaultInputSpecification()
$this->assertInstanceOf('Zend\Validator\File\Upload', $validators[0]['instance']);
}
+ public function testProvidesDefaultInputSpecificationForMultiple()
+ {
+ $element = new FileElement('foo');
+ $element->setAttribute('multiple', true);
+ $this->assertEquals('file', $element->getAttribute('type'));
+
+ $inputSpec = $element->getInputSpecification();
+ $factory = new InputFilterFactory();
+ $input = $factory->createInput($inputSpec);
+ $this->assertInstanceOf('Zend\InputFilter\FileInput', $input);
+
+ $validators = $input->getValidatorChain()->getValidators();
+ $this->assertNotEmpty($validators);
+ $validator = $validators[0]['instance'];
+ $this->assertInstanceOf('Zend\Validator\File\Explode', $validator);
+ $this->assertInstanceOf('Zend\Validator\File\Upload', $validator->getValidator());
+ }
+
public function testWillAddFileEnctypeAttributeToForm()
{
$file = new FileElement('foo');
View
54 tests/ZendTest/Form/View/Helper/FormFileTest.php
@@ -20,39 +20,54 @@
*/
class FormFileTest extends CommonTestCase
{
+ /**
+ * @return void
+ */
public function setUp()
{
$this->helper = new FormFileHelper();
parent::setUp();
}
+ /**
+ * @return void
+ */
public function testRaisesExceptionWhenNameIsNotPresentInElement()
{
- $element = new Element();
+ $element = new Element\File();
$this->setExpectedException('Zend\Form\Exception\DomainException', 'name');
$this->helper->render($element);
}
+ /**
+ * @return void
+ */
public function testGeneratesFileInputTagWithElement()
{
- $element = new Element('foo');
+ $element = new Element\File('foo');
$markup = $this->helper->render($element);
$this->assertContains('<input ', $markup);
$this->assertContains('type="file"', $markup);
}
+ /**
+ * @return void
+ */
public function testGeneratesFileInputTagRegardlessOfElementType()
{
- $element = new Element('foo');
+ $element = new Element\File('foo');
$element->setAttribute('type', 'email');
$markup = $this->helper->render($element);
$this->assertContains('<input ', $markup);
$this->assertContains('type="file"', $markup);
}
+ /**
+ * @return void
+ */
public function testRendersElementWithFileArrayValue()
{
- $element = new Element('foo');
+ $element = new Element\File('foo');
$element->setValue(array(
'tmp_name' => '/tmp/foofile',
'name' => 'foofile',
@@ -66,6 +81,9 @@ public function testRendersElementWithFileArrayValue()
$this->assertContains('value="foofile"', $markup);
}
+ /**
+ * @return array
+ */
public function validAttributes()
{
return array(
@@ -88,7 +106,7 @@ public function validAttributes()
array('max', 'assertNotContains'),
array('maxlength', 'assertNotContains'),
array('min', 'assertNotContains'),
- array('multiple', 'assertContains'),
+ array('multiple', 'assertNotContains'),
array('pattern', 'assertNotContains'),
array('placeholder', 'assertNotContains'),
array('readonly', 'assertNotContains'),
@@ -101,9 +119,12 @@ public function validAttributes()
);
}
+ /**
+ * @return Element\File
+ */
public function getCompleteElement()
{
- $element = new Element('foo');
+ $element = new Element\File('foo');
$element->setAttributes(array(
'accept' => 'value',
'alt' => 'value',
@@ -124,7 +145,7 @@ public function getCompleteElement()
'max' => 'value',
'maxlength' => 'value',
'min' => 'value',
- 'multiple' => 'multiple',
+ 'multiple' => false,
'name' => 'value',
'pattern' => 'value',
'placeholder' => 'value',
@@ -157,15 +178,32 @@ public function testAllValidFormMarkupAttributesPresentInElementAreRendered($att
$this->$assertion($expect, $markup);
}
+ /**
+ * @return void
+ */
+ public function testNameShouldHaveArrayNotationWhenMultipleIsSpecified()
+ {
+ $element = new Element\File('foo');
+ $element->setAttribute('multiple', true);
+ $markup = $this->helper->render($element);
+ $this->assertRegexp('#<input[^>]*?(name="foo\[\]")#', $markup);
+ }
+
+ /**
+ * @return void
+ */
public function testInvokeProxiesToRender()
{
- $element = new Element('foo');
+ $element = new Element\File('foo');
$markup = $this->helper->__invoke($element);
$this->assertContains('<input', $markup);
$this->assertContains('name="foo"', $markup);
$this->assertContains('type="file"', $markup);
}
+ /**
+ * @return void
+ */
public function testInvokeWithNoElementChainsHelper()
{
$this->assertSame($this->helper, $this->helper->__invoke());
View
31 tests/ZendTest/InputFilter/BaseInputFilterTest.php
@@ -443,6 +443,37 @@ public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoFileDataIsPr
$this->assertFalse($filter->isValid());
}
+ public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoMultiFileDataIsPresent()
+ {
+ $filter = new InputFilter();
+
+ $explode = new Validator\File\Explode();
+ $explode->setValidator(new Validator\File\Upload());
+
+ $foo = new FileInput();
+ $foo->getValidatorChain()->addValidator($explode);
+ $foo->setRequired(false);
+
+ $filter->add($foo, 'foo');
+
+ $data = array(
+ 'foo' => array(array(
+ 'tmp_name' => '/tmp/barfile',
+ 'name' => 'barfile',
+ 'type' => 'text',
+ 'size' => 0,
+ 'error' => 4, // UPLOAD_ERR_NO_FILE
+ )),
+ );
+ $filter->setData($data);
+ $this->assertTrue($filter->isValid());
+
+ // Negative test
+ $foo->setRequired(true);
+ $filter->setData($data);
+ $this->assertFalse($filter->isValid());
+ }
+
public function testValidationAllowsEmptyValuesToRequiredInputWhenAllowEmptyFlagIsTrue()
{
$filter = new InputFilter();
View
103 tests/ZendTest/Validator/File/ExplodeTest.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @link http://github.com/zendframework/zf2 for the canonical source repository
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @package Zend_Validator
+ */
+
+namespace ZendTest\Validator\File;
+
+use Zend\Validator\File\Explode as FileExplode;
+
+/**
+ * @category Zend
+ * @package Zend_Validator
+ * @subpackage UnitTests
+ * @group Zend_Validator
+ */
+class ExplodeTest extends \PHPUnit_Framework_TestCase
+{
+ public function testRaisesExceptionWhenValidatorIsMissing()
+ {
+ $validator = new FileExplode();
+ $this->setExpectedException('Zend\Validator\Exception\RuntimeException', 'validator');
+
+ $file = array(array(
+ 'name' => "test.jpg",
+ 'type' => 'image/jpeg',
+ 'tmp_name' => "/private/tmp/php0Pnzdi",
+ 'error' => 0,
+ 'size' => 344215,
+ ));
+ $validator->isValid($file);
+ }
+
+ public function getExpectedData()
+ {
+ $files = array();
+ for ($i = 0; $i < 3; $i++) {
+ $files[] = array(
+ 'name' => "test_$i.jpg",
+ 'type' => 'image/jpeg',
+ 'tmp_name' => "/private/tmp/php0Pnzdi$i",
+ 'error' => 0,
+ 'size' => 344215,
+ );
+ }
+ $file = array($files[0]);
+
+ return array(
+ // value break N valid messages expects
+ array($files, false, 3, true, array(), true),
+ array($files, true, 1, false, array('X'), false),
+ array($files, false, 3, false, array('X', 'X', 'X'), false),
+ array($file, false, 1, true, array(), true),
+ array($file, false, 1, false, array('X'), false),
+ array($file, true, 1, false, array('X'), false),
+ array('foo', false, 0, true, array(FileExplode::INVALID => 'Invalid'), false),
+ array(1, false, 0, true, array(FileExplode::INVALID => 'Invalid'), false),
+ );
+ }
+
+ /**
+ * @dataProvider getExpectedData
+ */
+ public function testExpectedBehavior($value, $breakOnFirst, $numIsValidCalls, $isValidReturn, $messages, $expects)
+ {
+ $mockValidator = $this->getMock('Zend\Validator\ValidatorInterface');
+ $mockValidator->expects($this->exactly($numIsValidCalls))->method('isValid')->will($this->returnValue($isValidReturn));
+ $mockValidator->expects($this->any())->method('getMessages')->will($this->returnValue('X'));
+
+ $validator = new FileExplode(array(
+ 'validator' => $mockValidator,
+ 'breakOnFirstFailure' => $breakOnFirst,
+ ));
+ $validator->setMessage('Invalid', FileExplode::INVALID);
+
+ $this->assertEquals($expects, $validator->isValid($value));
+ $this->assertEquals($messages, $validator->getMessages());
+ }
+
+ public function testGetMessagesReturnsDefaultValue()
+ {
+ $validator = new FileExplode();
+ $this->assertEquals(array(), $validator->getMessages());
+ }
+
+ public function testEqualsMessageTemplates()
+ {
+ $validator = new FileExplode(array());
+ $this->assertAttributeEquals($validator->getOption('messageTemplates'),
+ 'messageTemplates', $validator);
+ }
+
+ public function testEqualsMessageVariables()
+ {
+ $validator = new FileExplode(array());
+ $this->assertAttributeEquals($validator->getOption('messageVariables'),
+ 'messageVariables', $validator);
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.