BryanDavis has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/195204

Change subject: Add require and array methods to Form
......................................................................

Add require and array methods to Form

* Backport `require*` methods from Wikimania Scholarships
* Add `expect*Array` and `require*Array` methods that can validate array
  input.

Bug: T90387
Change-Id: I2be4532fb73297193e658f9d0a79946934bb989d
---
M src/Form.php
M tests/FormTest.php
2 files changed, 260 insertions(+), 9 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/wikimedia/iegreview 
refs/changes/04/195204/1

diff --git a/src/Form.php b/src/Form.php
index 4275362..b0a0926 100644
--- a/src/Form.php
+++ b/src/Form.php
@@ -18,7 +18,7 @@
  * <http://www.gnu.org/licenses/>.
  *
  * @file
- * @copyright © 2014 Bryan Davis, Wikimedia Foundation and contributors.
+ * @copyright © 2015 Bryan Davis, Wikimedia Foundation and contributors.
  */
 
 namespace Wikimedia\IEGReview;
@@ -111,6 +111,18 @@
                return $this->expect( $name, \FILTER_VALIDATE_BOOLEAN, $options 
);
        }
 
+       public function requireBool( $name, $options = null ) {
+               return $this->expectBool( $name, self::required( $options ) );
+       }
+
+       public function expectBoolArray( $name, $options = null ) {
+               return $this->expectBool( $name, self::wantArray( $options ) );
+       }
+
+       public function requireBoolArray( $name, $options = null ) {
+               return $this->requireBool( $name, self::wantArray( $options ) );
+       }
+
        public function expectTrue( $name, $options = null ) {
                $options = ( is_array( $options ) ) ? $options : array();
                $options['validate'] = function ($v) {
@@ -119,20 +131,80 @@
                return $this->expectBool( $name, $options );
        }
 
+       public function requireTrue( $name, $options = null ) {
+               return $this->expectTrue( $name, self::required( $options ) );
+       }
+
+       public function expectTrueArray( $name, $options = null ) {
+               return $this->expectTrue( $name, self::wantArray( $options ) );
+       }
+
+       public function requireTrueArray( $name, $options = null ) {
+               return $this->requireTrue( $name, self::wantArray( $options ) );
+       }
+
        public function expectEmail( $name, $options = null ) {
                return $this->expect( $name, \FILTER_VALIDATE_EMAIL, $options );
+       }
+
+       public function requireEmail( $name, $options = null ) {
+               return $this->expectEmail( $name, self::required( $options ) );
+       }
+
+       public function expectEmailArray( $name, $options = null ) {
+               return $this->expectEmail( $name, self::wantArray( $options ) );
+       }
+
+       public function requireEmailArray( $name, $options = null ) {
+               return $this->requireEmail( $name, self::wantArray( $options ) 
);
        }
 
        public function expectFloat( $name, $options = null ) {
                return $this->expect( $name, \FILTER_VALIDATE_FLOAT, $options );
        }
 
+       public function requireFloat( $name, $options = null ) {
+               return $this->expectFloat( $name, self::required( $options ) );
+       }
+
+       public function expectFloatArray( $name, $options = null ) {
+               return $this->expectFloat( $name, self::wantArray( $options ) );
+       }
+
+       public function requireFloatArray( $name, $options = null ) {
+               return $this->requireFloat( $name, self::wantArray( $options ) 
);
+       }
+
        public function expectInt( $name, $options = null ) {
                return $this->expect( $name, \FILTER_VALIDATE_INT, $options );
        }
 
+       public function requireInt( $name, $options = null ) {
+               return $this->expectInt( $name, self::required( $options ) );
+       }
+
+       public function expectIntArray( $name, $options = null ) {
+               return $this->expectInt( $name, self::wantArray( $options ) );
+       }
+
+       public function requireIntArray( $name, $options = null ) {
+               return $this->requireInt( $name, self::wantArray( $options ) );
+       }
+
        public function expectIp( $name, $options = null ) {
                return $this->expect( $name, \FILTER_VALIDATE_IP, $options );
+       }
+
+       public function requireIp( $name, $options = null ) {
+               return $this->expectIp( $name, self::required( $options ) );
+       }
+
+       public function expectIpArray( $name, $options = null ) {
+               return $this->expectIp( $name, self::wantArray( $options ) );
+       }
+
+       public function requireIpArray( $name, $options = null ) {
+               return $this->requireIp( $name, self::wantArray( $options ) );
        }
 
        public function expectRegex( $name, $re, $options = null ) {
@@ -141,25 +213,91 @@
                return $this->expect( $name, \FILTER_VALIDATE_REGEXP, $options 
);
        }
 
+       public function requireRegex( $name, $re, $options = null ) {
+               return $this->expectRegex( $name, $re, self::required( $options 
) );
+       }
+
+       public function expectRegexArray( $name, $re, $options = null ) {
+               return $this->expectRegex( $name, $re, self::wantArray( 
$options ) );
+       }
+
+       public function requireRegexArray( $name, $re, $options = null ) {
+               return $this->requireRegex( $name, $re, self::wantArray( 
$options ) );
+       }
+
        public function expectUrl( $name, $options = null ) {
                return $this->expect( $name, \FILTER_VALIDATE_URL, $options );
+       }
+
+       public function requireUrl( $name, $options = null ) {
+               return $this->expectUrl( $name, self::required( $options ) );
+       }
+
+       public function expectUrlArray( $name, $options = null ) {
+               return $this->expectUrl( $name, self::wantArray( $options ) );
+       }
+
+       public function requireUrlArray( $name, $options = null ) {
+               return $this->requireUrl( $name, self::wantArray( $options ) );
        }
 
        public function expectString( $name, $options = null ) {
                return $this->expectRegex( $name, '/^.+$/s', $options );
        }
 
+       public function requireString( $name, $options = null ) {
+               return $this->expectString( $name, self::required( $options ) );
+       }
+
+       public function expectStringArray( $name, $options = null ) {
+               return $this->expectString( $name, self::wantArray( $options ) 
);
+       }
+
+       public function requireStringArray( $name, $options = null ) {
+               return $this->requireString( $name, self::wantArray( $options ) 
);
+       }
+
        public function expectAnything( $name, $options = null ) {
                return $this->expect( $name, \FILTER_UNSAFE_RAW, $options );
+       }
+
+       public function requireAnything( $name, $options = null ) {
+               return $this->expectAnything( $name, self::required( $options ) 
);
+       }
+
+       public function expectAnythingArray( $name, $options = null ) {
+               return $this->expectAnything( $name, self::wantArray( $options 
) );
+       }
+
+       public function requireAnythingArray( $name, $options = null ) {
+               return $this->requireAnything( $name, self::wantArray( $options 
) );
        }
 
        public function expectInArray( $name, $valids, $options = null ) {
                $options = ( is_array( $options ) ) ? $options : array();
                $required = isset( $options['required'] ) ? 
$options['required'] : false;
-               $options['validate'] = function ($val) use ($valids, $required) 
{
+               $options['validate'] = function ( $val ) use ( $valids, 
$required ) {
                        return ( !$required && empty( $val ) ) || in_array( 
$val, $valids );
                };
                return $this->expectAnything( $name, $options );
+       }
+
+       public function requireInArray( $name, $valids, $options = null ) {
+               return $this->expectInArray( $name, $valids,
+                       self::required( $options )
+               );
+       }
+
+       public function expectInArrayArray( $name, $valids, $options = null ) {
+               return $this->expectInArray(
+                       $name, $values, self::wantArray( $options )
+               );
+       }
+
+       public function requireInArrayArray( $name, $valids, $options = null ) {
+               return $this->requireInArray(
+                       $name, $values, self::wantArray( $options )
+               );
        }
 
        /**
@@ -172,17 +310,44 @@
                $vars = $vars ?: $_POST;
                $this->values = array();
                $this->errors = array();
+               $arrayInvalids = array();
+
+               $cleaned = filter_var_array( $vars, $this->params );
 
                foreach ( $this->params as $name => $opt ) {
-                       $var = isset( $vars[$name] ) ? $vars[$name] : null;
-                       // Bypass filter_var if the input is null. This keeps 
filter_var
-                       // from "helping" by converting nulls to empty strings.
-                       $clean = ( $var === null ) ? $var : filter_var( $var, 
$opt['filter'], $opt );
+                       $clean = isset( $vars[$name] ) ? $cleaned[$name] : null;
 
                        if ( $clean === false &&
                                $opt['filter'] !== \FILTER_VALIDATE_BOOLEAN
                        ) {
                                $this->values[$name] = null;
+
+                       } elseif ( is_array( $clean ) &&
+                               ( $opt['flags'] & \FILTER_REQUIRE_ARRAY ) &&
+                               $opt['filter'] !== \FILTER_VALIDATE_BOOLEAN
+                       ) {
+                               // Strip invalid value markers from input array
+                               $this->values[$name] = array();
+                               foreach ( $clean as $key => $value ) {
+                                       if ( $opt['filter'] !== 
\FILTER_VALIDATE_BOOLEAN &&
+                                               $value !== false
+                                       ) {
+                                               $this->values[$name][$key] = 
$value;
+
+                                       } elseif ( $opt['filter'] === 
\FILTER_VALIDATE_BOOLEAN &&
+                                               $value !== null
+                                       ) {
+                                               $this->values[$name][$key] = 
$value;
+
+                                       } else {
+                                               // Keep track of invalid keys 
in case input was
+                                               // required
+                                               if ( !isset( 
$arrayInvalids[$name] ) ) {
+                                                       $arrayInvalids[$name] = 
array();
+                                               }
+                                               $arrayInvalids[$name][] = 
"{$name}[{$key}]";
+                                       }
+                               }
 
                        } else {
                                $this->values[$name] = $clean;
@@ -190,6 +355,11 @@
 
                        if ( $opt['required'] && $this->values[$name] === null 
) {
                                $this->errors[] = $name;
+
+                       } elseif ( $opt['required'] && isset( 
$arrayInvalids[$name] ) ) {
+                               $this->errors = array_merge(
+                                       $this->errors, $arrayInvalids[$name]
+                               );
 
                        } elseif ( is_callable( $opt['validate'] ) &&
                                call_user_func( $opt['validate'], 
$this->values[$name] ) === false
@@ -274,4 +444,26 @@
                return self::urlEncode( array_diff_key( $_GET, array_flip( 
$params ) ) );
        }
 
+       /**
+        * Ensure that the given options collection contains a 'required' key.
+        *
+        * @param array $options
+        * @return array
+        */
+       protected static function required( $options ) {
+               return array_merge( array( 'required' => true ), 
(array)$options );
+       }
+
+       /**
+        * Ensure that the given options collection contains a 'flags' key that
+        * requires the input to be an array.
+        *
+        * @param array $options
+        * @return array
+        */
+       protected static function wantArray( $options ) {
+               $options = array_merge( array( 'flags' => 0 ), (array)$options 
);
+               $options['flags'] = $options['flags'] | \FILTER_REQUIRE_ARRAY;
+               return $options;
+       }
 }
diff --git a/tests/FormTest.php b/tests/FormTest.php
index 51bdd23..19130ae 100644
--- a/tests/FormTest.php
+++ b/tests/FormTest.php
@@ -33,7 +33,7 @@
 
        public function testRequired () {
                $form = new Form();
-               $form->expectString( 'foo', array( 'required' => true ) );
+               $form->requireString( 'foo' );
 
                $this->assertFalse( $form->validate(), 'Form should be invalid' 
);
                $vals = $form->getValues();
@@ -56,7 +56,7 @@
 
        public function testNotInArray () {
                $form = new Form();
-               $form->expectInArray( 'foo', array( 'bar' ), array( 'required' 
=> true ) );
+               $form->requireInArray( 'foo', array( 'bar' ) );
 
                $this->assertFalse( $form->validate(), 'Form should be invalid' 
);
                $vals = $form->getValues();
@@ -68,7 +68,7 @@
        public function testInArray () {
                $_POST['foo'] = 'bar';
                $form = new Form();
-               $form->expectInArray( 'foo', array( 'bar' ), array( 'required' 
=> true ) );
+               $form->requireInArray( 'foo', array( 'bar' ) );
 
                $this->assertTrue( $form->validate(), 'Form should be valid' );
                $vals = $form->getValues();
@@ -89,6 +89,65 @@
                $this->assertNotContains( 'foo', $form->getErrors() );
        }
 
+       public function testMultipleInputs () {
+               $data = array(
+                       'bool' => false,
+                       'true' => true,
+                       'email' => 'user!user2+ro...@example.wiki',
+                       'float' => 1.23,
+                       'int' => 123,
+                       'ipv4' => '127.0.0.1',
+                       'ipv6' => '::1',
+                       'regex' => 'abc',
+                       'url' => 'proto://host.tld/path',
+                       'str' => 'one two three',
+               );
+               $form = new Form();
+               $form->requireBool( 'bool' );
+               $form->requireTrue( 'true' );
+               $form->requireEmail( 'email' );
+               $form->requireFloat( 'float' );
+               $form->requireInt( 'int' );
+               $form->requireIp( 'ipv4' );
+               $form->requireIp( 'ipv6' );
+               $form->requireRegex( 'regex', '/^ABC$/i' );
+               $form->requireUrl( 'url' );
+               $form->requireString( 'str' );
+
+               $this->assertTrue( $form->validate( $data ), 'Form should be 
valid' );
+               $this->assertSame( $data, $form->getValues() );
+       }
+
+       public function testArrayValues () {
+               $data = array(
+                       'bool' => array( true, false ),
+                       'float' => array( 1.23, 4.56 ),
+                       'int' => array( 123, 456 ),
+                       'str' => array( 'one', 'two', 'three'),
+               );
+               $form = new Form();
+               $form->requireBoolArray( 'bool' );
+               $form->requireFloatArray( 'float' );
+               $form->requireIntArray( 'int' );
+               $form->requireStringArray( 'str' );
+
+               $this->assertTrue( $form->validate( $data ), 'Form should be 
valid' );
+               $this->assertSame( $data, $form->getValues() );
+       }
+
+       public function testArrayValueErrors () {
+               $data = array(
+                       'int' => array( 123, 456, 'xyzzy', '678' ),
+               );
+               $form = new Form();
+               $form->requireIntArray( 'int' );
+               $this->assertFalse( $form->validate( $data ), 'Form should not 
be valid' );
+               $vals = $form->getValues();
+               $this->assertArrayHasKey( 'int', $vals );
+               $this->assertSame( array( 123, 456, 3 => 678 ), $vals['int'] );
+               $this->assertContains( 'int[2]', $form->getErrors() );
+       }
+
        public function testEncodeBasic () {
                $input = array(
                        'foo' => 1,

-- 
To view, visit https://gerrit.wikimedia.org/r/195204
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I2be4532fb73297193e658f9d0a79946934bb989d
Gerrit-PatchSet: 1
Gerrit-Project: wikimedia/iegreview
Gerrit-Branch: master
Gerrit-Owner: BryanDavis <bda...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to