Tim Starling has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/392364 )

Change subject: Added conflict and reserved word detection
......................................................................


Added conflict and reserved word detection

Detect conflicts due to two classes being renamed to the same thing.
Exempt existing class_alias() calls. Detect class names that use PHP
reserved words.

Change-Id: I91762c902b049fea472a189073acc87ab0ada85f
---
M bin/dumpCoreAliases.php
M src/CoreAliases.php
A src/ReservedWords.php
3 files changed, 333 insertions(+), 117 deletions(-)

Approvals:
  Tim Starling: Verified
  Anomie: Looks good to me, approved



diff --git a/bin/dumpCoreAliases.php b/bin/dumpCoreAliases.php
index e115c2b..f5b88e0 100644
--- a/bin/dumpCoreAliases.php
+++ b/bin/dumpCoreAliases.php
@@ -9,76 +9,94 @@
 
 require __DIR__ . '/../vendor/autoload.php';
 
-$self = array_shift( $argv );
-
-if ( !count( $argv ) ) {
-       echo "Usage: $self <autoload file>\n";
-       exit( 1 );
+function warnCallback( $msg ) {
+       fwrite( STDERR, "FYI: $msg\n" );
 }
 
-if ( $argv[0] === '--counts' ) {
-       $report = 'counts';
-       array_shift( $argv );
-} elseif ( $argv[0] === '--3col' ) {
-       $report = '3col';
-       array_shift( $argv );
-} elseif ( $argv[0] === '--classes' ) {
-       $report = 'classes';
-       array_shift( $argv );
-} else {
-       $report = 'php';
+function errorCallback( $msg ) {
+       fwrite( STDERR, "ERROR: $msg\n" );
 }
 
-if ( !count( $argv ) ) {
-       echo "Usage: $self <autoload file>\n";
-       exit( 1 );
-}
+function dumpCoreAliases() {
+       global $argv;
 
-$file = $argv[0];
+       $self = array_shift( $argv );
 
-$autoload = AutoloadLoader::getAutoloadClasses( $file );
-if ( $autoload === false ) {
-       echo "Unable to open autoload file $file\n";
-       exit( 1 );
-}
+       if ( !count( $argv ) ) {
+               echo "Usage: $self <autoload file>\n";
+               exit( 1 );
+       }
 
-$ca = new CoreAliases( $autoload );
-$aliases = $ca->getAliases();
-
-asort( $aliases );
-
-$counts = [];
-if ( $report === 'php' ) {
-       print "<?php\n\nreturn [\n";
-}
-foreach ( $aliases as $old => $new ) {
-       if ( $report === 'classes' ) {
-               print "$old\t$new\n";
-       } elseif ( $report === 'php' ) {
-               print "\t'" .
-                       strtr( $old, [ "\\" => "\\\\", "'" => "\\'" ] ) .
-                       "' => '" .
-                       strtr( $new, [ "\\" => "\\\\", "'" => "\\'" ] ) .
-                       "',\n";
+       if ( $argv[0] === '--counts' ) {
+               $report = 'counts';
+               array_shift( $argv );
+       } elseif ( $argv[0] === '--3col' ) {
+               $report = '3col';
+               array_shift( $argv );
+       } elseif ( $argv[0] === '--classes' ) {
+               $report = 'classes';
+               array_shift( $argv );
        } else {
-               if ( preg_match( '/(.*)\\\\([^\\\\]*)$/', $new, $m ) ) {
-                       if ( $report === 'counts' ) {
-                               if ( !isset( $counts[$m[1]] ) ) {
-                                       $counts[$m[1]] = 0;
+               $report = 'php';
+       }
+
+       if ( !count( $argv ) ) {
+               echo "Usage: $self <autoload file>\n";
+               exit( 1 );
+       }
+
+       $file = $argv[0];
+
+       $autoload = AutoloadLoader::getAutoloadClasses( $file );
+       if ( $autoload === false ) {
+               echo "Unable to open autoload file $file\n";
+               exit( 1 );
+       }
+
+       $ca = new CoreAliases( $autoload, 'errorCallback', 'warnCallback' );
+       $aliases = $ca->getAliases();
+       
+       if ( $aliases === false ) {
+               exit( 1 );
+       }
+
+       asort( $aliases );
+
+       $counts = [];
+       if ( $report === 'php' ) {
+               print "<?php\n\nreturn [\n";
+       }
+       foreach ( $aliases as $old => $new ) {
+               if ( $report === 'classes' ) {
+                       print "$old\t$new\n";
+               } elseif ( $report === 'php' ) {
+                       print "\t'" .
+                               strtr( $old, [ "\\" => "\\\\", "'" => "\\'" ] ) 
.
+                               "' => '" .
+                               strtr( $new, [ "\\" => "\\\\", "'" => "\\'" ] ) 
.
+                               "',\n";
+               } else {
+                       if ( preg_match( '/(.*)\\\\([^\\\\]*)$/', $new, $m ) ) {
+                               if ( $report === 'counts' ) {
+                                       if ( !isset( $counts[$m[1]] ) ) {
+                                               $counts[$m[1]] = 0;
+                                       }
+                                       $counts[$m[1]]++;
+                               } elseif ( $report === '3col' ) {
+                                       print "$old\t{$m[1]}\t{$m[2]}\n";
                                }
-                               $counts[$m[1]]++;
-                       } elseif ( $report === '3col' ) {
-                               print "$old\t{$m[1]}\t{$m[2]}\n";
                        }
                }
        }
-}
-if ( $report === 'php' ) {
-       print "];\n";
-}
+       if ( $report === 'php' ) {
+               print "];\n";
+       }
 
-if ( $report === 'counts' ) {
-       foreach ( $counts as $ns => $count ) {
-               print "$ns\t$count\n";
+       if ( $report === 'counts' ) {
+               foreach ( $counts as $ns => $count ) {
+                       print "$ns\t$count\n";
+               }
        }
 }
+
+dumpCoreAliases();
diff --git a/src/CoreAliases.php b/src/CoreAliases.php
index e78e507..1541692 100644
--- a/src/CoreAliases.php
+++ b/src/CoreAliases.php
@@ -442,13 +442,67 @@
                'MWUnknownContentModelException' => 
'MediaWiki\Exception\UnknownContentModelException',
        ];
 
+       static $existingAliases = [
+               'Blob' => 'Wikimedia\Rdbms\Encasing\Blob',
+               'DBAccessError' => 'Wikimedia\Rdbms\Exception\DBAccessError',
+               'DBConnRef' => 'Wikimedia\Rdbms\Database\DBConnRef',
+               'DBConnectionError' => 
'Wikimedia\Rdbms\Exception\DBConnectionError',
+               'DBError' => 'Wikimedia\Rdbms\Exception\DBError',
+               'DBExpectedError' => 
'Wikimedia\Rdbms\Exception\DBExpectedError',
+               'DBQueryError' => 'Wikimedia\Rdbms\Exception\DBQueryError',
+               'DBReadOnlyError' => 
'Wikimedia\Rdbms\Exception\DBReadOnlyError',
+               'DBReplicationWaitError' => 
'Wikimedia\Rdbms\Exception\DBReplicationWaitError',
+               'DBTransactionError' => 
'Wikimedia\Rdbms\Exception\DBTransactionError',
+               'DBTransactionSizeError' => 
'Wikimedia\Rdbms\Exception\DBTransactionSizeError',
+               'DBUnexpectedError' => 
'Wikimedia\Rdbms\Exception\DBUnexpectedError',
+               'Database' => 'Wikimedia\Rdbms\Database\Database',
+               'DatabaseBase' => 'Wikimedia\Rdbms\Database\Database',
+               'DatabaseMssql' => 'Wikimedia\Rdbms\Database\DatabaseMssql',
+               'DatabaseMysql' => 'Wikimedia\Rdbms\Database\DatabaseMysql',
+               'DatabaseMysqlBase' => 
'Wikimedia\Rdbms\Database\DatabaseMysqlBase',
+               'DatabaseMysqli' => 'Wikimedia\Rdbms\Database\DatabaseMysqli',
+               'DatabasePostgres' => 
'Wikimedia\Rdbms\Database\DatabasePostgres',
+               'DatabaseSqlite' => 'Wikimedia\Rdbms\Database\DatabaseSqlite',
+               'FakeResultWrapper' => 
'Wikimedia\Rdbms\Database\ResultWrapper\FakeResultWrapper',
+               'Field' => 'Wikimedia\Rdbms\Field\Field',
+               'IDatabase' => 'Wikimedia\Rdbms\Database\IDatabase',
+               'IMaintainableDatabase' => 
'Wikimedia\Rdbms\Database\IMaintainableDatabase',
+               'LBFactory' => 'Wikimedia\Rdbms\LBFactory\LBFactory',
+               'LoadBalancer' => 'Wikimedia\Rdbms\LoadBalancer\LoadBalancer',
+               'LoadBalancerSingle' => 
'Wikimedia\Rdbms\LoadBalancer\LoadBalancerSingle',
+               'MaintainableDBConnRef' => 
'Wikimedia\Rdbms\Database\MaintainableDBConnRef',
+               'ResultWrapper' => 
'Wikimedia\Rdbms\Database\ResultWrapper\ResultWrapper',
+               'TimestampException' => 
'Wikimedia\Timestamp\TimestampException',
+       ];
+
        static $ignoreDirs = [
                'maintenance',
        ];
 
-       public function __construct( $autoloadClasses ) {
+       private $aliases;
+       private $autoloadClasses;
+       private $errorCallback;
+       private $warnCallback;
+       private $valid;
+
+       public function __construct( $autoloadClasses, $errorCallback = null, 
$warnCallback = null ) {
                $this->autoloadClasses = $autoloadClasses;
+               $this->errorCallback = $errorCallback;
+               $this->warnCallback = $warnCallback;
        }
+
+       private function warn( $msg ) {
+               if ( $this->warnCallback ) {
+                       call_user_func( $this->warnCallback, $msg );
+               }
+       }
+
+       private function error( $msg ) {
+               if ( $this->errorCallback ) {
+                       call_user_func( $this->errorCallback, $msg );
+               }
+               $this->valid = false;
+       }               
 
        private function getAutoloadClasses() {
                $autoload = file_get_contents( $this->options['autoloadFile'] );
@@ -461,66 +515,118 @@
                return substr( $haystack, 0, strlen( $needle ) ) === $needle;
        }
 
-       public function getAliases() {
-               $aliases = [];
-
-               foreach ( $this->autoloadClasses as $class => $file ) {
-                       $dir = dirname( $file );
-
-                       if ( isset( self::$renamedClasses[$class] ) ) {
-                               $aliases[$class] = 
self::$renamedClasses[$class];
-                               continue;
+       private function addAlias( $source, $dest ) {
+               // Check for reserved words
+               $parts = explode( '\\', $dest );
+               foreach ( $parts as $part ) {
+                       if ( ReservedWords::isReserved( $part ) ) {
+                               $this->error( "Class name is reserved: $dest" );
                        }
-                       
-                       if ( isset( self::$namespacesByClass[$class] ) ) {
-                               $aliases[$class] = 
self::$namespacesByClass[$class] . '\\' . $class;
-                               continue;
-                       }
-
-                       if ( isset( self::$deprefixesByDir[$dir] ) ) {
-                               list( $namespace, $prefix ) = 
self::$deprefixesByDir[$dir];
-                               if ( $this->startsWith( $class, $prefix ) ) {
-                                       $aliases[$class] = $namespace . '\\' . 
substr( $class, strlen( $prefix ) );
-                                       continue;
-                               } else {
-                                       $aliases[$class] = $namespace . '\\' . 
$class;
-                                       continue;
-                               }
-                       }
-
-                       if ( isset( self::$namespacesByDir[$dir] ) ) {
-                               $namespace = self::$namespacesByDir[$dir];
-                               $slashPos = strrpos( $class, '\\' );
-                               if ( $slashPos !== false ) {
-                                       $priorNamespace = substr( $class, 0, 
$slashPos );
-                                       $classPart = substr( $class, $slashPos 
+ 1 );
-                                       
-                                       if ( $priorNamespace !== $namespace ) {
-                                               $aliases[$class] = $namespace . 
'\\' . $classPart;
-                                               continue;
-                                       } else {
-                                               continue;
-                                       }
-                               } else {
-                                       $aliases[$class] = $namespace . '\\' . 
$class;
-                                       continue;
-                               }
-                       }
-
-                       foreach ( self::$ignoreDirs as $ignoreDir ) {
-                               if ( $this->startsWith( $dir, "$ignoreDir/" ) 
|| $dir === $ignoreDir ) {
-                                       continue 2;
-                               }
-                       }
-
-                       if ( $dir === '.' ) {
-                               // profileinfo.php
-                               continue;
-                       }
-
-                       throw new \Exception( "Don't know what to do with class 
$class in directory $dir" );
                }
 
-               return $aliases;
+               // Check for conflicts
+               $lcDest = strtolower( $dest );
+               if ( isset( $this->destClasses[$lcDest]) ) {
+                       $this->error( "Duplicate qualified class name 
\"$dest\", " .
+                               "from sources \"$source\" and 
\"{$this->destClasses[$lcDest]}\"" );
+               }
+
+               if ( isset( $this->aliases[$source] ) ) {
+                       $this->error( "Duplicate source class name \"$source\"" 
);
+               }
+
+               $lcLastPart = strtolower( end( $parts ) );
+               $this->destNames[$lcLastPart][] = $dest;
+               $this->destClasses[$lcDest] = $source;
+               $this->aliases[$source] = $dest;
+       }
+
+       private function resetAliases() {
+               $this->aliases = [];
+               $this->valid = true;
+       }
+
+       private function reportSoftConflicts() {
+               foreach ( $this->destNames as $name => $nameList ) {
+                       if ( count ( $nameList ) > 1 ) {
+                               $this->warn( "Duplicate unqualified name $name: 
" . implode( ', ', $nameList ) );
+                       }
+               }
+       }
+
+       public function getAliases() {
+               $this->resetAliases();
+
+               foreach ( $this->autoloadClasses as $class => $file ) {
+                       $this->processClass( $class, $file );
+               }
+
+               $this->reportSoftConflicts();
+
+               foreach ( self::$existingAliases as $source => $dest ) {
+                       $this->aliases[$source] = $dest;
+               }
+               return $this->valid ? $this->aliases : false;
+       }
+
+       private function processClass( $class, $file ) {
+               $dir = dirname( $file );
+
+               if ( isset( self::$existingAliases[$class] ) ) {
+                       return;
+               }
+
+               if ( isset( self::$renamedClasses[$class] ) ) {
+                       $this->addAlias( $class, self::$renamedClasses[$class] 
);
+                       return;
+               }
+               
+               if ( isset( self::$namespacesByClass[$class] ) ) {
+                       $this->addAlias( $class, 
self::$namespacesByClass[$class] . '\\' . $class );
+                       return;
+               }
+
+               if ( isset( self::$deprefixesByDir[$dir] ) ) {
+                       list( $namespace, $prefix ) = 
self::$deprefixesByDir[$dir];
+                       if ( $this->startsWith( $class, $prefix ) ) {
+                               $this->addAlias( $class, $namespace . '\\' . 
substr( $class, strlen( $prefix ) ) );
+                               return;
+                       } else {
+                               $this->addAlias( $class, $namespace . '\\' . 
$class );
+                               return;
+                       }
+               }
+
+               if ( isset( self::$namespacesByDir[$dir] ) ) {
+                       $namespace = self::$namespacesByDir[$dir];
+                       $slashPos = strrpos( $class, '\\' );
+                       if ( $slashPos !== false ) {
+                               $priorNamespace = substr( $class, 0, $slashPos 
);
+                               $classPart = substr( $class, $slashPos + 1 );
+                               
+                               if ( $priorNamespace !== $namespace ) {
+                                       $this->addAlias( $class, $namespace . 
'\\' . $classPart );
+                                       return;
+                               } else {
+                                       return;
+                               }
+                       } else {
+                               $this->addAlias( $class, $namespace . '\\' . 
$class );
+                               return;
+                       }
+               }
+
+               foreach ( self::$ignoreDirs as $ignoreDir ) {
+                       if ( $this->startsWith( $dir, "$ignoreDir/" ) || $dir 
=== $ignoreDir ) {
+                               return;
+                       }
+               }
+
+               if ( $dir === '.' ) {
+                       // profileinfo.php
+                       return;
+               }
+
+               $this->error( "Don't know what to do with class $class in 
directory $dir" );
        }
 }
diff --git a/src/ReservedWords.php b/src/ReservedWords.php
new file mode 100644
index 0000000..4389ebb
--- /dev/null
+++ b/src/ReservedWords.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace MediaWiki\Tool\Namespaceizer;
+
+class ReservedWords {
+       private static $words = [
+               '__halt_compiler',
+               'abstract',
+               'and',
+               'array',
+               'as',
+               'bool',
+               'break',
+               'callable',
+               'case',
+               'catch',
+               'class',
+               'clone',
+               'const',
+               'continue',
+               'declare',
+               'default',
+               'die',
+               'do',
+               'echo',
+               'else',
+               'elseif',
+               'empty',
+               'enddeclare',
+               'endfor',
+               'endforeach',
+               'endif',
+               'endswitch',
+               'endwhile',
+               'eval',
+               'exit',
+               'extends',
+               'false',
+               'final',
+               'finally',
+               'float',
+               'for',
+               'foreach',
+               'function',
+               'global',
+               'goto',
+               'if',
+               'implements',
+               'include',
+               'include_once',
+               'instanceof',
+               'insteadof',
+               'int',
+               'interface',
+               'isset',
+               'iterable',
+               'list',
+               'mixed',
+               'namespace',
+               'new',
+               'null',
+               'numeric',
+               'object',
+               'or',
+               'print',
+               'private',
+               'protected',
+               'public',
+               'require',
+               'require_once',
+               'resource',
+               'return',
+               'static',
+               'string',
+               'switch',
+               'throw',
+               'trait',
+               'true',
+               'try',
+               'unset',
+               'use',
+               'var',
+               'void',
+               'while',
+               'xor',
+               'yield',
+       ];
+
+       public function isReserved( $word ) {
+               return in_array( strtolower( $word ), self::$words );
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/392364
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I91762c902b049fea472a189073acc87ab0ada85f
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/tools/namespaceizer
Gerrit-Branch: master
Gerrit-Owner: Tim Starling <[email protected]>
Gerrit-Reviewer: Anomie <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Tim Starling <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to