From d831f1c50a9a7c86b4819d156e93f4a18e0b93be Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Tue, 25 Jul 2017 17:30:17 +0200 Subject: [PATCH] Re-structured validation --- Datatypes/Iban.php | 4 +- Validation/Finance/BIC.php | 50 ++++++++ Validation/Finance/CreditCard.php | 100 +++++++++++++++ Validation/Finance/Iban.php | 174 +++++++++++++++++++++++++++ Validation/Finance/IbanEnum.php | 115 ++++++++++++++++++ Validation/Finance/IbanErrorType.php | 38 ++++++ Validation/ModelValidationTrait.php | 1 - Validation/Network/Email.php | 60 +++++++++ Validation/Network/Hostname.php | 50 ++++++++ Validation/Network/Ip.php | 60 +++++++++ Validation/Validator.php | 3 +- Validation/ValidatorAbstract.php | 2 +- Validation/ValidatorInterface.php | 14 ++- 13 files changed, 664 insertions(+), 7 deletions(-) create mode 100644 Validation/Finance/BIC.php create mode 100644 Validation/Finance/CreditCard.php create mode 100644 Validation/Finance/Iban.php create mode 100644 Validation/Finance/IbanEnum.php create mode 100644 Validation/Finance/IbanErrorType.php create mode 100644 Validation/Network/Email.php create mode 100644 Validation/Network/Hostname.php create mode 100644 Validation/Network/Ip.php diff --git a/Datatypes/Iban.php b/Datatypes/Iban.php index 767279f98..1ae324a27 100644 --- a/Datatypes/Iban.php +++ b/Datatypes/Iban.php @@ -16,7 +16,7 @@ declare(strict_types=1); namespace phpOMS\Datatypes; -use phpOMS\Validation\Base\IbanEnum; +use phpOMS\Validation\Finance\IbanEnum; /** * Iban class. @@ -65,7 +65,7 @@ class Iban implements \Serializable { $this->iban = self::normalize($iban); - if (!\phpOMS\Validation\Base\Iban::isValid($this->iban)) { + if (!\phpOMS\Validation\Finance\Iban::isValid($this->iban)) { throw new \InvalidArgumentException('Invalid IBAN'); } } diff --git a/Validation/Finance/BIC.php b/Validation/Finance/BIC.php new file mode 100644 index 000000000..b214451d5 --- /dev/null +++ b/Validation/Finance/BIC.php @@ -0,0 +1,50 @@ + + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +declare(strict_types=1); + +namespace phpOMS\Validation\Finance; + +use phpOMS\Validation\ValidatorAbstract; + +/** + * Validator abstract. + * + * @category Validation + * @package Framework + * @author OMS Development Team + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class BIC extends ValidatorAbstract +{ + + /** + * Constructor. + * + * @since 1.0.0 + */ + public function __construct() + { + } + + /** + * {@inheritdoc} + */ + public static function isValid($value) : bool + { + return (bool) preg_match('/^[a-z]{6}[0-9a-z]{2}([0-9a-z]{3})?\z/i', $value); + } +} diff --git a/Validation/Finance/CreditCard.php b/Validation/Finance/CreditCard.php new file mode 100644 index 000000000..785473cb1 --- /dev/null +++ b/Validation/Finance/CreditCard.php @@ -0,0 +1,100 @@ + + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +declare(strict_types=1); + +namespace phpOMS\Validation\Finance; + +use phpOMS\Validation\ValidatorAbstract; + +/** + * Validator abstract. + * + * @category Validation + * @package Framework + * @author OMS Development Team + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +abstract class CreditCard extends ValidatorAbstract +{ + + /** + * Constructor. + * + * @since 1.0.0 + */ + public function __construct() + { + } + + /** + * {@inheritdoc} + */ + public static function isValid($value) : bool + { + $value = preg_replace('/\D/', '', $value); + + // Set the string length and parity + $number_length = strlen($value); + $parity = $number_length % 2; + + // Loop through each digit and do the maths + $total = 0; + for ($i = 0; $i < $number_length; $i++) { + $digit = $value[$i]; + // Multiply alternate digits by two + if ($i % 2 == $parity) { + $digit *= 2; + // If the sum is two digits, add them together (in effect) + if ($digit > 9) { + $digit -= 9; + } + } + // Total up the digits + $total += $digit; + } + + // If the total mod 10 equals 0, the value is valid + return ($total % 10 == 0) ? true : false; + } + + /** + * Luhn algorithm or mod 10 algorithm is used to verify credit cards. + * + * @param string $num Credit card number. + * + * @return bool Returns true if the number is a valid credit card and false if it isn't. + * + * @since 1.0.0 + */ + public static function luhnTest(string $num) : bool + { + $len = strlen($num); + $sum = 0; + + for ($i = $len - 1; $i >= 0; $i--) { + $ord = ord($num[$i]); + + if (($len - 1) & $i) { + $sum += $ord; + } else { + $sum += $ord / 5 + (2 * $ord) % 10; + } + } + + return $sum % 10 == 0; + } +} diff --git a/Validation/Finance/Iban.php b/Validation/Finance/Iban.php new file mode 100644 index 000000000..09df00608 --- /dev/null +++ b/Validation/Finance/Iban.php @@ -0,0 +1,174 @@ + + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +declare(strict_types=1); + +namespace phpOMS\Validation\Finance; + +use phpOMS\Validation\ValidatorAbstract; + + +/** + * Validator abstract. + * + * @category Validation + * @package Framework + * @author OMS Development Team + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +abstract class Iban extends ValidatorAbstract +{ + /** + * Constructor. + * + * @since 1.0.0 + */ + private function __construct() + { + } + + /** + * @param string $value Iban to validate + * + * @return bool + * + * @since 1.0.0 + */ + public static function isValid($value) : bool + { + $value = str_replace(' ', '', strtolower($value)); + $enumName = 'C_' . strtoupper(substr($value, 0, 2)); + + if (!IbanEnum::isValidName($enumName)) { + self::$error = IbanErrorType::INVALID_COUNTRY; + + return false; + } + + $layout = str_replace(' ', '', IbanEnum::getByName($enumName)); + + if (strlen($value) !== strlen($layout)) { + self::$error = IbanErrorType::INVALID_LENGTH; + + return false; + } + + if (!self::validateZeros($value, $layout)) { + self::$error = IbanErrorType::EXPECTED_ZERO; + + return false; + } + + if (!self::validateNumeric($value, $layout)) { + self::$error = IbanErrorType::EXPECTED_NUMERIC; + + return false; + } + + if (!self::validateChecksum($value)) { + self::$error = IbanErrorType::INVALID_CHECKSUM; + + return false; + } + + return true; + } + + /** + * Validate positions that should have zeros + * + * @param string $iban Iban to validate + * @param string $layout Iban layout + * + * @return bool + * + * @since 1.0.0 + */ + private static function validateZeros(string $iban, string $layout) : bool + { + if (strpos($layout, '0') === false) { + return true; + } + + $lastPos = 0; + while (($lastPos = strpos($layout, '0', $lastPos)) !== false) { + if ($iban[$lastPos] !== '0') { + return false; + } + + $lastPos += 1; + } + + return true; + } + + /** + * Validate positions that should be numeric + * + * @param string $iban Iban to validate + * @param string $layout Iban layout + * + * @return bool + * + * @since 1.0.0 + */ + private static function validateNumeric(string $iban, string $layout) : bool + { + if (strpos($layout, 'n') === false) { + return true; + } + + $lastPos = 0; + while (($lastPos = strpos($layout, 'n', $lastPos)) !== false) { + if (!is_numeric($iban[$lastPos])) { + return false; + } + + $lastPos += 1; + } + + return true; + } + + /** + * Validate checksum + * + * @param string $iban Iban to validate + * + * @return bool + * + * @since 1.0.0 + */ + private static function validateChecksum(string $iban) : bool + { + $chars = ['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17, 'i' => 18, + 'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23, 'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27, + 's' => 28, 't' => 29, 'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35,]; + $moved = substr($iban, 4) . substr($iban, 0, 4); + $movedArray = str_split($moved); + $new = ''; + + foreach ($movedArray as $key => $value) { + if (!is_numeric($movedArray[$key])) { + $movedArray[$key] = $chars[$movedArray[$key]]; + } + + $new .= $movedArray[$key]; + } + + return bcmod($new, '97') == 1; + } +} diff --git a/Validation/Finance/IbanEnum.php b/Validation/Finance/IbanEnum.php new file mode 100644 index 000000000..65bccbaf7 --- /dev/null +++ b/Validation/Finance/IbanEnum.php @@ -0,0 +1,115 @@ + + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +declare(strict_types=1); + +namespace phpOMS\Validation\Finance; + +use phpOMS\Datatypes\Enum; + +/** + * Iban layout definition. + * + * @category Framework + * @package phpOMS\Localization + * @author OMS Development Team + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class IbanEnum extends Enum +{ + /* public */ const C_AL = 'ALkk bbbs sssx cccc cccc cccc cccc'; + /* public */ const C_AD = 'ADkk bbbb ssss cccc cccc cccc'; + /* public */ const C_AT = 'ATkk bbbb bccc cccc cccc'; + /* public */ const C_AZ = 'AZkk bbbb cccc cccc cccc cccc cccc '; + /* public */ const C_BH = 'BHkk bbbb cccc cccc cccc cc'; + /* public */ const C_BE = 'BEkk bbbc cccc ccxx'; + /* public */ const C_BA = 'BAkk bbbs sscc cccc ccxx'; + /* public */ const C_BR = 'BRkk bbbb bbbb ssss sccc cccc ccct n'; + /* public */ const C_BG = 'BGkk bbbb ssss ttcc cccc cc'; + /* public */ const C_CR = 'CRkk bbbc cccc cccc cccc c'; + /* public */ const C_HR = 'HRkk bbbb bbbc cccc cccc c'; + /* public */ const C_CY = 'CYkk bbbs ssss cccc cccc cccc cccc'; + /* public */ const C_CZ = 'CZkk bbbb ssss sscc cccc cccc'; + /* public */ const C_DK = 'DKkk bbbb cccc cccc cc'; + /* public */ const C_DO = 'DOkk bbbb cccc cccc cccc cccc cccc'; + /* public */ const C_TL = 'TLkk bbbc cccc cccc cccc cxx'; + /* public */ const C_EE = 'EEkk bbss cccc cccc cccx'; + /* public */ const C_FO = 'FOkk bbbb cccc cccc cx'; + /* public */ const C_FI = 'FIkk bbbb bbcc cccc cx'; + /* public */ const C_FR = 'FRkk bbbb bsss sscc cccc cccc cxx'; + /* public */ const C_GE = 'GEkk bbcc cccc cccc cccc cc'; + /* public */ const C_DE = 'DEkk bbbb bbbb cccc cccc cc'; + /* public */ const C_GI = 'GIkk bbbb cccc cccc cccc ccc'; + /* public */ const C_GR = 'GRkk bbbs sssc cccc cccc cccc ccc'; + /* public */ const C_GL = 'GLkk bbbb cccc cccc cc'; + /* public */ const C_GT = 'GTkk bbbb mmtt cccc cccc cccc cccc'; + /* public */ const C_HU = 'HUkk bbbs sssk cccc cccc cccc cccx'; + /* public */ const C_IS = 'ISkk bbbb sscc cccc iiii iiii ii'; + /* public */ const C_IE = 'IEkk aaaa bbbb bbcc cccc cc'; + /* public */ const C_IL = 'ILkk bbbn nncc cccc cccc ccc'; + /* public */ const C_IT = 'ITkk xbbb bbss sssc cccc cccc ccc'; + /* public */ const C_JO = 'JOkk bbbb ssss cccc cccc cccc cccc cc'; + /* public */ const C_KZ = 'KZkk bbbc cccc cccc cccc'; + /* public */ const C_XK = 'XKkk bbbb cccc cccc cccc'; + /* public */ const C_KW = 'KWkk bbbb cccc cccc cccc cccc cccc cc'; + /* public */ const C_LV = 'LVkk bbbb cccc cccc cccc c'; + /* public */ const C_LB = 'LBkk bbbb cccc cccc cccc cccc cccc'; + /* public */ const C_LI = 'LIkk bbbb bccc cccc cccc c'; + /* public */ const C_LT = 'LTkk bbbb bccc cccc cccc'; + /* public */ const C_LU = 'LUkk bbbc cccc cccc cccc'; + /* public */ const C_MK = 'MKkk bbbc cccc cccc cxx'; + /* public */ const C_MT = 'MTkk bbbb ssss sccc cccc cccc cccc ccc'; + /* public */ const C_MR = 'MRkk bbbb bsss sscc cccc cccc cxx'; + /* public */ const C_MU = 'MUkk bbbb bbss cccc cccc cccc 000m mm'; + /* public */ const C_MC = 'MCkk bbbb bsss sscc cccc cccc cxx'; + /* public */ const C_MD = 'MDkk bbcc cccc cccc cccc cccc'; + /* public */ const C_ME = 'MEkk bbbc cccc cccc cccc xx'; + /* public */ const C_NL = 'NLkk bbbb cccc cccc cc'; + /* public */ const C_NO = 'NOkk bbbb cccc ccx'; + /* public */ const C_PK = 'PKkk bbbb cccc cccc cccc cccc'; + /* public */ const C_PS = 'PSkk bbbb xxxx xxxx xccc cccc cccc c'; + /* public */ const C_PL = 'PLkk bbbs sssx cccc cccc cccc cccc'; + /* public */ const C_PT = 'PTkk bbbb ssss cccc cccc cccx x'; + /* public */ const C_QA = 'QAkk bbbb cccc cccc cccc cccc cccc c'; + /* public */ const C_RO = 'ROkk bbbb cccc cccc cccc cccc'; + /* public */ const C_SM = 'SMkk xbbb bbss sssc cccc cccc ccc'; + /* public */ const C_SA = 'SAkk bbcc cccc cccc cccc cccc'; + /* public */ const C_RS = 'RSkk bbbc cccc cccc cccc xx'; + /* public */ const C_SK = 'SKkk bbbb ssss sscc cccc cccc'; + /* public */ const C_SI = 'SIkk bbss sccc cccc cxx'; + /* public */ const C_ES = 'ESkk bbbb ssss xxcc cccc cccc'; + /* public */ const C_SE = 'SEkk bbbc cccc cccc cccc cccc'; + /* public */ const C_CH = 'CHkk bbbb bccc cccc cccc c'; + /* public */ const C_TN = 'TNkk bbss sccc cccc cccc cccc'; + /* public */ const C_TR = 'TRkk bbbb bxcc cccc cccc cccc cc'; + /* public */ const C_UA = 'UAkk bbbb bbcc cccc cccc cccc cccc c'; + /* public */ const C_AE = 'AEkk bbbc cccc cccc cccc ccc'; + /* public */ const C_GB = 'GBkk bbbb ssss sscc cccc cc'; + /* public */ const C_VG = 'VGkk bbbb cccc cccc cccc cccc'; + /* public */ const C_SN = 'SNkk annn nnnn nnnn nnnn nnnn nnnn'; + /* public */ const C_MZ = 'MZkk nnnn nnnn nnnn nnnn nnnn n'; + /* public */ const C_ML = 'MLkk annn nnnn nnnn nnnn nnnn nnnn'; + /* public */ const C_MG = 'MGkk nnnn nnnn nnnn nnnn nnnn nnn'; + /* public */ const C_CI = 'CIkk annn nnnn nnnn nnnn nnnn nnnn'; + /* public */ const C_IR = 'IRkk nnnn nnnn nnnn nnnn nnnn nn'; + /* public */ const C_CV = 'CVkk nnnn nnnn nnnn nnnn nnnn n'; + /* public */ const C_CM = 'CMkk nnnn nnnn nnnn nnnn nnnn nnn'; + /* public */ const C_BI = 'BIkk nnnn nnnn nnnn'; + /* public */ const C_BF = 'BFkk nnnn nnnn nnnn nnnn nnnn nnn'; + /* public */ const C_BJ = 'BJkk annn nnnn nnnn nnnn nnnn nnnn'; + /* public */ const C_AO = 'AOkk nnnn nnnn nnnn nnnn nnnn n'; + /* public */ const C_DZ = 'DZkk nnnn nnnn nnnn nnnn nnnn'; +} diff --git a/Validation/Finance/IbanErrorType.php b/Validation/Finance/IbanErrorType.php new file mode 100644 index 000000000..e2b17fe60 --- /dev/null +++ b/Validation/Finance/IbanErrorType.php @@ -0,0 +1,38 @@ + + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +declare(strict_types=1); + +namespace phpOMS\Validation\Finance; + +use phpOMS\Datatypes\Enum; + +/** + * Iban error type enum. + * + * @category Framework + * @package phpOMS\Datatypes + * @author OMS Development Team + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +abstract class IbanErrorType extends Enum +{ + /* public */ const INVALID_COUNTRY = 1; + /* public */ const INVALID_LENGTH = 2; + /* public */ const INVALID_CHECKSUM = 4; + /* public */ const EXPECTED_ZERO = 8; + /* public */ const EXPECTED_NUMERIC = 16; +} diff --git a/Validation/ModelValidationTrait.php b/Validation/ModelValidationTrait.php index 6ad5b9321..8c4175463 100644 --- a/Validation/ModelValidationTrait.php +++ b/Validation/ModelValidationTrait.php @@ -67,7 +67,6 @@ trait ModelValidationTrait } /** @noinspection PhpUndefinedFieldInspection */ - return Validator::isValid($var, self::$validation[$name]); } diff --git a/Validation/Network/Email.php b/Validation/Network/Email.php new file mode 100644 index 000000000..dca36c7d1 --- /dev/null +++ b/Validation/Network/Email.php @@ -0,0 +1,60 @@ + + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +declare(strict_types=1); + +namespace phpOMS\Validation\Network; + +use phpOMS\Validation\ValidatorAbstract; + +/** + * Validator abstract. + * + * @category Validation + * @package Framework + * @author OMS Development Team + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class Email extends ValidatorAbstract +{ + + /** + * Constructor. + * + * @since 1.0.0 + */ + private function __construct() + { + } + + /** + * {@inheritdoc} + */ + public static function isValid(string $value) : bool + { + if (filter_var($value, FILTER_VALIDATE_EMAIL) === false) { + self::$msg = 'Invalid Email by filter_var standards'; + self::$error = 1; + + return false; + } + + self::$msg = ''; + self::$error = 0; + + return true; + } +} diff --git a/Validation/Network/Hostname.php b/Validation/Network/Hostname.php new file mode 100644 index 000000000..f62698133 --- /dev/null +++ b/Validation/Network/Hostname.php @@ -0,0 +1,50 @@ + + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +declare(strict_types=1); + +namespace phpOMS\Validation\Network; + +use phpOMS\Validation\ValidatorAbstract; + +/** + * Validator abstract. + * + * @category Validation + * @package Framework + * @author OMS Development Team + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +abstract class Hostname extends ValidatorAbstract +{ + + /** + * Constructor. + * + * @since 1.0.0 + */ + public function __construct() + { + } + + /** + * {@inheritdoc} + */ + public static function isValid($value) : bool + { + return filter_var(gethostbyname($value), FILTER_VALIDATE_IP) !== false; + } +} diff --git a/Validation/Network/Ip.php b/Validation/Network/Ip.php new file mode 100644 index 000000000..f30e90ea7 --- /dev/null +++ b/Validation/Network/Ip.php @@ -0,0 +1,60 @@ + + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://orange-management.com + */ +declare(strict_types=1); + +namespace phpOMS\Validation\Network; + +use phpOMS\Validation\ValidatorAbstract; + +/** + * Validator abstract. + * + * @category Validation + * @package Framework + * @author OMS Development Team + * @license OMS License 1.0 + * @link http://orange-management.com + * @since 1.0.0 + */ +class Ip extends ValidatorAbstract +{ + + /** + * Constructor. + * + * @since 1.0.0 + */ + private function __construct() + { + } + + /** + * {@inheritdoc} + */ + public static function isValid($value) : bool + { + return filter_var($value, FILTER_VALIDATE_IP) !== false; + } + + public static function isValidIpv6($value) : bool + { + return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false; + } + + public static function isValidIpv4($value) : bool + { + return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false; + } +} diff --git a/Validation/Validator.php b/Validation/Validator.php index a322b8e5c..cd19a3ec2 100644 --- a/Validation/Validator.php +++ b/Validation/Validator.php @@ -38,9 +38,10 @@ final class Validator extends ValidatorAbstract * * @since 1.0.0 */ - public static function isValid($var, array $constraints) : bool + public static function isValid($var, array $constraints = null) : bool { foreach ($constraints as $callback => $settings) { + $callback = StringUtils::endsWith($callback, 'Not') ? substr($callback, 0, -3) : $callback; $valid = self::$callback($var, ...$settings); $valid = (StringUtils::endsWith($callback, 'Not') ? $valid : !$valid); diff --git a/Validation/ValidatorAbstract.php b/Validation/ValidatorAbstract.php index 6f46d6693..f3283916e 100644 --- a/Validation/ValidatorAbstract.php +++ b/Validation/ValidatorAbstract.php @@ -26,7 +26,7 @@ namespace phpOMS\Validation; * @link http://orange-management.com * @since 1.0.0 */ -abstract class ValidatorAbstract +abstract class ValidatorAbstract implements ValidatorInterface { /** diff --git a/Validation/ValidatorInterface.php b/Validation/ValidatorInterface.php index 92f90c26c..91aeefce1 100644 --- a/Validation/ValidatorInterface.php +++ b/Validation/ValidatorInterface.php @@ -33,12 +33,13 @@ interface ValidatorInterface * Check if value is valid. * * @param mixed $value Value to validate + * @param array $constraints Constraints for validation * * @return bool * * @since 1.0.0 */ - public static function isValid($value); + public static function isValid($value, array $constraints = null); /** * Get most recent error string. @@ -47,5 +48,14 @@ interface ValidatorInterface * * @since 1.0.0 */ - public static function getMessage(); + public static function getMessage() : string; + + /** + * Get most recent error code. + * + * @return int + * + * @since 1.0.0 + */ + public static function getErrorCode() : int }