jenkins-bot has submitted this change and it was merged. 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(-) Approvals: Niharika29: Looks good to me, approved jenkins-bot: Verified 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: merged Gerrit-Change-Id: I2be4532fb73297193e658f9d0a79946934bb989d Gerrit-PatchSet: 1 Gerrit-Project: wikimedia/iegreview Gerrit-Branch: master Gerrit-Owner: BryanDavis <bda...@wikimedia.org> Gerrit-Reviewer: Niharika29 <niharikakohl...@gmail.com> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits