* @copyright Meritoo.pl */ class Regex { /** * Patterns used to validate / verify values * * @var array */ private static $patterns = [ 'email' => '/[\w-]{2,}@[\w-]+\.[\w]{2,}+/', 'phone' => '/^\+?[0-9 ]+$/', 'camelCasePart' => '/([a-z]|[A-Z]){1}[a-z]*/', 'urlProtocol' => '/^([a-z]+:\/\/)', 'urlDomain' => '([\da-z\.-]+)\.([a-z\.]{2,6})(\/)?([\w\.\-]*)?(\?)?([\w \.\-\/=&]*)\/?$/i', 'letterOrDigit' => '/[a-zA-Z0-9]+/', 'htmlEntity' => '/&[a-z0-9]+;/', 'fileName' => '/.+\.\w+$/', 'isQuoted' => '/^[\'"]{1}.+[\'"]{1}$/', 'windowsBasedPath' => '/^[A-Z]{1}:\\\.*$/', 'money' => '/^[-+]?\d+([\.,]{1}\d*)?$/', 'color' => '/^[a-f0-9]{6}$/i', 'bundleName' => '/^(([A-Z]{1}[a-z0-9]+)((?2))*)(Bundle)$/', ]; /** * Returns information if given e-mail address is valid * * @param string $email E-mail address to validate / verify * @return bool * * Examples: * a) valid e-mails: * - ni@g-m.pl * - ni@gm.pl * - ni@g_m.pl * b) invalid e-mails: * - ni@g-m.p * - n@g-m.pl */ public static function isValidEmail($email) { $pattern = self::getEmailPattern(); return (bool)preg_match($pattern, $email); } /** * Returns information if given tax ID is valid (in Poland it's named "NIP") * * @param string $taxIdString Tax ID (NIP) string * @return bool */ public static function isValidTaxId($taxIdString) { if (!empty($taxIdString)) { $weights = [ 6, 5, 7, 2, 3, 4, 5, 6, 7, ]; $taxId = preg_replace('/[\s-]/', '', $taxIdString); $sum = 0; if (10 == strlen($taxId) && is_numeric($taxId)) { for ($x = 0; $x <= 8; ++$x) { $sum += $taxId[$x] * $weights[$x]; } if ((($sum % 11) % 10) == $taxId[9]) { return true; } } } return false; } /** * Returns information if given url address is valid * * @param string $url The url to validate / verify * @param bool $requireProtocol (optional) If is set to true, the protocol is required to be passed in the url. * Otherwise - not. * @return bool */ public static function isValidUrl($url, $requireProtocol = false) { $pattern = self::getUrlPattern($requireProtocol); return (bool)preg_match($pattern, $url); } /** * Returns information if given phone number is valid * * @param string $phoneNumber The phone number to validate / verify * @return bool */ public static function isValidPhoneNumber($phoneNumber) { $pattern = self::getPhoneNumberPattern(); return (bool)preg_match($pattern, $phoneNumber); } /** * Returns array values that matches given pattern (or values that keys matches) * * @param string $pattern Pattern to match * @param array $dataArray The array * @param bool $itsKeyPattern (optional) If is set to true, keys are checks if they match pattern. Otherwise - * values are checks. * @return array */ public static function getArrayValuesByPattern($pattern, $dataArray, $itsKeyPattern = false) { if ($itsKeyPattern) { $effect = []; if (!empty($dataArray)) { $matches = []; foreach ($dataArray as $key => $value) { if (preg_match($pattern, $key, $matches)) { $effect[$key] = $value; } } } return $effect; } return preg_grep($pattern, $dataArray); } /** * Filters array by given expression and column * * Expression can be simple compare expression, like ' == 2', or regular expression. * Returns filtered array. * * @param array $array The array that should be filtered * @param string $arrayColumnKey Column name * @param string $filterExpression Filter expression, e.g. '== 2' or '!= \'home\'' * @param bool $itsRegularExpression (optional) If is set to true, means that filter expression is a * regular expression * @return array */ public static function arrayFilter($array, $arrayColumnKey, $filterExpression, $itsRegularExpression = false) { $effect = []; if (!empty($array)) { $effect = $array; foreach ($effect as $key => &$item) { if (isset($item[$arrayColumnKey])) { $value = $item[$arrayColumnKey]; if ($itsRegularExpression) { $matches = []; $pattern = '|' . $filterExpression . '|'; $matchesCount = preg_match($pattern, $value, $matches); $remove = 0 == $matchesCount; } else { if ('' == $value) { $value = '\'\''; } elseif (is_string($value)) { $value = '\'' . $value . '\''; } eval('$isTrue = ' . $value . $filterExpression . ';'); /* @var bool $isTrue */ $remove = !$isTrue; } if ($remove) { unset($effect[$key]); } } } } return $effect; } /** * Perform regular expression match with many given patterns. * Returns information if given $subject matches one or all given $patterns. * * @param array|string $patterns The patterns to match * @param string $subject The string to check * @param bool $mustAllMatch (optional) If is set to true, $subject must match all $patterns. Otherwise - * not. * @return bool */ public static function pregMultiMatch($patterns, $subject, $mustAllMatch = false) { $effect = false; $patterns = Arrays::makeArray($patterns); if (!empty($patterns)) { if ($mustAllMatch) { $effect = true; } foreach ($patterns as $pattern) { $matches = []; $matched = (bool)preg_match_all($pattern, $subject, $matches); if ($mustAllMatch) { $effect = $effect && $matched; } else { if ($matched) { $effect = $matched; break; } } } } return $effect; } /** * Returns string in human readable style generated from given camel case string / text * * @param string $string The string / text to convert * @param bool $applyUpperCaseFirst (optional) If is set to true, first word / element from the converted * string is uppercased. Otherwise - not. * @return string */ public static function camelCase2humanReadable($string, $applyUpperCaseFirst = false) { $parts = self::getCamelCaseParts($string); if (!empty($parts)) { $elements = []; foreach ($parts as $part) { $elements[] = strtolower($part); } $string = implode(' ', $elements); if ($applyUpperCaseFirst) { $string = ucfirst($string); } } return $string; } /** * Returns parts of given camel case string / text * * @param string $string The string / text to retrieve parts * @return array */ public static function getCamelCaseParts($string) { $pattern = self::getCamelCasePartPattern(); $matches = []; preg_match_all($pattern, $string, $matches); return $matches[0]; } /** * Returns simple, lowercase string generated from given camel case string / text * * @param string $string The string / text to convert * @param string $separator (optional) Separator used to concatenate parts of the string, e.g. '-' or '_' * @param bool $applyLowercase (optional) If is set to true, returned string will be lowercased. Otherwise - not. * @return string */ public static function camelCase2simpleLowercase($string, $separator = '', $applyLowercase = true) { $parts = self::getCamelCaseParts($string); if (!empty($parts)) { $string = implode($separator, $parts); if ($applyLowercase) { $string = strtolower($string); } } return $string; } /** * Returns pattern used to validate / verify or get e-mail address * * @return string */ public static function getEmailPattern() { return self::$patterns['email']; } /** * Returns pattern used to validate / verify or get phone number * * @return string */ public static function getPhoneNumberPattern() { return self::$patterns['phone']; } /** * Returns pattern used to validate / verify or get camel case parts of string * * @return string */ public static function getCamelCasePartPattern() { return self::$patterns['camelCasePart']; } /** * Returns pattern used to validate / verify or get url address * * @param bool $requireProtocol (optional) If is set to true, the protocol is required to be passed in the url. * Otherwise - not. * @return string */ public static function getUrlPattern($requireProtocol = false) { $urlProtocol = self::$patterns['urlProtocol']; $urlDomain = self::$patterns['urlDomain']; $protocolPatternPart = '?'; if ($requireProtocol) { $protocolPatternPart = ''; } return sprintf('%s%s%s', $urlProtocol, $protocolPatternPart, $urlDomain); } /** * Returns information if given path is sub-path of another path, e.g. path file is owned by path of directory * * @param string $subPath Path to verify, probably sub-path * @param string $path Main / parent path * @return bool */ public static function isSubPathOf($subPath, $path) { /* * Empty path? * Nothing to do */ if (empty($path) || empty($subPath)) { return false; } /* * I have to escape all slashes (directory separators): "/" -> "\/" */ $prepared = preg_quote($path, '/'); /* * Slash at the ending is optional */ if (self::endsWith($path, '/')) { $prepared .= '?'; } $pattern = sprintf('/^%s.*/', $prepared); return (bool)preg_match($pattern, $subPath); } /** * Returns pattern used to validate / verify letter or digit * * @return string */ public static function getLetterOrDigitPattern() { return self::$patterns['letterOrDigit']; } /** * Returns information if given character is a letter or digit * * @param string $char Character to check * @return bool */ public static function isLetterOrDigit($char) { $pattern = self::getLetterOrDigitPattern(); return is_scalar($char) && (bool)preg_match($pattern, $char); } /** * Returns information if the string starts with given beginning / characters * * @param string $string String to check * @param string $beginning The beginning of string, one or more characters * @return bool */ public static function startsWith($string, $beginning) { if (!empty($string) && !empty($beginning)) { if (1 == strlen($beginning) && !self::isLetterOrDigit($beginning)) { $beginning = '\\' . $beginning; } $pattern = sprintf('|^%s|', $beginning); return (bool)preg_match($pattern, $string); } return false; } /** * Returns information if the string ends with given ending / characters * * @param string $string String to check * @param string $ending The ending of string, one or more characters * @return bool */ public static function endsWith($string, $ending) { if (1 == strlen($ending) && !self::isLetterOrDigit($ending)) { $ending = '\\' . $ending; } return (bool)preg_match('|' . $ending . '$|', $string); } /** * Returns information if the string starts with directory's separator * * @param string $string String that may contain a directory's separator at the start / beginning * @param string $separator (optional) The directory's separator, e.g. "/". If is empty (not provided), system's * separator is used. * @return bool */ public static function startsWithDirectorySeparator($string, $separator = '') { if (empty($separator)) { $separator = DIRECTORY_SEPARATOR; } return self::startsWith($string, $separator); } /** * Returns information if the string ends with directory's separator * * @param string $text String that may contain a directory's separator at the end * @param string $separator (optional) The directory's separator, e.g. "/". If is empty (not provided), system's * separator is used. * @return string */ public static function endsWithDirectorySeparator($text, $separator = '') { if (empty($separator)) { $separator = DIRECTORY_SEPARATOR; } return self::endsWith($text, $separator); } /** * Returns information if uri contains parameter * * @param string $uri Uri string (e.g. $_SERVER['REQUEST_URI']) * @param string $parameterName Uri parameter name * @return bool */ public static function isSetUriParameter($uri, $parameterName) { return (bool)preg_match('|[?&]{1}' . $parameterName . '=|', $uri); // e.g. ?name=phil&type=4 -> '$type=' } /** * Returns pattern used to validate / verify html entity * * @return string */ public static function getHtmlEntityPattern() { return self::$patterns['htmlEntity']; } /** * Returns information if the string contains html entities * * @param string $string String to check * @return bool */ public static function containsEntities($string) { $pattern = self::getHtmlEntityPattern(); return (bool)preg_match_all($pattern, $string); } /** * Returns information if one string contains another string * * @param string $haystack The string to search in * @param string $needle The string to be search for * @return bool */ public static function contains($haystack, $needle) { if (1 == strlen($needle) && !self::isLetterOrDigit($needle)) { $needle = '\\' . $needle; } return (bool)preg_match('|.*' . $needle . '.*|', $haystack); } /** * Returns pattern used to validate / verify name of file * * @return string */ public static function getFileNamePattern() { return self::$patterns['fileName']; } /** * Returns information if given name of file is a really name of file. * Verifies if given name contains a dot and an extension, e.g. "My File 001.jpg". * * @param string $fileName Name of file to check. It may be path of file also. * @return bool */ public static function isFileName($fileName) { $pattern = self::getFileNamePattern(); return (bool)preg_match($pattern, $fileName); } /** * Returns pattern used to validate / verify if value is quoted (by apostrophes or quotation marks) * * @return string */ public static function getIsQuotedPattern() { return self::$patterns['isQuoted']; } /** * Returns information if given value is quoted (by apostrophes or quotation marks) * * @param mixed $value The value to check * @return bool */ public static function isQuoted($value) { $pattern = self::getIsQuotedPattern(); return is_scalar($value) && (bool)preg_match($pattern, $value); } /** * Returns pattern used to validate / verify if given path is a Windows-based path, e.g. "C:\path\to\file.jpg" * * @return string */ public static function getWindowsBasedPathPattern() { return self::$patterns['windowsBasedPath']; } /** * Returns information if given path is a Windows-based path, e.g. "C:\path\to\file.jpg" * * @param string $path The path to verify * @return bool */ public static function isWindowsBasedPath($path) { $pattern = self::getWindowsBasedPathPattern(); return (bool)preg_match($pattern, $path); } /** * Returns information if given NIP number is valid * * @param string $nip A given NIP number * @return bool * * @see https://pl.wikipedia.org/wiki/NIP#Znaczenie_numeru */ public static function isValidNip($nip) { $nip = preg_replace('/[^0-9]/', '', $nip); $invalidNips = [ '1234567890', '0000000000', ]; if (!preg_match('/^[0-9]{10}$/', $nip) || in_array($nip, $invalidNips)) { return false; } $sum = 0; $weights = [ 6, 5, 7, 2, 3, 4, 5, 6, 7, ]; for ($i = 0; $i < 9; ++$i) { $sum += $weights[$i] * $nip[$i]; } $modulo = $sum % 11; $numberControl = (10 == $modulo) ? 0 : $modulo; return $numberControl == $nip[9]; } /** * Returns pattern used to validate / verify if given value is money-related value * * @return string */ public static function getMoneyPattern() { return self::$patterns['money']; } /** * Returns information if given value is valid money-related value * * @param mixed $value Value to verify * @return bool */ public static function isValidMoneyValue($value) { $pattern = self::getMoneyPattern(); return (bool)preg_match($pattern, $value); } /** * Returns valid given hexadecimal value of color. * If the value is invalid, throws an exception or returns false. * * @param string $color Color to verify * @param bool $throwException (optional) If is set to true, throws an exception if given color is invalid * (default behaviour). Otherwise - not. * @return string|bool * * @throws IncorrectColorHexLengthException * @throws InvalidColorHexValueException */ public static function getValidColorHexValue($color, $throwException = true) { $color = Miscellaneous::replace($color, '/#/', ''); $length = strlen($color); if (3 === $length) { $color = Miscellaneous::replace($color, '/(.)(.)(.)/', '$1$1$2$2$3$3'); } else { if (6 !== $length) { if ($throwException) { throw new IncorrectColorHexLengthException($color); } return false; } } $pattern = self::$patterns['color']; $match = (bool)preg_match($pattern, $color); if (!$match) { if ($throwException) { throw new InvalidColorHexValueException($color); } return false; } return strtolower($color); } /** * Returns information if given name of bundle is valid * * @param string $bundleName Full name of bundle to verify, e.g. "MyExtraBundle" * @return bool */ public static function isValidBundleName($bundleName) { if (!is_string($bundleName)) { return false; } $pattern = self::getBundleNamePattern(); return (bool)preg_match($pattern, $bundleName); } /** * Returns pattern used to validate / verify name of bundle * * @return string */ public static function getBundleNamePattern() { return self::$patterns['bundleName']; } }