AutoloadGenerator.php: Update 'AutoloadClasses' in extension.json
[lhc/web/wiklou.git] / includes / utils / AutoloadGenerator.php
index 727f485..dd1a38a 100644 (file)
@@ -50,7 +50,7 @@ class AutoloadGenerator {
                if ( !is_array( $flags ) ) {
                        $flags = array( $flags );
                }
-               $this->basepath = realpath( $basepath );
+               $this->basepath = self::normalizePathSeparator( realpath( $basepath ) );
                $this->collector = new ClassCollector;
                if ( in_array( 'local', $flags ) ) {
                        $this->variableName = 'wgAutoloadLocalClasses';
@@ -63,9 +63,10 @@ class AutoloadGenerator {
         *
         * @param string $fqcn FQCN to force the location of
         * @param string $inputPath Full path to the file containing the class
+        * @throws Exception
         */
        public function forceClassPath( $fqcn, $inputPath ) {
-               $path = realpath( $inputPath );
+               $path = self::normalizePathSeparator( realpath( $inputPath ) );
                if ( !$path ) {
                        throw new \Exception( "Invalid path: $inputPath" );
                }
@@ -78,9 +79,14 @@ class AutoloadGenerator {
        }
 
        /**
-        * @var string $inputPath Path to a php file to find classes within
+        * @param string $inputPath Path to a php file to find classes within
+        * @throws Exception
         */
        public function readFile( $inputPath ) {
+               // NOTE: do NOT expand $inputPath using realpath(). It is perfectly
+               // reasonable for LocalSettings.php and similiar files to be symlinks
+               // to files that are outside of $this->basepath.
+               $inputPath = self::normalizePathSeparator( $inputPath );
                $len = strlen( $this->basepath );
                if ( substr( $inputPath, 0, $len ) !== $this->basepath ) {
                        throw new \Exception( "Path is not within basepath: $inputPath" );
@@ -99,7 +105,8 @@ class AutoloadGenerator {
         *  for php files with either .php or .inc extensions
         */
        public function readDir( $dir ) {
-               $it = new RecursiveDirectoryIterator( realpath( $dir ) );
+               $it = new RecursiveDirectoryIterator(
+                       self::normalizePathSeparator( realpath( $dir ) ) );
                $it = new RecursiveIteratorIterator( $it );
 
                foreach ( $it as $path => $file ) {
@@ -119,48 +126,81 @@ class AutoloadGenerator {
         *  developers towards the appropriate way to update the autoload.
         */
        public function generateAutoload( $commandName = 'AutoloadGenerator' ) {
-               $content = array();
 
-               // We need to generate a line each rather than exporting the
-               // full array so __DIR__ can be prepended to all the paths
-               $format = "%s => __DIR__ . %s,";
-               foreach ( $this->classes as $path => $contained ) {
-                       $exportedPath = var_export( $path, true );
-                       foreach ( $contained as $fqcn ) {
+               // We need to check whether an extenson.json exists or not, and
+               // incase it doesn't, update the autoload.php file.
+
+               if ( file_exists( $this->basepath . '/extension.json' ) ) {
+                       require_once __DIR__ . '/../../includes/json/FormatJson.php';
+                       $key = 'AutoloadClasses';
+                       $json = FormatJson::decode( file_get_contents( $this->basepath
+                               . '/extension.json' ), true );
+                       unset( $json[$key] );
+                       // Inverting the key-value pairs so that they become of the
+                       // format class-name : path when they get converted into json.
+                       foreach ( $this->classes as $path => $contained ) {
+                               foreach ( $contained as $fqcn ) {
+
+                                       // Using substr to remove the leading '/'
+                                       $json[$key][$fqcn] = substr( $path, 1 );
+                               }
+                       }
+                       foreach ( $this->overrides as $path => $fqcn ) {
+
+                               // Using substr to remove the leading '/'
+                               $json[$key][$fqcn] = substr( $path, 1 );
+                       }
+
+                       // Sorting the list of autoload classes.
+                       ksort( $json[$key] );
+
+                       // Update extension.json, using constants for the required
+                       // formatting.
+                       file_put_contents( $this->basepath . '/extension.json',
+                               FormatJson::encode( $json, true ) . "\n" );
+               } else {
+                       $content = array();
+
+                       // We need to generate a line each rather than exporting the
+                       // full array so __DIR__ can be prepended to all the paths
+                       $format = "%s => __DIR__ . %s,";
+                       foreach ( $this->classes as $path => $contained ) {
+                               $exportedPath = var_export( $path, true );
+                               foreach ( $contained as $fqcn ) {
+                                       $content[$fqcn] = sprintf(
+                                               $format,
+                                               var_export( $fqcn, true ),
+                                               $exportedPath
+                                       );
+                               }
+                       }
+
+                       foreach ( $this->overrides as $fqcn => $path ) {
                                $content[$fqcn] = sprintf(
                                        $format,
                                        var_export( $fqcn, true ),
-                                       $exportedPath
+                                       var_export( $path, true )
                                );
                        }
-               }
-
-               foreach ( $this->overrides as $fqcn => $path ) {
-                       $content[$fqcn] = sprintf(
-                               $format,
-                               var_export( $fqcn, true ),
-                               var_export( $path, true )
-                       );
-               }
 
-               // sort for stable output
-               ksort( $content );
+                       // sort for stable output
+                       ksort( $content );
 
-               // extensions using this generator are appending to the existing
-               // autoload.
-               if ( $this->variableName === 'wgAutoloadClasses' ) {
-                       $op = '+=';
-               } else {
-                       $op = '=';
-               }
+                       // extensions using this generator are appending to the existing
+                       // autoload.
+                       if ( $this->variableName === 'wgAutoloadClasses' ) {
+                               $op = '+=';
+                       } else {
+                               $op = '=';
+                       }
 
-               $output = implode( "\n\t", $content );
-               file_put_contents(
-                       $this->basepath . '/autoload.php',
-                       <<<EOD
+                       $output = implode( "\n\t", $content );
+                       file_put_contents(
+                               $this->basepath . '/autoload.php',
+                               <<<EOD
 <?php
 // This file is generated by $commandName, do not adjust manually
-
+// @codingStandardsIgnoreFile
 global \${$this->variableName};
 
 \${$this->variableName} {$op} array(
@@ -168,7 +208,17 @@ global \${$this->variableName};
 );
 
 EOD
-               );
+                       );
+               }
+       }
+       /**
+        * Ensure that Unix-style path separators ("/") are used in the path.
+        *
+        * @param string $path
+        * @return string
+        */
+       protected static function normalizePathSeparator( $path ) {
+               return str_replace( '\\', '/', $path );
        }
 }
 
@@ -227,7 +277,7 @@ class ClassCollector {
                if ( is_string( $token ) ) {
                        return;
                }
-               switch( $token[0] ) {
+               switch ( $token[0] ) {
                case T_NAMESPACE:
                case T_CLASS:
                case T_INTERFACE:
@@ -241,7 +291,7 @@ class ClassCollector {
         * @param array
         */
        protected function tryEndExpect( $token ) {
-               switch( $this->startToken[0] ) {
+               switch ( $this->startToken[0] ) {
                case T_NAMESPACE:
                        if ( $token === ';' || $token === '{' ) {
                                $this->namespace = $this->implodeTokens() . '\\';