[0-9]+) | (?P([0-9]*\.(?P>LNUM)|(?P>LNUM)\.[0-9]*)) ) [e][+-]?(?P>LNUM) ) | (?P>DNUM) | (?:0|[1-9][0-9]*) )$ `ixD'; /** * Retrieve information about a number token. * * Helper function to deal with numeric literals, potentially with underscore separators * and/or explicit octal notation. * * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. * @param int $stackPtr The position of a T_LNUMBER or T_DNUMBER token. * * @return array An array with information about the number. * The format of the array return value is: * ```php * array( * 'orig_content' => string, // The original content of the token(s); * 'content' => string, // The content, underscore(s) removed; * 'code' => int, // The token code of the number, either * // T_LNUMBER or T_DNUMBER. * 'type' => string, // The token type, either 'T_LNUMBER' * // or 'T_DNUMBER'. * 'decimal' => string, // The decimal value of the number; * 'last_token' => int, // The stackPtr to the last token which was * // part of the number. * // At this time, this will be always be the original * // stackPtr. This may change in the future if * // new numeric syntaxes would be added to PHP. * ) * ``` * * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type * `T_LNUMBER` or `T_DNUMBER`. */ public static function getCompleteNumber(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]) === false || ($tokens[$stackPtr]['code'] !== \T_LNUMBER && $tokens[$stackPtr]['code'] !== \T_DNUMBER) ) { throw new RuntimeException( 'Token type "' . $tokens[$stackPtr]['type'] . '" is not T_LNUMBER or T_DNUMBER' ); } $content = $tokens[$stackPtr]['content']; return [ 'orig_content' => $content, 'content' => \str_replace('_', '', $content), 'code' => $tokens[$stackPtr]['code'], 'type' => $tokens[$stackPtr]['type'], 'decimal' => self::getDecimalValue($content), 'last_token' => $stackPtr, ]; } /** * Get the decimal number value of a numeric string. * * Takes PHP 7.4 numeric literal separators and explicit octal literals in numbers into account. * * @since 1.0.0 * * @param string $textString Arbitrary text string. * This text string should be the (combined) token content of * one or more tokens which together represent a number in PHP. * * @return string|false Decimal number as a string or `FALSE` if the passed parameter * was not a numeric string. * > Note: floating point numbers with exponent will not be expanded, * but returned as-is. */ public static function getDecimalValue($textString) { if (\is_string($textString) === false || $textString === '') { return false; } /* * Remove potential PHP 7.4 numeric literal separators. * * {@internal While the is..() functions also do this, this is still needed * here to allow the hexdec(), bindec() functions to work correctly and for * the decimal/float to return a cross-version compatible decimal value.} */ $textString = \str_replace('_', '', $textString); if (self::isDecimalInt($textString) === true) { return $textString; } if (self::isHexidecimalInt($textString) === true) { return (string) \hexdec($textString); } if (self::isBinaryInt($textString) === true) { return (string) \bindec($textString); } if (self::isOctalInt($textString) === true) { return (string) \octdec($textString); } if (self::isFloat($textString) === true) { return $textString; } return false; } /** * Verify whether the contents of an arbitrary string represents a decimal integer. * * Takes PHP 7.4 numeric literal separators in numbers into account. * * @since 1.0.0 * * @param string $textString Arbitrary string. * * @return bool */ public static function isDecimalInt($textString) { if (\is_string($textString) === false || $textString === '') { return false; } // Remove potential PHP 7.4 numeric literal separators. $textString = \str_replace('_', '', $textString); return (\preg_match(self::REGEX_DECIMAL_INT, $textString) === 1); } /** * Verify whether the contents of an arbitrary string represents a hexidecimal integer. * * Takes PHP 7.4 numeric literal separators in numbers into account. * * @since 1.0.0 * * @param string $textString Arbitrary string. * * @return bool */ public static function isHexidecimalInt($textString) { if (\is_string($textString) === false || $textString === '') { return false; } // Remove potential PHP 7.4 numeric literal separators. $textString = \str_replace('_', '', $textString); return (\preg_match(self::REGEX_HEX_INT, $textString) === 1); } /** * Verify whether the contents of an arbitrary string represents a binary integer. * * Takes PHP 7.4 numeric literal separators in numbers into account. * * @since 1.0.0 * * @param string $textString Arbitrary string. * * @return bool */ public static function isBinaryInt($textString) { if (\is_string($textString) === false || $textString === '') { return false; } // Remove potential PHP 7.4 numeric literal separators. $textString = \str_replace('_', '', $textString); return (\preg_match(self::REGEX_BINARY_INT, $textString) === 1); } /** * Verify whether the contents of an arbitrary string represents an octal integer. * * Takes PHP 7.4 numeric literal separators and explicit octal literals in numbers into account. * * @since 1.0.0 * * @param string $textString Arbitrary string. * * @return bool */ public static function isOctalInt($textString) { if (\is_string($textString) === false || $textString === '') { return false; } // Remove potential PHP 7.4 numeric literal separators. $textString = \str_replace('_', '', $textString); return (\preg_match(self::REGEX_OCTAL_INT, $textString) === 1); } /** * Verify whether the contents of an arbitrary string represents a floating point number. * * Takes PHP 7.4 numeric literal separators in numbers into account. * * @since 1.0.0 * * @param string $textString Arbitrary string. * * @return bool */ public static function isFloat($textString) { if (\is_string($textString) === false || $textString === '') { return false; } // Remove potential PHP 7.4 numeric literal separators. $textString = \str_replace('_', '', $textString); return (\preg_match(self::REGEX_FLOAT, $textString) === 1); } }