From 569d0156ee33014f1f48d591cf64cd866182c6f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Guzm=C3=A1n=20Maeso?= Date: Tue, 27 May 2025 12:13:23 +0200 Subject: [PATCH] feat: improve namespaces PHPFile --- src/PhpFileCleaner.php | 13 ++++++++----- src/PhpFileParser.php | 37 +++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/PhpFileCleaner.php b/src/PhpFileCleaner.php index b6222aa..4591aa2 100644 --- a/src/PhpFileCleaner.php +++ b/src/PhpFileCleaner.php @@ -53,11 +53,11 @@ class PhpFileCleaner public static function setTypeConfig(array $types): void { foreach ($types as $type) { - self::$typeConfig[$type[0]] = array( + self::$typeConfig[$type[0]] = [ 'name' => $type, 'length' => \strlen($type), 'pattern' => '{.\b(?])'.$type.'\s++[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+}Ais', - ); + ]; } self::$restPattern = '{[^?"\'skipToNewline(); continue; } + if ($this->peek('*')) { $this->skipComment(); continue; @@ -122,9 +123,7 @@ public function clean(): string \substr($this->contents, $this->index, $type['length']) === $type['name'] && Preg::isMatch($type['pattern'], $this->contents, $match, 0, $this->index - 1) ) { - $clean .= $match[0]; - - return $clean; + return $clean . $match[0]; } } @@ -161,10 +160,12 @@ private function skipString(string $delimiter): void $this->index += 2; continue; } + if ($this->contents[$this->index] === $delimiter) { $this->index += 1; break; } + $this->index += 1; } } @@ -188,6 +189,7 @@ private function skipToNewline(): void if ($this->contents[$this->index] === "\r" || $this->contents[$this->index] === "\n") { return; } + $this->index += 1; } } @@ -214,6 +216,7 @@ private function skipHeredoc(string $delimiter): void return; } + break; } diff --git a/src/PhpFileParser.php b/src/PhpFileParser.php index 5a6875d..80f6559 100644 --- a/src/PhpFileParser.php +++ b/src/PhpFileParser.php @@ -12,6 +12,7 @@ namespace Composer\ClassMapGenerator; +use RuntimeException; use Composer\Pcre\Preg; /** @@ -23,7 +24,7 @@ class PhpFileParser * Extract the classes in the given file * * @param string $path The file to check - * @throws \RuntimeException + * @throws RuntimeException * @return list The found classes */ public static function findClasses(string $path): array @@ -31,8 +32,9 @@ public static function findClasses(string $path): array $extraTypes = self::getExtraTypes(); if (!function_exists('php_strip_whitespace')) { - throw new \RuntimeException('Classmap generation relies on the php_strip_whitespace function, but it has been disabled by the disable_functions directive.'); + throw new RuntimeException('Classmap generation relies on the php_strip_whitespace function, but it has been disabled by the disable_functions directive.'); } + // Use @ here instead of Silencer to actively suppress 'unhelpful' output // @link https://github.com/composer/composer/pull/4886 $contents = @php_strip_whitespace($path); @@ -43,21 +45,23 @@ public static function findClasses(string $path): array $message = 'File at "%s" is not readable, check its permissions'; } elseif ('' === trim((string) file_get_contents($path))) { // The input file was really empty and thus contains no classes - return array(); + return []; } else { $message = 'File at "%s" could not be parsed as PHP, it may be binary or corrupted'; } + $error = error_get_last(); if (isset($error['message'])) { $message .= PHP_EOL . 'The following message may be helpful:' . PHP_EOL . $error['message']; } - throw new \RuntimeException(sprintf($message, $path)); + + throw new RuntimeException(sprintf($message, $path)); } // return early if there is no chance of matching anything in this file Preg::matchAllStrictGroups('{\b(?:class|interface|trait'.$extraTypes.')\s}i', $contents, $matches); - if (0 === \count($matches)) { - return array(); + if ([] === $matches) { + return []; } $p = new PhpFileCleaner($contents, count($matches[0])); @@ -71,22 +75,26 @@ public static function findClasses(string $path): array ) }ix', $contents, $matches); - $classes = array(); + $classes = []; $namespace = ''; - for ($i = 0, $len = count($matches['type']); $i < $len; $i++) { + for ($i = 0, $len = count($matches['type']); $i < $len; ++$i) { if (isset($matches['ns'][$i]) && $matches['ns'][$i] !== '') { - $namespace = str_replace(array(' ', "\t", "\r", "\n"), '', (string) $matches['nsname'][$i]) . '\\'; + $namespace = str_replace([' ', "\t", "\r", "\n"], '', (string) $matches['nsname'][$i]) . '\\'; } else { $name = $matches['name'][$i]; assert(is_string($name)); // skip anon classes extending/implementing - if ($name === 'extends' || $name === 'implements') { + if ($name === 'extends') { + continue; + } + if ($name === 'implements') { continue; } + if ($name[0] === ':') { // This is an XHP class, https://github.com/facebook/xhp - $name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1); + $name = 'xhp'.substr(str_replace(['-', ':'], ['_', '__'], $name), 1); } elseif (strtolower((string) $matches['type'][$i]) === 'enum') { // something like: // enum Foo: int { HERP = '123'; } @@ -101,6 +109,7 @@ public static function findClasses(string $path): array $name = substr($name, 0, $colonPos); } } + /** @var class-string */ $className = ltrim($namespace . $name, '\\'); $classes[] = $className; @@ -110,9 +119,6 @@ public static function findClasses(string $path): array return $classes; } - /** - * @return string - */ private static function getExtraTypes(): string { static $extraTypes = null; @@ -123,7 +129,7 @@ private static function getExtraTypes(): string $extraTypes .= '|enum'; } - $extraTypesArray = array_filter(explode('|', $extraTypes), function (string $type) { + $extraTypesArray = array_filter(explode('|', $extraTypes), function (string $type): bool { return $type !== ''; }); PhpFileCleaner::setTypeConfig(array_merge(['class', 'interface', 'trait'], $extraTypesArray)); @@ -140,7 +146,6 @@ private static function getExtraTypes(): string * * @see Composer\Util\Filesystem::isReadable * - * @param string $path * @return bool */ private static function isReadable(string $path)