diff --git a/README.md b/README.md index 3189d48..8ba0403 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,12 @@ php artisan vendor:publish --provider="Arifszn\AdvancedValidation\ServiceProvide You can specify the error message on the fly when declaring the rules. Simple pass the error message parameter. ```php -use Arifszn\AdvancedValidation\Rules\Base64Image; +use Arifszn\AdvancedValidation\Rules\Username; public function rules() { return [ - 'avatar' => [new Base64Image('Your custom error message')], + 'foo' => [new Username('Your custom error message')], ]; } ``` @@ -52,8 +52,12 @@ public function rules() - [`Divisible By`](#divisibleby) - [`Ethereum Address`](#ethereumaddress) - [`Float Number`](#floatnumber) +- [`Hash`](#hash) - [`Image URL`](#imageurl) +- [`JWT`](#jwt) +- [`Name`](#name) - [`Phone`](#phone) +- [`Username`](#username) - [`Without Spaces`](#withoutspaces) @@ -61,6 +65,10 @@ public function rules() The field under validation must contain ASCII chars only. +``` +public Arifszn\AdvancedValidation\Rules\Ascii::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\Ascii; @@ -76,6 +84,10 @@ public function rules() The field under validation must be a Base64 encoded image. +``` +public Arifszn\AdvancedValidation\Rules\Base64Image::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\Base64Image; @@ -91,6 +103,10 @@ public function rules() The field under validation must be a Base64 encoded string. +``` +public Arifszn\AdvancedValidation\Rules\Base64String::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\Base64String; @@ -106,6 +122,10 @@ public function rules() The field under validation must be a BIC([Business Identifier Code](https://en.wikipedia.org/wiki/ISO_9362)) or SWIFT code. +``` +public Arifszn\AdvancedValidation\Rules\BIC::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\BIC; @@ -121,6 +141,10 @@ public function rules() The field under validation must be a valid BTC address. +``` +public Arifszn\AdvancedValidation\Rules\BtcAddress::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\BtcAddress; @@ -136,6 +160,10 @@ public function rules() The field under validation must be a valid credit card number. +``` +public Arifszn\AdvancedValidation\Rules\CreditCard::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\CreditCard; @@ -151,6 +179,10 @@ public function rules() The field under validation must have [data uri format](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs). +``` +public Arifszn\AdvancedValidation\Rules\DataURI::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\DataURI; @@ -166,6 +198,10 @@ public function rules() The field under validation must be divisible by the given number. +``` +public Arifszn\AdvancedValidation\Rules\DivisibleBy::__construct(int $number, string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\DivisibleBy; @@ -180,6 +216,10 @@ public function rules() ### `EthereumAddress` The field under validation must be an [Ethereum](https://ethereum.org/en/) address. Does not validate address checksums. +``` +public Arifszn\AdvancedValidation\Rules\EthereumAddress::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\EthereumAddress; @@ -195,6 +235,10 @@ public function rules() The field under validation must be a float number. +``` +public Arifszn\AdvancedValidation\Rules\FloatNumber::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\FloatNumber; @@ -206,6 +250,27 @@ public function rules() } ``` +### `Hash` + +The field under validation must be a hash of type algorithm. + +Algorithm is one of `'md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b'`. + +``` +public Arifszn\AdvancedValidation\Rules\Hash::__construct(string $algorithm, string $errorMessage = null) +``` + +```php +use Arifszn\AdvancedValidation\Rules\Hash; + +public function rules() +{ + return [ + 'foo' => [new Hash('md4')], + ]; +} +``` + ### `ImageURL` The field under validation must be a valid image URL. @@ -213,6 +278,10 @@ The field under validation must be a valid image URL. ✓ https://www.php.net/images/logos/php-logo.png \ ✕ https://imaginarysite123.com/invalid.png +``` +public Arifszn\AdvancedValidation\Rules\ImageURL::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\ImageURL; @@ -224,6 +293,48 @@ public function rules() } ``` +### `JWT` + +The field under validation must have a valid format of JWT ([JSON Web Token](https://en.wikipedia.org/wiki/JSON_Web_Token)). + +``` +public Arifszn\AdvancedValidation\Rules\Jwt::__construct(string $errorMessage = null) +``` + +```php +use Arifszn\AdvancedValidation\Rules\Jwt; + +public function rules() +{ + return [ + 'foo' => [new Jwt()], + ]; +} +``` + +### `Name` + +The field under validation must be a valid name. + +- no emoji +- no number (if $allowNumber flag is true, it will accept numbers, default is false) +- special characters are allowed (restricting special characters will cause false-negative for names like `Martin Luther King, Jr.` or `李小龍`) + +``` +public Arifszn\AdvancedValidation\Rules\Name::__construct(bool $allowNumber = false, string $errorMessage = null) +``` + +```php +use Arifszn\AdvancedValidation\Rules\Name; + +public function rules() +{ + return [ + 'name' => [new Name()], + ]; +} +``` + ### `Phone` The field under validation must be a valid phone number. @@ -233,6 +344,10 @@ The field under validation must be a valid phone number. ✓ (xxx) xxx-xxxx \ ✓ xxxxxxxxxx +``` +public Arifszn\AdvancedValidation\Rules\Phone::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\Phone; @@ -244,10 +359,38 @@ public function rules() } ``` +### `Username` + +The field under validation must be a valid username. + +- starts with a letter (alpha) +- only alpha-numeric (a-z, A-Z, 0-9), underscore, minus and dot +- multiple underscores, minus and are not allowed (-- or __ or ..) +- underscores, minus and dot are not allowed at the beginning or end + +``` +public Arifszn\AdvancedValidation\Rules\Username::__construct(string $errorMessage = null) +``` + +```php +use Arifszn\AdvancedValidation\Rules\Username; + +public function rules() +{ + return [ + 'username' => [new Username()], + ]; +} +``` + ### `WithoutSpaces` The field under validation must not contain spaces. +``` +public Arifszn\AdvancedValidation\Rules\WithoutSpaces::__construct(string $errorMessage = null) +``` + ```php use Arifszn\AdvancedValidation\Rules\WithoutSpaces; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index ff042fa..1992eab 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -1,4 +1,3 @@ - 'The :attribute must have data uri format.', 'divisible_by' => 'The :attribute must be divisible by :number.', 'ethereum_address' => 'The :attribute must be an Ethereum address.', - 'float_number' => 'The :attribute must be a float number', + 'float_number' => 'The :attribute must be a float number.', + 'hash' => 'The :attribute must be a hash of :algorithm algorithm.', 'image_url' => 'The :attribute must be a valid image URL.', + 'jwt' => 'The :attribute must have a valid format of JWT.', + 'name' => 'The :attribute must be a valid name.', 'phone' => 'The :attribute must be a valid phone number.', + 'username' => 'The :attribute must be a valid username.', 'without_spaces' => 'The :attribute must not contain spaces.', ]; diff --git a/src/Rules/Hash.php b/src/Rules/Hash.php new file mode 100644 index 0000000..c480681 --- /dev/null +++ b/src/Rules/Hash.php @@ -0,0 +1,103 @@ +algorithm = $algorithm; + $this->errorMessage = $errorMessage; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + $this->attribute = $attribute; + + try { + $hash = '/^[a-fA-F0-9]{' . $this->getLength($this->algorithm) . '}$/'; + + return preg_match($hash, $value); + } catch (\Throwable $th) { + return false; + } + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return $this->errorMessage ? $this->errorMessage : trans('advancedValidation::validation.hash', [ + 'attribute' => $this->attribute, + 'algorithm' => $this->algorithm + ]); + } + + /** + * Get length. + * + * @param string $algorithm + * @return int + */ + private function getLength(string $algorithm) + { + $lengths = [ + 'md5' => 32, + 'md4' => 32, + 'sha1' => 40, + 'sha256' => 64, + 'sha384' => 96, + 'sha512' => 128, + 'ripemd128' => 32, + 'ripemd160' => 40, + 'tiger128' => 32, + 'tiger160' => 40, + 'tiger192' => 48, + 'crc32' => 8, + 'crc32b' => 8, + ]; + + return $lengths[$algorithm]; + } +} diff --git a/src/Rules/Jwt.php b/src/Rules/Jwt.php new file mode 100644 index 0000000..a2a1b26 --- /dev/null +++ b/src/Rules/Jwt.php @@ -0,0 +1,51 @@ +errorMessage = $errorMessage; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + return preg_match('/^[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+$/', $value); + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return $this->errorMessage ? $this->errorMessage : trans('advancedValidation::validation.jwt'); + } +} diff --git a/src/Rules/Name.php b/src/Rules/Name.php new file mode 100644 index 0000000..9604dee --- /dev/null +++ b/src/Rules/Name.php @@ -0,0 +1,76 @@ +allowNumber = $allowNumber; + $this->errorMessage = $errorMessage; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + // check empty + if (!trim($value)) { + return false; + } + + // check no emoji + if (preg_match('/\p{S}/u', $value)) { + return false; + } + + if (!$this->allowNumber && !preg_match('/^([^0-9]*)$/', $value)) { + return false; + } + + return true; + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return $this->errorMessage ? $this->errorMessage : trans('advancedValidation::validation.name'); + } +} diff --git a/src/Rules/Username.php b/src/Rules/Username.php new file mode 100644 index 0000000..6b574fb --- /dev/null +++ b/src/Rules/Username.php @@ -0,0 +1,56 @@ +errorMessage = $errorMessage; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + return preg_match('/^[a-z][a-z0-9]*(?:[_\-\.][a-z0-9]+)*$/i', $value); + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return $this->errorMessage ? $this->errorMessage : trans('advancedValidation::validation.username'); + } +} diff --git a/tests/Rules/HashTest.php b/tests/Rules/HashTest.php new file mode 100644 index 0000000..14d7a43 --- /dev/null +++ b/tests/Rules/HashTest.php @@ -0,0 +1,38 @@ +assertEquals($result, (new Hash($algorithm))->passes('foo', $value)); + } + } + + public function provider() + { + return [ + [true, 'd94f3f016ae679c3008de268209132f2'], + [true, '751adbc511ccbe8edf23d486fa4581cd'], + [true, '88dae00e614d8f24cfd5a8b3f8002e93'], + [true, '0bf1c35032a71a14c2f719e5a14c1e96'], + [true, 'd94f3F016Ae679C3008de268209132F2'], + [true, '88DAE00e614d8f24cfd5a8b3f8002E93'], + + [false, 'q94375dj93458w34'], + [false, '39485729348'], + [false, '%&FHKJFvk'], + [false, 'KYT0bf1c35032a71a14c2f719e5a1'], + ]; + } +} diff --git a/tests/Rules/JwtTest.php b/tests/Rules/JwtTest.php new file mode 100644 index 0000000..04a1784 --- /dev/null +++ b/tests/Rules/JwtTest.php @@ -0,0 +1,33 @@ +assertEquals($result, (new Jwt())->passes('foo', $value)); + } + + public function provider() + { + return [ + [true, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI'], + [true, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb3JlbSI6Imlwc3VtIn0.ymiJSsMJXR6tMSr8G9usjQ15_8hKPDv_CArLhxw28MI'], + [true, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkb2xvciI6InNpdCIsImFtZXQiOlsibG9yZW0iLCJpcHN1bSJdfQ.rRpe04zbWbbJjwM43VnHzAboDzszJtGrNsUxaqQ-GQ8'], + [true, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqb2huIjp7ImFnZSI6MjUsImhlaWdodCI6MTg1fSwiamFrZSI6eyJhZ2UiOjMwLCJoZWlnaHQiOjI3MH19.YRLPARDmhGMC3BBk_OhtwwK21PIkVCqQe8ncIRPKo-E'], + + [false, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'], + [false, '$Zs.ewu.su84'], + [false, 'ks64$S/9.dy$§kz.3sd73b'], + [false, ''], + [false, ' '], + ]; + } +} diff --git a/tests/Rules/NameTest.php b/tests/Rules/NameTest.php new file mode 100644 index 0000000..9bb6d9f --- /dev/null +++ b/tests/Rules/NameTest.php @@ -0,0 +1,62 @@ +assertEquals($result, (new Name(false))->passes('foo', $value)); + } + + /** + * @dataProvider providerWithAllowNumber + */ + public function testValidationWithAllowNumber($result, $value) + { + $this->assertEquals($result, (new Name(true))->passes('foo', $value)); + } + + public function providerWithoutAllowNumber() + { + return array_merge($this->shared(), [ + [false, '1234 abc DEF'], + [false, '1234abcDEF'], + [false, '123'], + [false, '李小龍123'], + ]); + } + + public function providerWithAllowNumber() + { + return array_merge($this->shared(), [ + [true, '1234 abc DEF'], + [true, '1234abcDEF'], + [true, '123'], + [true, '李小龍123'], + ]); + } + + private function shared() + { + return [ + [true, 'John Doe'], + [true, 'john doe'], + [true, 'j'], + [true, 'john'], + [true, 'Md. Ariful Alam'], + [true, 'Martin Luther King, Jr.'], + [true, '李小龍'], + [false, '😀'], + [false, 'john 😀'], + [false, ''], + [false, ' '], + ]; + } +} diff --git a/tests/Rules/UsernameTest.php b/tests/Rules/UsernameTest.php new file mode 100644 index 0000000..f9ea105 --- /dev/null +++ b/tests/Rules/UsernameTest.php @@ -0,0 +1,51 @@ +assertEquals($result, (new Username())->passes('foo', $value)); + } + + public function provider() + { + return [ + [true, 'john'], + [true, 'john.doe'], + [true, 'john_doe'], + [true, 'john-doe'], + [true, 'john.doe.123'], + [true, 'john123'], + [true, 'john_123'], + [true, 'john-123'], + + [false, '_john'], + [false, '-john'], + [false, '.john'], + [false, 'john_'], + [false, 'john-'], + [false, 'john.'], + [false, '_john_'], + [false, 'john__doe'], + [false, 'john--doe'], + [false, 'john..doe'], + [false, '123'], + [false, '123.john'], + [false, ' '], + [false, 'a b c'], + [false, 'foobar'], + [false, '😀'], + [false, 'john😀'], + [false, ''], + [false, ' '], + ]; + } +}