diff --git a/CHANGELOG.md b/CHANGELOG.md index f14bb939d..0dcf3f20c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Please also have a look at our ### Changed +- Remove `thecodingmachine/safe` dependency (#1482) - Clean up extra whitespace in CSS selector (#1398) - The array keys passed to `DeclarationBlock::setSelectors()` are no longer preserved (#1407) diff --git a/bin/quickdump.php b/bin/quickdump.php index dc0fe8697..7d3919a5d 100755 --- a/bin/quickdump.php +++ b/bin/quickdump.php @@ -5,7 +5,7 @@ use Sabberworm\CSS\Parser; -use function Safe\file_get_contents; +use function Sabberworm\CSS\Safe\file_get_contents; /** * This script is used for generating the examples in the README. diff --git a/composer.json b/composer.json index 49cc7519c..4c957bb6f 100644 --- a/composer.json +++ b/composer.json @@ -24,8 +24,7 @@ "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", "require": { "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "ext-iconv": "*", - "thecodingmachine/safe": "^1.3 || ^2.5 || ^3.3" + "ext-iconv": "*" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "1.4.0", @@ -44,6 +43,9 @@ "ext-mbstring": "for parsing UTF-8 CSS" }, "autoload": { + "files": [ + "src/Safe/safe_functions.php" + ], "psr-4": { "Sabberworm\\CSS\\": "src/" } diff --git a/config/phpstan.neon b/config/phpstan.neon index 55ba682af..059c75a0f 100644 --- a/config/phpstan.neon +++ b/config/phpstan.neon @@ -15,6 +15,9 @@ parameters: - %currentWorkingDirectory%/src/ - %currentWorkingDirectory%/tests/ + excludePaths: + - %currentWorkingDirectory%/src/Safe/safe_functions.php + type_perfect: no_mixed_property: true no_mixed_caller: true diff --git a/src/CSSList/CSSList.php b/src/CSSList/CSSList.php index 62f7fe654..b97d23791 100644 --- a/src/CSSList/CSSList.php +++ b/src/CSSList/CSSList.php @@ -25,7 +25,7 @@ use Sabberworm\CSS\Value\URL; use Sabberworm\CSS\Value\Value; -use function Safe\preg_match; +use function Sabberworm\CSS\Safe\preg_match; /** * This is the most generic container available. It can contain `DeclarationBlock`s (rule sets with a selector), diff --git a/src/Parsing/ParserState.php b/src/Parsing/ParserState.php index 3451fe0c4..f1eaad426 100644 --- a/src/Parsing/ParserState.php +++ b/src/Parsing/ParserState.php @@ -7,9 +7,9 @@ use Sabberworm\CSS\Comment\Comment; use Sabberworm\CSS\Settings; -use function Safe\iconv; -use function Safe\preg_match; -use function Safe\preg_split; +use function Sabberworm\CSS\Safe\iconv; +use function Sabberworm\CSS\Safe\preg_match; +use function Sabberworm\CSS\Safe\preg_split; /** * @internal since 8.7.0 diff --git a/src/Property/Selector.php b/src/Property/Selector.php index d74554535..9c3aa96d6 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -11,8 +11,8 @@ use Sabberworm\CSS\Property\Selector\SpecificityCalculator; use Sabberworm\CSS\Renderable; -use function Safe\preg_match; -use function Safe\preg_replace; +use function Sabberworm\CSS\Safe\preg_match; +use function Sabberworm\CSS\Safe\preg_replace; /** * Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this diff --git a/src/Property/Selector/SpecificityCalculator.php b/src/Property/Selector/SpecificityCalculator.php index 6c7bbccd5..73a0d3ca4 100644 --- a/src/Property/Selector/SpecificityCalculator.php +++ b/src/Property/Selector/SpecificityCalculator.php @@ -4,7 +4,7 @@ namespace Sabberworm\CSS\Property\Selector; -use function Safe\preg_match_all; +use function Sabberworm\CSS\Safe\preg_match_all; /** * Utility class to calculate the specificity of a CSS selector. diff --git a/src/Rule/Rule.php b/src/Rule/Rule.php index 26e68b231..d3af4e2d4 100644 --- a/src/Rule/Rule.php +++ b/src/Rule/Rule.php @@ -17,7 +17,7 @@ use Sabberworm\CSS\Value\RuleValueList; use Sabberworm\CSS\Value\Value; -use function Safe\preg_match; +use function Sabberworm\CSS\Safe\preg_match; /** * `Rule`s just have a string key (the rule) and a 'Value'. diff --git a/src/Safe/Exceptions/DirException.php b/src/Safe/Exceptions/DirException.php new file mode 100644 index 000000000..33075ca79 --- /dev/null +++ b/src/Safe/Exceptions/DirException.php @@ -0,0 +1,15 @@ + 'PREG_INTERNAL_ERROR: Internal error', + PREG_BACKTRACK_LIMIT_ERROR => 'PREG_BACKTRACK_LIMIT_ERROR: Backtrack limit reached', + PREG_RECURSION_LIMIT_ERROR => 'PREG_RECURSION_LIMIT_ERROR: Recursion limit reached', + PREG_BAD_UTF8_ERROR => 'PREG_BAD_UTF8_ERROR: Invalid UTF8 character', + PREG_BAD_UTF8_OFFSET_ERROR => 'PREG_BAD_UTF8_OFFSET_ERROR', + PREG_JIT_STACKLIMIT_ERROR => 'PREG_JIT_STACKLIMIT_ERROR', + ]; + $errMsg = $errorMap[\preg_last_error()] ?? 'Unknown PCRE error: ' . \preg_last_error(); + + return new self($errMsg, \preg_last_error()); + } +} diff --git a/src/Safe/Exceptions/SafeExceptionInterface.php b/src/Safe/Exceptions/SafeExceptionInterface.php new file mode 100644 index 000000000..63f9c65fa --- /dev/null +++ b/src/Safe/Exceptions/SafeExceptionInterface.php @@ -0,0 +1,9 @@ + Returns an array containing substrings of subject split along boundaries matched by pattern. + * + * @throws PcreException + */ +function preg_split(string $pattern, string $subject, ?int $limit = -1, int $flags = 0): array +{ + \error_clear_last(); + $safeResult = \preg_split($pattern, $subject, $limit, $flags); + if ($safeResult === false) { + throw PcreException::createFromPhpError(); + } + + return $safeResult; +} + +/** + * Searches subject for matches to pattern and replaces them with replacement. + * + * @param string[]|string $pattern The pattern to search for. + * @param string[]|string $replacement The string or an array with strings to replace. + * @param string|array|string[] $subject The string or an array with strings to search and replace. + * @param int $limit The maximum possible replacements for each pattern. Defaults to -1 (no limit). + * @param int $count If specified, this variable will be filled with the number of replacements done. + * @param-out int $count + * + * @return string|array|string[] Returns an array if the subject parameter is an array, or a string otherwise. + * + * @throws PcreException + */ +function preg_replace($pattern, $replacement, $subject, int $limit = -1, ?int &$count = null) +{ + \error_clear_last(); + $result = \preg_replace($pattern, $replacement, $subject, $limit, $count); + if (\preg_last_error() !== PREG_NO_ERROR || $result === null) { + throw PcreException::createFromPhpError(); + } + + return $result; +} + +/** + * Performs a character set conversion on the string from from_encoding to to_encoding. + * + * @param string $from_encoding The input charset. + * @param string $to_encoding The output charset. + * @param string $string The string to be converted. + * + * @return string Returns the converted string. + * + * @throws IconvException + */ +function iconv(string $from_encoding, string $to_encoding, string $string): string +{ + \error_clear_last(); + $safeResult = \iconv($from_encoding, $to_encoding, $string); + if ($safeResult === false) { + throw IconvException::createFromPhpError(); + } + + return $safeResult; +} + +/** + * This function is similar to file, except that file_get_contents returns the file in a string. + * + * @param string $filename Name of the file to read. + * @param bool $use_include_path Whether to search in the include path. + * @param resource|null $context A valid context resource created with stream_context_create. + * @param int $offset The offset where the reading starts on the original stream. + * @param 0|positive-int $length Maximum length of data read. + * + * @return string The function returns the read data. + * + * @throws FilesystemException + */ +function file_get_contents(string $filename, bool $use_include_path = false, $context = null, int $offset = 0, ?int $length = null): string +{ + \error_clear_last(); + if ($length !== null) { + $safeResult = \file_get_contents($filename, $use_include_path, $context, $offset, $length); + } elseif ($offset !== 0) { + $safeResult = \file_get_contents($filename, $use_include_path, $context, $offset); + } elseif ($context !== null) { + $safeResult = \file_get_contents($filename, $use_include_path, $context); + } else { + $safeResult = \file_get_contents($filename, $use_include_path); + } + if ($safeResult === false) { + throw FilesystemException::createFromPhpError(); + } + + return $safeResult; +} + +/** + * Opens up a directory handle to be used in subsequent closedir, readdir, and rewinddir calls. + * + * @param string $directory The directory path that is to be opened. + * @param resource|null $context For a description of the context parameter, refer to the streams section. + * + * @return resource Returns a directory handle resource on success. + * + * @throws DirException + */ +function opendir(string $directory, $context = null) +{ + \error_clear_last(); + if ($context !== null) { + $safeResult = \opendir($directory, $context); + } else { + $safeResult = \opendir($directory); + } + if ($safeResult === false) { + throw DirException::createFromPhpError(); + } + + return $safeResult; +} diff --git a/src/Value/CSSString.php b/src/Value/CSSString.php index 8271671f2..180db0cea 100644 --- a/src/Value/CSSString.php +++ b/src/Value/CSSString.php @@ -11,7 +11,7 @@ use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\ShortClassNameProvider; -use function Safe\preg_match; +use function Sabberworm\CSS\Safe\preg_match; /** * This class is a wrapper for quoted strings to distinguish them from keywords. diff --git a/src/Value/CalcFunction.php b/src/Value/CalcFunction.php index 3d85b22a7..852d15b57 100644 --- a/src/Value/CalcFunction.php +++ b/src/Value/CalcFunction.php @@ -8,7 +8,7 @@ use Sabberworm\CSS\Parsing\UnexpectedEOFException; use Sabberworm\CSS\Parsing\UnexpectedTokenException; -use function Safe\preg_match; +use function Sabberworm\CSS\Safe\preg_match; class CalcFunction extends CSSFunction { diff --git a/src/Value/Size.php b/src/Value/Size.php index ad6d794cc..ac11a168a 100644 --- a/src/Value/Size.php +++ b/src/Value/Size.php @@ -10,8 +10,8 @@ use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\ShortClassNameProvider; -use function Safe\preg_match; -use function Safe\preg_replace; +use function Sabberworm\CSS\Safe\preg_match; +use function Sabberworm\CSS\Safe\preg_replace; /** * A `Size` consists of a numeric `size` value and a unit. diff --git a/src/Value/Value.php b/src/Value/Value.php index 67065d370..8c86e32b0 100644 --- a/src/Value/Value.php +++ b/src/Value/Value.php @@ -13,7 +13,7 @@ use Sabberworm\CSS\Position\Positionable; use Sabberworm\CSS\ShortClassNameProvider; -use function Safe\preg_match; +use function Sabberworm\CSS\Safe\preg_match; /** * Abstract base class for specific classes of CSS values: `Size`, `Color`, `CSSString` and `URL`, and another diff --git a/tests/ParserTest.php b/tests/ParserTest.php index d05dc8a88..f0c5613be 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -30,8 +30,8 @@ use Sabberworm\CSS\Value\URL; use Sabberworm\CSS\Value\ValueList; -use function Safe\file_get_contents; -use function Safe\opendir; +use function Sabberworm\CSS\Safe\file_get_contents; +use function Sabberworm\CSS\Safe\opendir; /** * @covers \Sabberworm\CSS\Parser diff --git a/tests/RuleSet/LenientParsingTest.php b/tests/RuleSet/LenientParsingTest.php index ae249701d..200326464 100644 --- a/tests/RuleSet/LenientParsingTest.php +++ b/tests/RuleSet/LenientParsingTest.php @@ -10,7 +10,7 @@ use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\Settings; -use function Safe\file_get_contents; +use function Sabberworm\CSS\Safe\file_get_contents; /** * @coversNothing