diff --git a/docs/Type.md b/docs/Type.md index 0a4e51eb..acf0e919 100644 --- a/docs/Type.md +++ b/docs/Type.md @@ -14,6 +14,7 @@ v::type('object')->validate(new stdClass()); // true Version | Description --------|------------- + 2.0.0 | Became case-sensitive 0.8.0 | Created *** diff --git a/library/Exceptions/TypeException.php b/library/Exceptions/TypeException.php index cf422411..4e9ee6ca 100644 --- a/library/Exceptions/TypeException.php +++ b/library/Exceptions/TypeException.php @@ -13,8 +13,17 @@ declare(strict_types=1); namespace Respect\Validation\Exceptions; -class TypeException extends ValidationException +/** + * Exceptions thrown by Type rule. + * + * @author Henrique Moody + * @author Paul Karikari + */ +final class TypeException extends ValidationException { + /** + * {@inheritdoc} + */ public static $defaultTemplates = [ self::MODE_DEFAULT => [ self::STANDARD => '{{name}} must be {{type}}', diff --git a/library/Rules/Type.php b/library/Rules/Type.php index e84ca7a7..34e41f66 100644 --- a/library/Rules/Type.php +++ b/library/Rules/Type.php @@ -14,11 +14,27 @@ declare(strict_types=1); namespace Respect\Validation\Rules; use Respect\Validation\Exceptions\ComponentException; +use function array_keys; +use function gettype; +use function implode; +use function is_callable; +use function sprintf; -class Type extends AbstractRule +/** + * Validates the type of input. + * + * @author Gabriel Caruso + * @author Henrique Moody + * @author Paul Karikari + */ +final class Type extends AbstractRule { - public $type; - public $availableTypes = [ + /** + * Collection of available types for validation. + * + * @var array + */ + private const AVAILABLE_TYPES = [ 'array' => 'array', 'bool' => 'boolean', 'boolean' => 'boolean', @@ -33,23 +49,44 @@ class Type extends AbstractRule 'string' => 'string', ]; - public function __construct($type) + /** + * Type to validate input against. + * + * @var string + */ + private $type; + + /** + * Initializes the rule. + * + * @param string $type + * + * @throws ComponentException When $type is not a valid one + */ + public function __construct(string $type) { - $lowerType = mb_strtolower($type); - if (!isset($this->availableTypes[$lowerType])) { - throw new ComponentException(sprintf('"%s" is not a valid type', print_r($type, true))); + if (!isset(self::AVAILABLE_TYPES[$type])) { + throw new ComponentException( + sprintf( + '"%s" is not a valid type (Available: %s)', + $type, + implode(', ', array_keys(self::AVAILABLE_TYPES)) + ) + ); } $this->type = $type; } + /** + * {@inheritdoc} + */ public function validate($input): bool { - $lowerType = mb_strtolower($this->type); - if ('callable' === $lowerType) { + if ('callable' === $this->type) { return is_callable($input); } - return $this->availableTypes[$lowerType] === gettype($input); + return self::AVAILABLE_TYPES[$this->type] === gettype($input); } } diff --git a/tests/integration/rules/type.phpt b/tests/integration/rules/type.phpt new file mode 100644 index 00000000..d2cb99e5 --- /dev/null +++ b/tests/integration/rules/type.phpt @@ -0,0 +1,37 @@ +--FILE-- +check('42'); +} catch (TypeException $exception) { + echo $exception->getMessage().PHP_EOL; +} + +try { + v::not(v::type('string'))->check('foo'); +} catch (TypeException $exception) { + echo $exception->getMessage().PHP_EOL; +} + +try { + v::type('double')->assert(20); +} catch (NestedValidationException $exception) { + echo $exception->getFullMessage().PHP_EOL; +} + +try { + v::not(v::type('bool'))->assert(true); +} catch (NestedValidationException $exception) { + echo $exception->getFullMessage().PHP_EOL; +} +?> +--EXPECTF-- +"42" must be "integer" +"foo" must not be "string" +- 20 must be "double" +- `TRUE` must not be "bool" diff --git a/tests/unit/Rules/TypeTest.php b/tests/unit/Rules/TypeTest.php index 0811acfd..1b0ef5e5 100644 --- a/tests/unit/Rules/TypeTest.php +++ b/tests/unit/Rules/TypeTest.php @@ -13,34 +13,23 @@ declare(strict_types=1); namespace Respect\Validation\Rules; -use PHPUnit\Framework\TestCase; +use Respect\Validation\Test\RuleTestCase; use stdClass; +use function tmpfile; /** * @group rule + * * @covers \Respect\Validation\Rules\Type - * @covers \Respect\Validation\Exceptions\TypeException + * + * @author Henrique Moody + * @author Paul Karikari */ -class TypeTest extends TestCase +class TypeTest extends RuleTestCase { - public function testShouldDefineTypeOnConstructor(): void - { - $type = 'int'; - $rule = new Type($type); - - self::assertSame($type, $rule->type); - } - - public function testShouldNotBeCaseSensitive(): void - { - $rule = new Type('InTeGeR'); - - self::assertTrue($rule->validate(42)); - } - /** * @expectedException \Respect\Validation\Exceptions\ComponentException - * @expectedExceptionMessage "whatever" is not a valid type + * @expectedExceptionMessage "whatever" is not a valid type (Available: array, bool, boolean, callable, double, float, int, integer, null, object, resource, string) */ public function testShouldThrowExceptionWhenTypeIsNotValid(): void { @@ -48,59 +37,35 @@ class TypeTest extends TestCase } /** - * @dataProvider providerForValidType + * {@inheritdoc} */ - public function testShouldValidateValidTypes($type, $input): void - { - $rule = new Type($type); - - self::assertTrue($rule->validate($input)); - } - - /** - * @dataProvider providerForInvalidType - */ - public function testShouldNotValidateInvalidTypes($type, $input): void - { - $rule = new Type($type); - - self::assertFalse($rule->validate($input)); - } - - /** - * @expectedException \Respect\Validation\Exceptions\TypeException - * @expectedExceptionMessage "Something" must be "integer" - */ - public function testShouldThrowTypeExceptionWhenCheckingAnInvalidInput(): void - { - $rule = new Type('integer'); - $rule->check('Something'); - } - - public function providerForValidType() + public function providerForValidInput(): array { return [ - ['array', []], - ['bool', true], - ['boolean', false], - ['callable', function (): void { + [new Type('array'), []], + [new Type('bool'), true], + [new Type('boolean'), false], + [new Type('callable'), function (): void { }], - ['double', 0.8], - ['float', 1.0], - ['int', 42], - ['integer', 13], - ['null', null], - ['object', new stdClass()], - ['resource', tmpfile()], - ['string', 'Something'], + [new Type('double'), 0.8], + [new Type('float'), 1.0], + [new Type('int'), 42], + [new Type('integer'), 13], + [new Type('null'), null], + [new Type('object'), new stdClass()], + [new Type('resource'), tmpfile()], + [new Type('string'), 'Something'], ]; } - public function providerForInvalidType() + /** + * {@inheritdoc} + */ + public function providerForInvalidInput(): array { return [ - ['int', '1'], - ['bool', '1'], + [new Type('int'), '1'], + [new Type('bool'), '1'], ]; } }