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