X-Git-Url: https://git.cyclocoop.org/%27.WWW_URL.%27admin/?a=blobdiff_plain;f=includes%2Futils%2FAutoloadGenerator.php;h=260f66c9797c5c8753ced294ddc1250c09dbd11b;hb=9642ee9419450bf0e944c08a5f0c27768f5d7b88;hp=511b67346b93f48499b3a1d574ab1249eb7fd898;hpb=fe25fde55bc9e58741965ad0f2275a7ba2ac77fa;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/utils/AutoloadGenerator.php b/includes/utils/AutoloadGenerator.php index 511b67346b..260f66c979 100644 --- a/includes/utils/AutoloadGenerator.php +++ b/includes/utils/AutoloadGenerator.php @@ -1,4 +1,22 @@ path + */ + protected $psr4Namespaces = []; + /** * @param string $basepath Root path of the project being scanned for classes * @param array|string $flags @@ -79,6 +104,22 @@ class AutoloadGenerator { } } + /** + * Set PSR4 namespaces + * + * Unlike self::setExcludePaths(), this will only skip outputting the + * autoloader entry when the namespace matches the path. + * + * @since 1.32 + * @param string[] $namespaces Associative array mapping namespace to path + */ + public function setPsr4Namespaces( array $namespaces ) { + foreach ( $namespaces as $ns => $path ) { + $ns = rtrim( $ns, '\\' ) . '\\'; + $this->psr4Namespaces[$ns] = rtrim( self::normalizePathSeparator( $path ), '/' ); + } + } + /** * Whether the file should be excluded * @@ -135,6 +176,25 @@ class AutoloadGenerator { $result = $this->collector->getClasses( file_get_contents( $inputPath ) ); + + // Filter out classes that will be found by PSR4 + $result = array_filter( $result, function ( $class ) use ( $inputPath ) { + $parts = explode( '\\', $class ); + for ( $i = count( $parts ) - 1; $i > 0; $i-- ) { + $ns = implode( '\\', array_slice( $parts, 0, $i ) ) . '\\'; + if ( isset( $this->psr4Namespaces[$ns] ) ) { + $expectedPath = $this->psr4Namespaces[$ns] . '/' + . implode( '/', array_slice( $parts, $i ) ) + . '.php'; + if ( $inputPath === $expectedPath ) { + return false; + } + } + } + + return true; + } ); + if ( $result ) { $shortpath = substr( $inputPath, $len ); $this->classes[$shortpath] = $result; @@ -326,190 +386,3 @@ EOD; } } } - -/** - * Reads PHP code and returns the FQCN of every class defined within it. - */ -class ClassCollector { - - /** - * @var string Current namespace - */ - protected $namespace = ''; - - /** - * @var array List of FQCN detected in this pass - */ - protected $classes; - - /** - * @var array Token from token_get_all() that started an expect sequence - */ - protected $startToken; - - /** - * @var array List of tokens that are members of the current expect sequence - */ - protected $tokens; - - /** - * @var array Class alias with target/name fields - */ - protected $alias; - - /** - * @param string $code PHP code (including namespace = ''; - $this->classes = []; - $this->startToken = null; - $this->alias = null; - $this->tokens = []; - - foreach ( token_get_all( $code ) as $token ) { - if ( $this->startToken === null ) { - $this->tryBeginExpect( $token ); - } else { - $this->tryEndExpect( $token ); - } - } - - return $this->classes; - } - - /** - * Determine if $token begins the next expect sequence. - * - * @param array $token - */ - protected function tryBeginExpect( $token ) { - if ( is_string( $token ) ) { - return; - } - // Note: When changing class name discovery logic, - // AutoLoaderStructureTest.php may also need to be updated. - switch ( $token[0] ) { - case T_NAMESPACE: - case T_CLASS: - case T_INTERFACE: - case T_TRAIT: - case T_DOUBLE_COLON: - case T_NEW: - $this->startToken = $token; - break; - case T_STRING: - if ( $token[1] === 'class_alias' ) { - $this->startToken = $token; - $this->alias = []; - } - } - } - - /** - * Accepts the next token in an expect sequence - * - * @param array $token - */ - protected function tryEndExpect( $token ) { - switch ( $this->startToken[0] ) { - case T_DOUBLE_COLON: - // Skip over T_CLASS after T_DOUBLE_COLON because this is something like - // "self::static" which accesses the class name. It doens't define a new class. - $this->startToken = null; - break; - case T_NEW: - // Skip over T_CLASS after T_NEW because this is a PHP 7 anonymous class. - if ( !is_array( $token ) || $token[0] !== T_WHITESPACE ) { - $this->startToken = null; - } - break; - case T_NAMESPACE: - if ( $token === ';' || $token === '{' ) { - $this->namespace = $this->implodeTokens() . '\\'; - } else { - $this->tokens[] = $token; - } - break; - - case T_STRING: - if ( $this->alias !== null ) { - // Flow 1 - Two string literals: - // - T_STRING class_alias - // - '(' - // - T_CONSTANT_ENCAPSED_STRING 'TargetClass' - // - ',' - // - T_WHITESPACE - // - T_CONSTANT_ENCAPSED_STRING 'AliasName' - // - ')' - // Flow 2 - Use of ::class syntax for first parameter - // - T_STRING class_alias - // - '(' - // - T_STRING TargetClass - // - T_DOUBLE_COLON :: - // - T_CLASS class - // - ',' - // - T_WHITESPACE - // - T_CONSTANT_ENCAPSED_STRING 'AliasName' - // - ')' - if ( $token === '(' ) { - // Start of a function call to class_alias() - $this->alias = [ 'target' => false, 'name' => false ]; - } elseif ( $token === ',' ) { - // Record that we're past the first parameter - if ( $this->alias['target'] === false ) { - $this->alias['target'] = true; - } - } elseif ( is_array( $token ) && $token[0] === T_CONSTANT_ENCAPSED_STRING ) { - if ( $this->alias['target'] === true ) { - // We already saw a first argument, this must be the second. - // Strip quotes from the string literal. - $this->alias['name'] = substr( $token[1], 1, -1 ); - } - } elseif ( $token === ')' ) { - // End of function call - $this->classes[] = $this->alias['name']; - $this->alias = null; - $this->startToken = null; - } elseif ( !is_array( $token ) || ( - $token[0] !== T_STRING && - $token[0] !== T_DOUBLE_COLON && - $token[0] !== T_CLASS && - $token[0] !== T_WHITESPACE - ) ) { - // Ignore this call to class_alias() - compat/Timestamp.php - $this->alias = null; - $this->startToken = null; - } - } - break; - - case T_CLASS: - case T_INTERFACE: - case T_TRAIT: - $this->tokens[] = $token; - if ( is_array( $token ) && $token[0] === T_STRING ) { - $this->classes[] = $this->namespace . $this->implodeTokens(); - } - } - } - - /** - * Returns the string representation of the tokens within the - * current expect sequence and resets the sequence. - * - * @return string - */ - protected function implodeTokens() { - $content = []; - foreach ( $this->tokens as $token ) { - $content[] = is_string( $token ) ? $token : $token[1]; - } - - $this->tokens = []; - $this->startToken = null; - - return trim( implode( '', $content ), " \n\t" ); - } -}