From 115bb8238eed094aaf3c550ecdfa7e1e3a3d2865 Mon Sep 17 00:00:00 2001 From: Raphael Stolt Date: Tue, 17 Sep 2024 07:52:08 +0200 Subject: [PATCH] Removes laminas/laminas-stdlib dependency --- CHANGELOG.md | 4 + composer.json | 3 +- phpstan.neon.dist | 1 + src/Analyser.php | 1 - src/Commands/ValidateCommand.php | 2 +- src/ErrorHandler.php | 116 +++++++++++++ src/Glob.php | 224 +++++++++++++++++++++++++ tests/Commands/ValidateCommandTest.php | 4 + 8 files changed, 351 insertions(+), 4 deletions(-) create mode 100644 src/ErrorHandler.php create mode 100644 src/Glob.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 28e1a74..8ddd273 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] +### Removed + +- Removed laminas/laminas-stdlib dependency + ## [v4.0.3] - 2024-07-10 ### Added diff --git a/composer.json b/composer.json index a7bffb8..08b6eec 100755 --- a/composer.json +++ b/composer.json @@ -11,8 +11,7 @@ ], "require": { "php": ">=8.1", - "laminas/laminas-stdlib": "^3.19", - "sebastian/diff": "^5.0||^4.0.3", + "sebastian/diff": "^6.0.1||^5.0||^4.0.3", "symfony/console": "^7.1.0||^v5.4.8" }, "autoload": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 12df076..192071a 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -14,3 +14,4 @@ parameters: - '#is never read#' - '#TMock#' - '#unknown class#' + - '#set_error_handler expects#' diff --git a/src/Analyser.php b/src/Analyser.php index 78d85b6..23dc9c2 100755 --- a/src/Analyser.php +++ b/src/Analyser.php @@ -2,7 +2,6 @@ namespace Stolt\LeanPackage; -use Laminas\Stdlib\Glob; use Stolt\LeanPackage\Exceptions\InvalidGlobPattern; use Stolt\LeanPackage\Exceptions\InvalidGlobPatternFile; use Stolt\LeanPackage\Exceptions\NonExistentGlobPatternFile; diff --git a/src/Commands/ValidateCommand.php b/src/Commands/ValidateCommand.php index 4de2793..fc0c009 100755 --- a/src/Commands/ValidateCommand.php +++ b/src/Commands/ValidateCommand.php @@ -270,7 +270,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } - } elseif($this->isGlobPatternFileSettable($globPatternFile)) { + } elseif ($this->isGlobPatternFileSettable($globPatternFile)) { try { if ($this->isDefaultGlobPatternFilePresent()) { $this->analyser->setGlobPatternFromFile($globPatternFile); diff --git a/src/ErrorHandler.php b/src/ErrorHandler.php new file mode 100644 index 0000000..3b49943 --- /dev/null +++ b/src/ErrorHandler.php @@ -0,0 +1,116 @@ + + */ + protected static $stack = []; + + /** + * Check if this error handler is active + * + * @return bool + */ + public static function started() + { + return (bool) static::getNestedLevel(); + } + + /** + * Get the current nested level + * + * @return int + */ + public static function getNestedLevel() + { + return \count(static::$stack); + } + + /** + * Starting the error handler + * + * @param int $errorLevel + * @return void + */ + public static function start($errorLevel = E_WARNING) + { + if (! static::$stack) { + \set_error_handler([static::class, 'addError'], $errorLevel); + } + + static::$stack[] = null; + } + + /** + * Stopping the error handler + * + * @param bool $throw Throw the ErrorException if any + * @throws ErrorException If an error has been caught and $throw is true. + * @return null|ErrorException + */ + public static function stop($throw = false) + { + $errorException = null; + + if (static::$stack) { + $errorException = \array_pop(static::$stack); + + if (! static::$stack) { + \restore_error_handler(); + } + + if ($errorException && $throw) { + throw $errorException; + } + } + + return $errorException; + } + + /** + * Stop all active handler + * + * @return void + */ + public static function clean() + { + if (static::$stack) { + \restore_error_handler(); + } + + static::$stack = []; + } + + /** + * Add an error to the stack + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @return void + */ + public static function addError($errno, $errstr = '', $errfile = '', $errline = 0) + { + $stack = &static::$stack[\count(static::$stack) - 1]; + $stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack); + } +} diff --git a/src/Glob.php b/src/Glob.php new file mode 100644 index 0000000..2379fcd --- /dev/null +++ b/src/Glob.php @@ -0,0 +1,224 @@ + GLOB_MARK, + self::GLOB_NOSORT => GLOB_NOSORT, + self::GLOB_NOCHECK => GLOB_NOCHECK, + self::GLOB_NOESCAPE => GLOB_NOESCAPE, + self::GLOB_BRACE => \defined('GLOB_BRACE') ? GLOB_BRACE : 0, + self::GLOB_ONLYDIR => GLOB_ONLYDIR, + self::GLOB_ERR => GLOB_ERR, + ]; + + $globFlags = 0; + + foreach ($flagMap as $internalFlag => $globFlag) { + if ($flags & $internalFlag) { + $globFlags |= $globFlag; + } + } + } else { + $globFlags = 0; + } + + ErrorHandler::start(); + $res = \glob($pattern, $globFlags); + $err = ErrorHandler::stop(); + if ($res === false) { + throw new RuntimeException("glob('{$pattern}', {$globFlags}) failed", 0, $err); + } + return $res; + } + + /** + * Expand braces manually, then use the system glob. + * + * @param string $pattern + * @param int $flags + * @throws RuntimeException + * @return array + */ + protected static function fallbackGlob($pattern, $flags) + { + if (! self::flagsIsEqualTo($flags, self::GLOB_BRACE)) { + return static::systemGlob($pattern, $flags); + } + + $flags &= ~self::GLOB_BRACE; + $length = \strlen($pattern); + $paths = []; + + if ($flags & self::GLOB_NOESCAPE) { + $begin = \strpos($pattern, '{'); + } else { + $begin = 0; + + while (true) { + if ($begin === $length) { + $begin = false; + break; + } elseif ($pattern[$begin] === '\\' && ($begin + 1) < $length) { + $begin++; + } elseif ($pattern[$begin] === '{') { + break; + } + + $begin++; + } + } + + if ($begin === false) { + return static::systemGlob($pattern, $flags); + } + + $next = static::nextBraceSub($pattern, $begin + 1, $flags); + + if ($next === null) { + return static::systemGlob($pattern, $flags); + } + + $rest = $next; + + while ($pattern[$rest] !== '}') { + $rest = static::nextBraceSub($pattern, $rest + 1, $flags); + + if ($rest === null) { + return static::systemGlob($pattern, $flags); + } + } + + $p = $begin + 1; + + while (true) { + $subPattern = \substr($pattern, 0, $begin) + . \substr($pattern, $p, $next - $p) + . \substr($pattern, $rest + 1); + + $result = static::fallbackGlob($subPattern, $flags | self::GLOB_BRACE); + + if ($result) { + $paths = \array_merge($paths, $result); + } + + if ($pattern[$next] === '}') { + break; + } + + $p = $next + 1; + $next = static::nextBraceSub($pattern, $p, $flags); + } + + return \array_unique($paths); + } + + /** + * Find the end of the sub-pattern in a brace expression. + * + * @param string $pattern + * @param int $begin + * @param int $flags + * @return int|null + */ + protected static function nextBraceSub($pattern, $begin, $flags) + { + $length = \strlen($pattern); + $depth = 0; + $current = $begin; + + while ($current < $length) { + $flagsEqualsNoEscape = self::flagsIsEqualTo($flags, self::GLOB_NOESCAPE); + + if ($flagsEqualsNoEscape && $pattern[$current] === '\\') { + if (++$current === $length) { + break; + } + + $current++; + } else { + if ( + ($pattern[$current] === '}' && $depth-- === 0) + || ($pattern[$current] === ',' && $depth === 0) + ) { + break; + } elseif ($pattern[$current++] === '{') { + $depth++; + } + } + } + + return $current < $length ? $current : null; + } + + /** @internal */ + public static function flagsIsEqualTo(int $flags, int $otherFlags): bool + { + return (bool) ($flags & $otherFlags); + } +} diff --git a/tests/Commands/ValidateCommandTest.php b/tests/Commands/ValidateCommandTest.php index 3277fcc..a6108b4 100644 --- a/tests/Commands/ValidateCommandTest.php +++ b/tests/Commands/ValidateCommandTest.php @@ -339,6 +339,10 @@ public function gitattributesFileWithNoExportIgnoresContentShowsExpectedContent( #[Ticket('https://github.com/raphaelstolt/lean-package-validator/issues/13')] public function gitattributesIsInSuggestedFileContent(): void { + if ((new OsHelper())->isWindows()) { + $this->markTestSkipped('Skipping test on Windows systems'); + } + $artifactFilenames = [ 'CONDUCT.md', 'phpspec.yml.dist',