AutoLoader: Skip tokenizing of irrelevant lines in ClassCollector
authorTimo Tijhof <krinklemail@gmail.com>
Tue, 18 Jun 2019 17:26:06 +0000 (18:26 +0100)
committerKrinkle <krinklemail@gmail.com>
Wed, 19 Jun 2019 15:09:49 +0000 (15:09 +0000)
This makes AutoLoaderStructureTest in PHPUnit and the
generateLocalAutoload.php maintenance script much faster.

On my machine, it made it 35X faster (or time spent reduced by 97%).

Bug: T225730
Change-Id: Ife959bd17ce9c2ae952dfbd158ddb3d8475e8cb2

includes/utils/ClassCollector.php
tests/phpunit/includes/utils/ClassCollectorTest.php
tests/phpunit/structure/AutoLoaderStructureTest.php

index c987354..12b8a70 100644 (file)
@@ -59,6 +59,31 @@ class ClassCollector {
                $this->alias = null;
                $this->tokens = [];
 
+               // HACK: The PHP tokenizer is slow (T225730).
+               // Speed it up by reducing the input to the three kinds of statement we care about:
+               // - namespace X;
+               // - [final] [abstract] class X … {}
+               // - class_alias( … );
+               $lines = [];
+               $matches = null;
+               preg_match_all(
+                       // phpcs:ignore Generic.Files.LineLength.TooLong
+                       '#^\t*(?:namespace |(final )?(abstract )?(class|interface|trait) |class_alias\()[^;{]+[;{]\s*\}?#m',
+                       $code,
+                       $matches
+               );
+               if ( isset( $matches[0][0] ) ) {
+                       foreach ( $matches[0] as $match ) {
+                               $match = trim( $match );
+                               if ( substr( $match, -1 ) === '{' ) {
+                                       // Keep it balanced
+                                       $match .= '}';
+                               }
+                               $lines[] = $match;
+                       }
+               }
+               $code = '<?php ' . implode( "\n", $lines ) . "\n";
+
                foreach ( token_get_all( $code ) as $token ) {
                        if ( $this->startToken === null ) {
                                $this->tryBeginExpect( $token );
index 9c7c50f..6f8aa52 100644 (file)
@@ -29,10 +29,21 @@ class ClassCollectorTest extends PHPUnit\Framework\TestCase {
                                "namespace Example;\nclass Foo {}\nclass_alias( 'Example\Foo', 'Bar' );",
                                [ 'Example\Foo', 'Bar' ],
                        ],
+                       [
+                               // Support a multiline 'class' statement
+                               "namespace Example;\nclass Foo extends\n\tFooBase {\n\t"
+                                               . "public function x() {}\n}\nclass_alias( 'Example\Foo', 'Bar' );",
+                               [ 'Example\Foo', 'Bar' ],
+                       ],
                        [
                                "class_alias( Foo::class, 'Bar' );",
                                [ 'Bar' ],
                        ],
+                       [
+                               // Support nested class_alias() calls
+                                       "if ( false ) {\n\tclass_alias( Foo::class, 'Bar' );\n}",
+                                       [ 'Bar' ],
+                       ],
                        [
                                // Namespaced class is not currently supported. Must use namespace declaration
                                // earlier in the file.
index 37babce..c20be57 100644 (file)
@@ -198,7 +198,7 @@ class AutoLoaderStructureTest extends MediaWikiTestCase {
        }
 
        public function testAutoloadOrder() {
-               $path = realpath( __DIR__ . '/../../..' );
+               $path = __DIR__ . '/../../..';
                $oldAutoload = file_get_contents( $path . '/autoload.php' );
                $generator = new AutoloadGenerator( $path, 'local' );
                $generator->setPsr4Namespaces( AutoLoader::getAutoloadNamespaces() );