because of Phabricator reports.
* (T203908) Added language support for Eastern Pwo (kjp).
+* (T213717) Fixed a translation error on Goan Konkani (gom-deva) translations
+ for NS_TEMPLATE.
=== Breaking changes in 1.33 ===
* The parameteter $lang in DifferenceEngine::setTextLanguage must be of type
Title::getUserPermissionsErrors() and Title::userCan(). Previously, the method
was only called in Action::checkCanExecute(). Actions should ensure that their
requiresUnblock() returns the proper result (the default is `true`).
+* (T211608) The MediaWiki\Services namespace has been renamed to
+ Wikimedia\Services. The old name is still supported, but deprecated.
* …
=== Other changes in 1.33 ===
'MediaWiki\\OutputHandler' => __DIR__ . '/includes/OutputHandler.php',
'MediaWiki\\ProcOpenError' => __DIR__ . '/includes/exception/ProcOpenError.php',
'MediaWiki\\Search\\ParserOutputSearchDataExtractor' => __DIR__ . '/includes/search/ParserOutputSearchDataExtractor.php',
+ 'MediaWiki\\Services\\CannotReplaceActiveServiceException' => __DIR__ . '/includes/libs/services/CannotReplaceActiveServiceException.php',
+ 'MediaWiki\\Services\\ContainerDisabledException' => __DIR__ . '/includes/libs/services/ContainerDisabledException.php',
+ 'MediaWiki\\Services\\DestructibleService' => __DIR__ . '/includes/libs/services/DestructibleService.php',
+ 'MediaWiki\\Services\\NoSuchServiceException' => __DIR__ . '/includes/libs/services/NoSuchServiceException.php',
+ 'MediaWiki\\Services\\SalvageableService' => __DIR__ . '/includes/libs/services/SalvageableService.php',
+ 'MediaWiki\\Services\\ServiceAlreadyDefinedException' => __DIR__ . '/includes/libs/services/ServiceAlreadyDefinedException.php',
+ 'MediaWiki\\Services\\ServiceContainer' => __DIR__ . '/includes/libs/services/ServiceContainer.php',
+ 'MediaWiki\\Services\\ServiceDisabledException' => __DIR__ . '/includes/libs/services/ServiceDisabledException.php',
'MediaWiki\\ShellDisabledError' => __DIR__ . '/includes/exception/ShellDisabledError.php',
'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php',
'MediaWiki\\Special\\SpecialPageFactory' => __DIR__ . '/includes/specialpage/SpecialPageFactory.php',
'MediaWiki\\Linker\\' => __DIR__ . '/linker/',
'MediaWiki\\Preferences\\' => __DIR__ . '/preferences/',
'MediaWiki\\Revision\\' => __DIR__ . '/Revision/',
- 'MediaWiki\\Services\\' => __DIR__ . '/services/',
'MediaWiki\\Session\\' => __DIR__ . '/session/',
'MediaWiki\\Shell\\' => __DIR__ . '/shell/',
'MediaWiki\\Sparql\\' => __DIR__ . '/sparql/',
'MediaWiki\\Storage\\' => __DIR__ . '/Storage/',
'MediaWiki\\Tidy\\' => __DIR__ . '/tidy/',
+ 'Wikimedia\\Services\\' => __DIR__ . '/libs/services/',
];
}
}
* @since 1.22
*/
public static function getBlocksForIPList( array $ipChain, $isAnon, $fromMaster = false ) {
- if ( !count( $ipChain ) ) {
+ if ( $ipChain === [] ) {
return [];
}
$conds[] = self::getRangeCond( IP::toHex( $ipaddr ) );
}
- if ( !count( $conds ) ) {
+ if ( $conds === [] ) {
return [];
}
* @return Block|null The "best" block from the list
*/
public static function chooseBlock( array $blocks, array $ipChain ) {
- if ( !count( $blocks ) ) {
+ if ( $blocks === [] ) {
return null;
} elseif ( count( $blocks ) == 1 ) {
return $blocks[0];
// Already compressed
return;
}
- if ( !count( $this->mItems ) ) {
- // Empty
+ if ( $this->mItems === [] ) {
return;
}
$this->mDiffs = [];
$this->mDiffMap = [];
foreach ( $sequences as $seq ) {
- if ( !count( $seq['diffs'] ) ) {
+ if ( $seq['diffs'] === [] ) {
continue;
}
if ( $tail === '' ) {
*/
function __sleep() {
$this->compress();
- if ( !count( $this->mItems ) ) {
- // Empty object
+ if ( $this->mItems === [] ) {
$info = false;
} else {
// Take forward differences to improve the compression ratio for sequences
use MediaWiki\Config\ConfigRepository;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Linker\LinkRendererFactory;
-use MediaWiki\Services\SalvageableService;
-use MediaWiki\Services\ServiceContainer;
-use MediaWiki\Services\NoSuchServiceException;
use MWException;
use MimeAnalyzer;
use ObjectCache;
use TitleFormatter;
use TitleParser;
use VirtualRESTServiceClient;
+use Wikimedia\Services\SalvageableService;
+use Wikimedia\Services\ServiceContainer;
+use Wikimedia\Services\NoSuchServiceException;
use MediaWiki\Interwiki\InterwikiLookup;
use MagicWordFactory;
$user = $this->mRecord->getUser( $audience, $user );
return $user ? $user->getName() : '';
}
+
/**
* Fetch revision comment if it's available to the specified audience.
* If the specified audience does not have access to the comment, an
// Note that we allow 1 link higher than the max for things like 56 days but a 60 day link.
sort( $wgRCLinkDays );
- // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall
- for ( $i = 0; $i < count( $wgRCLinkDays ); $i++ ) {
- if ( $wgRCLinkDays[$i] >= $rcMaxAgeDays ) {
- $wgRCLinkDays = array_slice( $wgRCLinkDays, 0, $i + 1, false );
+ foreach ( $wgRCLinkDays as $i => $days ) {
+ if ( $days >= $rcMaxAgeDays ) {
+ array_splice( $wgRCLinkDays, $i + 1 );
break;
}
}
$session->renew();
if ( MediaWiki\Session\PHPSessionHandler::isEnabled() &&
- ( $session->isPersistent() || $session->shouldRememberUser() )
+ ( $session->isPersistent() || $session->shouldRememberUser() ) &&
+ session_id() !== $session->getId()
) {
// Start the PHP-session for backwards compatibility
+ if ( session_id() !== '' ) {
+ wfDebugLog( 'session', 'PHP session {old_id} was already started, changing to {new_id}', 'all', [
+ 'old_id' => session_id(),
+ 'new_id' => $session->getId(),
+ ] );
+ session_write_close();
+ }
session_id( $session->getId() );
- Wikimedia\quietCall( 'session_start' );
+ session_start();
}
unset( $session );
}
// no group with the given right(s) exists, no need for a query
- if ( !count( $groups ) ) {
+ if ( $groups === [] ) {
$this->getResult()->addIndexedTagName( [ 'query', $this->getModuleName() ], '' );
return;
if ( count( $ids ) ) {
$ids = $this->filterIDs( [ [ $table, $field ] ], $ids );
- if ( !count( $ids ) ) {
+ if ( $ids === [] ) {
// Return nothing, no IDs are valid
$this->where[] = '0 = 1';
} else {
return $v >= $cont_page;
} );
}
- if ( !count( $pages ) ) {
+ if ( $pages === [] ) {
// Nothing to do
return;
}
$getTitles[] = $t->getTalkPage();
}
}
- if ( !count( $getTitles ) ) {
+ if ( $getTitles === [] ) {
return;
}
$pageIds = array_keys( $this->titles );
- if ( !count( $pageIds ) ) {
+ if ( $pageIds === [] ) {
return;
}
}
private function getVariantTitles() {
- if ( !count( $this->titles ) ) {
+ if ( $this->titles === [] ) {
return;
}
$this->variantTitles = [];
$pages = $filteredPages;
}
- if ( !count( $pages ) ) {
+ if ( $pages === [] ) {
# Nothing to do
return;
}
// prepareQuery might try to sort by actor and confuse everything.
$batchSize = 1;
} elseif ( isset( $this->params['userids'] ) ) {
- if ( !count( $this->params['userids'] ) ) {
+ if ( $this->params['userids'] === [] ) {
$encParamName = $this->encodeParamName( 'userids' );
$this->dieWithError( [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName" );
}
$writeSockets["$clientIndex/$i"] = $socket;
}
}
- if ( !count( $readSockets ) && !count( $writeSockets ) ) {
+ if ( $readSockets === [] && $writeSockets === [] ) {
break;
}
*
* @file
*/
-use MediaWiki\Services\SalvageableService;
use Wikimedia\Assert\Assert;
+use Wikimedia\Services\SalvageableService;
/**
* Factory class to create Config objects
namespace MediaWiki\Config;
-use MediaWiki\Services\SalvageableService;
use Wikimedia\Assert\Assert;
+use Wikimedia\Services\SalvageableService;
/**
* Object which holds currently registered configuration options.
public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
$fname = __METHOD__
) {
- if ( !count( $rows ) ) {
+ if ( $rows === [] ) {
return true; // nothing to do
}
public function setLanguage( Language $language ) {
$this->language = $language;
}
+
/**
* @param int $cutoff
* @see $wgWikiDiff2MovedParagraphDetectionCutoff
* @return mixed[]
*/
public function mergeClassesIntoAttributes( array $classes, array $attribs ) {
- if ( !count( $classes ) ) {
+ if ( $classes === [] ) {
return $attribs;
}
'handle this content model.' );
$this->modelId = $modelId;
}
+
/** @return string */
public function getModelId() {
return $this->modelId;
* @return array[] An Array of arrays or iterators of file objects and the hash as key
*/
function findBySha1s( array $hashes ) {
- if ( !count( $hashes ) ) {
+ if ( $hashes === [] ) {
return []; // empty parameter
}
$conds['fa_sha1'] = $this->sha1;
}
- if ( !count( $conds ) ) {
+ if ( $conds === [] ) {
throw new MWException( "No specific information for retrieving archived file" );
}
'enableSectionEditLinks' => false,
'unwrap' => true,
] );
- } catch ( MediaWiki\Services\ServiceDisabledException $e ) {
+ } catch ( Wikimedia\Services\ServiceDisabledException $e ) {
$html = '<!--DB access attempted during parse--> ' . htmlspecialchars( $text );
}
final public function batchPush( array $jobs, $flags = 0 ) {
$this->assertNotReadOnly();
- if ( !count( $jobs ) ) {
+ if ( $jobs === [] ) {
return; // nothing to do
}
* @return void
*/
public function doBatchPushInternal( IDatabase $dbw, array $jobs, $flags, $method ) {
- if ( !count( $jobs ) ) {
+ if ( $jobs === [] ) {
return;
}
}
$jobs = is_array( $jobs ) ? $jobs : [ $jobs ];
- if ( !count( $jobs ) ) {
+ if ( $jobs === [] ) {
return;
}
}
}
- if ( !count( $items ) ) {
+ if ( $items === [] ) {
return; // nothing to do
}
if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
}
- if ( !count( $ops ) ) {
+ if ( $ops === [] ) {
return $this->newStatus(); // nothing to do
}
* @see FileBackend::doOperations()
* @param array $ops
* @param array $opts
+ * @return StatusValue
*/
abstract protected function doOperationsInternal( array $ops, array $opts );
if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
}
- if ( !count( $ops ) ) {
+ if ( $ops === [] ) {
return $this->newStatus(); // nothing to do
}
/**
* @see FileBackend::doQuickOperations()
* @param array $ops
+ * @return StatusValue
* @since 1.20
*/
abstract protected function doQuickOperationsInternal( array $ops );
/**
* @see FileBackend::prepare()
* @param array $params
+ * @return StatusValue
*/
abstract protected function doPrepare( array $params );
/**
* @see FileBackend::secure()
* @param array $params
+ * @return StatusValue
*/
abstract protected function doSecure( array $params );
/**
* @see FileBackend::publish()
* @param array $params
+ * @return StatusValue
*/
abstract protected function doPublish( array $params );
/**
* @see FileBackend::clean()
* @param array $params
+ * @return StatusValue
*/
abstract protected function doClean( array $params );
* @return StatusValue
*/
final public function logChangeBatch( array $entries, $batchId ) {
- if ( !count( $entries ) ) {
+ if ( $entries === [] ) {
return StatusValue::newGood();
}
if ( $this->locksHeld[$path][$type] <= 0 ) {
unset( $this->locksHeld[$path][$type] );
}
- if ( !count( $this->locksHeld[$path] ) ) {
+ if ( $this->locksHeld[$path] === [] ) {
unset( $this->locksHeld[$path] ); // no locks on this path
if ( isset( $this->handles[$path] ) ) {
$handlesToClose[] = $this->handles[$path];
protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
$status = StatusValue::newGood();
- if ( !count( $paths ) ) {
+ if ( $paths === [] ) {
return $status; // nothing to lock
}
$bucket = $this->getBucketFromPath( $path );
$pathsToUnlock[$bucket][$type][] = $path;
}
- if ( !count( $this->locksHeld[$path] ) ) {
+ if ( $this->locksHeld[$path] === [] ) {
unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
}
}
foreach ( $pathsToUnlock as $bucket => $pathsToUnlockByType ) {
$status->merge( $this->doUnlockingRequestBucket( $bucket, $pathsToUnlockByType ) );
}
- if ( !count( $this->locksHeld ) ) {
+ if ( $this->locksHeld === [] ) {
$status->merge( $this->releaseAllLocks() );
$this->degradedBuckets = []; // safe to retry the normal quorum
}
$this->filterMatchType = $callbackReturn;
}
}
+
/**
* Handle coming across a <!DOCTYPE declaration.
*
);
}
- if ( !count( $this->shutdownPositions ) ) {
+ if ( $this->shutdownPositions === [] ) {
return []; // nothing to save
}
public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
$fname = __METHOD__
) {
- if ( !count( $rows ) ) {
+ if ( $rows === [] ) {
return true; // nothing to do
}
public function upsert( $table, array $rows, array $uniqueIndexes,
array $set, $fname = __METHOD__
) {
- if ( !count( $rows ) ) {
+ if ( $rows === [] ) {
return true; // nothing to do
}
* @return array (reader index, lagged replica mode) or false on failure
*/
private function pickReaderIndex( array $loads, $domain = false ) {
- if ( !count( $loads ) ) {
+ if ( $loads === [] ) {
throw new InvalidArgumentException( "Empty server array given to LoadBalancer" );
}
}
// If all servers were down, quit now
- if ( !count( $currentLoads ) ) {
+ if ( $currentLoads === [] ) {
$this->connLogger->error( __METHOD__ . ": all servers down" );
}
--- /dev/null
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when trying to replace an already active service.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when trying to replace an already active service.
+ */
+class CannotReplaceActiveServiceException extends RuntimeException {
+
+ /**
+ * @param string $serviceName
+ * @param Exception|null $previous
+ */
+ public function __construct( $serviceName, Exception $previous = null ) {
+ parent::__construct( "Cannot replace an active service: $serviceName", 0, $previous );
+ }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( CannotReplaceActiveServiceException::class,
+ 'MediaWiki\Services\CannotReplaceActiveServiceException' );
--- /dev/null
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when trying to access a service on a disabled container or factory.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when trying to access a service on a disabled container or factory.
+ */
+class ContainerDisabledException extends RuntimeException {
+
+ /**
+ * @param Exception|null $previous
+ */
+ public function __construct( Exception $previous = null ) {
+ parent::__construct( 'Container disabled!', 0, $previous );
+ }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( ContainerDisabledException::class, 'MediaWiki\Services\ContainerDisabledException' );
--- /dev/null
+<?php
+namespace Wikimedia\Services;
+
+/**
+ * Interface for destructible services.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.27
+ */
+
+/**
+ * DestructibleService defines a standard interface for shutting down a service instance.
+ * The intended use is for a service container to be able to shut down services that should
+ * no longer be used, and allow such services to release any system resources.
+ *
+ * @note There is no expectation that services will be destroyed when the process (or web request)
+ * terminates.
+ */
+interface DestructibleService {
+
+ /**
+ * Notifies the service object that it should expect to no longer be used, and should release
+ * any system resources it may own. The behavior of all service methods becomes undefined after
+ * destroy() has been called. It is recommended that implementing classes should throw an
+ * exception when service methods are accessed after destroy() has been called.
+ */
+ public function destroy();
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( DestructibleService::class, 'MediaWiki\Services\DestructibleService' );
--- /dev/null
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when the requested service is not known.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when the requested service is not known.
+ */
+class NoSuchServiceException extends RuntimeException {
+
+ /**
+ * @param string $serviceName
+ * @param Exception|null $previous
+ */
+ public function __construct( $serviceName, Exception $previous = null ) {
+ parent::__construct( "No such service: $serviceName", 0, $previous );
+ }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( NoSuchServiceException::class, 'MediaWiki\Services\NoSuchServiceException' );
--- /dev/null
+<?php
+namespace Wikimedia\Services;
+
+/**
+ * Interface for salvageable services.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.28
+ */
+
+/**
+ * SalvageableService defines an interface for services that are able to salvage state from a
+ * previous instance of the same class. The intent is to allow new service instances to re-use
+ * resources that would be expensive to re-create, such as cached data or network connections.
+ *
+ * @note There is no expectation that services will be destroyed when the process (or web request)
+ * terminates.
+ */
+interface SalvageableService {
+
+ /**
+ * Re-uses state from $other. $other must not be used after being passed to salvage(),
+ * and should be considered to be destroyed.
+ *
+ * @note Implementations are responsible for determining what parts of $other can be re-used
+ * safely. In particular, implementations should check that the relevant configuration of
+ * $other is the same as in $this before re-using resources from $other.
+ *
+ * @note Implementations must take care to detach any re-used resources from the original
+ * service instance. If $other is destroyed later, resources that are now used by the
+ * new service instance must not be affected.
+ *
+ * @note If $other is a DestructibleService, implementations should make sure that $other
+ * is in destroyed state after salvage finished. This may be done by calling $other->destroy()
+ * after carefully detaching all relevant resources.
+ *
+ * @param SalvageableService $other The object to salvage state from. $other must have the
+ * exact same type as $this.
+ */
+ public function salvage( SalvageableService $other );
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( SalvageableService::class, 'MediaWiki\Services\SalvageableService' );
--- /dev/null
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when a service was already defined, but the
+ * caller expected it to not exist.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when a service was already defined, but the
+ * caller expected it to not exist.
+ */
+class ServiceAlreadyDefinedException extends RuntimeException {
+
+ /**
+ * @param string $serviceName
+ * @param Exception|null $previous
+ */
+ public function __construct( $serviceName, Exception $previous = null ) {
+ parent::__construct( "Service already defined: $serviceName", 0, $previous );
+ }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( ServiceAlreadyDefinedException::class,
+ 'MediaWiki\Services\ServiceAlreadyDefinedException' );
--- /dev/null
+<?php
+namespace Wikimedia\Services;
+
+use InvalidArgumentException;
+use RuntimeException;
+use Wikimedia\Assert\Assert;
+
+/**
+ * Generic service container.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.27
+ */
+
+/**
+ * ServiceContainer provides a generic service to manage named services using
+ * lazy instantiation based on instantiator callback functions.
+ *
+ * Services managed by an instance of ServiceContainer may or may not implement
+ * a common interface.
+ *
+ * @note When using ServiceContainer to manage a set of services, consider
+ * creating a wrapper or a subclass that provides access to the services via
+ * getter methods with more meaningful names and more specific return type
+ * declarations.
+ *
+ * @see docs/injection.txt for an overview of using dependency injection in the
+ * MediaWiki code base.
+ */
+class ServiceContainer implements DestructibleService {
+
+ /**
+ * @var object[]
+ */
+ private $services = [];
+
+ /**
+ * @var callable[]
+ */
+ private $serviceInstantiators = [];
+
+ /**
+ * @var callable[][]
+ */
+ private $serviceManipulators = [];
+
+ /**
+ * @var bool[] disabled status, per service name
+ */
+ private $disabled = [];
+
+ /**
+ * @var array
+ */
+ private $extraInstantiationParams;
+
+ /**
+ * @var bool
+ */
+ private $destroyed = false;
+
+ /**
+ * @param array $extraInstantiationParams Any additional parameters to be passed to the
+ * instantiator function when creating a service. This is typically used to provide
+ * access to additional ServiceContainers or Config objects.
+ */
+ public function __construct( array $extraInstantiationParams = [] ) {
+ $this->extraInstantiationParams = $extraInstantiationParams;
+ }
+
+ /**
+ * Destroys all contained service instances that implement the DestructibleService
+ * interface. This will render all services obtained from this ServiceContainer
+ * instance unusable. In particular, this will disable access to the storage backend
+ * via any of these services. Any future call to getService() will throw an exception.
+ *
+ * @see resetGlobalInstance()
+ */
+ public function destroy() {
+ foreach ( $this->getServiceNames() as $name ) {
+ $service = $this->peekService( $name );
+ if ( $service !== null && $service instanceof DestructibleService ) {
+ $service->destroy();
+ }
+ }
+
+ // Break circular references due to the $this reference in closures, by
+ // erasing the instantiator array. This allows the ServiceContainer to
+ // be deleted when it goes out of scope.
+ $this->serviceInstantiators = [];
+ // Also remove the services themselves, to avoid confusion.
+ $this->services = [];
+ $this->destroyed = true;
+ }
+
+ /**
+ * @param array $wiringFiles A list of PHP files to load wiring information from.
+ * Each file is loaded using PHP's include mechanism. Each file is expected to
+ * return an associative array that maps service names to instantiator functions.
+ */
+ public function loadWiringFiles( array $wiringFiles ) {
+ foreach ( $wiringFiles as $file ) {
+ // the wiring file is required to return an array of instantiators.
+ $wiring = require $file;
+
+ Assert::postcondition(
+ is_array( $wiring ),
+ "Wiring file $file is expected to return an array!"
+ );
+
+ $this->applyWiring( $wiring );
+ }
+ }
+
+ /**
+ * Registers multiple services (aka a "wiring").
+ *
+ * @param array $serviceInstantiators An associative array mapping service names to
+ * instantiator functions.
+ */
+ public function applyWiring( array $serviceInstantiators ) {
+ Assert::parameterElementType( 'callable', $serviceInstantiators, '$serviceInstantiators' );
+
+ foreach ( $serviceInstantiators as $name => $instantiator ) {
+ $this->defineService( $name, $instantiator );
+ }
+ }
+
+ /**
+ * Imports all wiring defined in $container. Wiring defined in $container
+ * will override any wiring already defined locally. However, already
+ * existing service instances will be preserved.
+ *
+ * @since 1.28
+ *
+ * @param ServiceContainer $container
+ * @param string[] $skip A list of service names to skip during import
+ */
+ public function importWiring( ServiceContainer $container, $skip = [] ) {
+ $newInstantiators = array_diff_key(
+ $container->serviceInstantiators,
+ array_flip( $skip )
+ );
+
+ $this->serviceInstantiators = array_merge(
+ $this->serviceInstantiators,
+ $newInstantiators
+ );
+
+ $newManipulators = array_diff(
+ array_keys( $container->serviceManipulators ),
+ $skip
+ );
+
+ foreach ( $newManipulators as $name ) {
+ if ( isset( $this->serviceManipulators[$name] ) ) {
+ $this->serviceManipulators[$name] = array_merge(
+ $this->serviceManipulators[$name],
+ $container->serviceManipulators[$name]
+ );
+ } else {
+ $this->serviceManipulators[$name] = $container->serviceManipulators[$name];
+ }
+ }
+ }
+
+ /**
+ * Returns true if a service is defined for $name, that is, if a call to getService( $name )
+ * would return a service instance.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasService( $name ) {
+ return isset( $this->serviceInstantiators[$name] );
+ }
+
+ /**
+ * Returns the service instance for $name only if that service has already been instantiated.
+ * This is intended for situations where services get destroyed/cleaned up, so we can
+ * avoid creating a service just to destroy it again.
+ *
+ * @note This is intended for internal use and for test fixtures.
+ * Application logic should use getService() instead.
+ *
+ * @see getService().
+ *
+ * @param string $name
+ *
+ * @return object|null The service instance, or null if the service has not yet been instantiated.
+ * @throws RuntimeException if $name does not refer to a known service.
+ */
+ public function peekService( $name ) {
+ if ( !$this->hasService( $name ) ) {
+ throw new NoSuchServiceException( $name );
+ }
+
+ return $this->services[$name] ?? null;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getServiceNames() {
+ return array_keys( $this->serviceInstantiators );
+ }
+
+ /**
+ * Define a new service. The service must not be known already.
+ *
+ * @see getService().
+ * @see redefineService().
+ *
+ * @param string $name The name of the service to register, for use with getService().
+ * @param callable $instantiator Callback that returns a service instance.
+ * Will be called with this ServiceContainer instance as the only parameter.
+ * Any extra instantiation parameters provided to the constructor will be
+ * passed as subsequent parameters when invoking the instantiator.
+ *
+ * @throws RuntimeException if there is already a service registered as $name.
+ */
+ public function defineService( $name, callable $instantiator ) {
+ Assert::parameterType( 'string', $name, '$name' );
+
+ if ( $this->hasService( $name ) ) {
+ throw new ServiceAlreadyDefinedException( $name );
+ }
+
+ $this->serviceInstantiators[$name] = $instantiator;
+ }
+
+ /**
+ * Replace an already defined service.
+ *
+ * @see defineService().
+ *
+ * @note This will fail if the service was already instantiated. If the service was previously
+ * disabled, it will be re-enabled by this call. Any manipulators registered for the service
+ * will remain in place.
+ *
+ * @param string $name The name of the service to register.
+ * @param callable $instantiator Callback function that returns a service instance.
+ * Will be called with this ServiceContainer instance as the only parameter.
+ * The instantiator must return a service compatible with the originally defined service.
+ * Any extra instantiation parameters provided to the constructor will be
+ * passed as subsequent parameters when invoking the instantiator.
+ *
+ * @throws NoSuchServiceException if $name is not a known service.
+ * @throws CannotReplaceActiveServiceException if the service was already instantiated.
+ */
+ public function redefineService( $name, callable $instantiator ) {
+ Assert::parameterType( 'string', $name, '$name' );
+
+ if ( !$this->hasService( $name ) ) {
+ throw new NoSuchServiceException( $name );
+ }
+
+ if ( isset( $this->services[$name] ) ) {
+ throw new CannotReplaceActiveServiceException( $name );
+ }
+
+ $this->serviceInstantiators[$name] = $instantiator;
+ unset( $this->disabled[$name] );
+ }
+
+ /**
+ * Add a service manipulator callback for the given service.
+ * This method may be used by extensions that need to wrap, replace, or re-configure a
+ * service. It would typically be called from a MediaWikiServices hook handler.
+ *
+ * The manipulator callback is called just after the service is instantiated.
+ * It can call methods on the service to change configuration, or wrap or otherwise
+ * replace it.
+ *
+ * @see defineService().
+ * @see redefineService().
+ *
+ * @note This will fail if the service was already instantiated.
+ *
+ * @since 1.32
+ *
+ * @param string $name The name of the service to manipulate.
+ * @param callable $manipulator Callback function that manipulates, wraps or replaces a
+ * service instance. The callback receives the new service instance and this
+ * ServiceContainer as parameters, as well as any extra instantiation parameters specified
+ * when constructing this ServiceContainer. If the callback returns a value, that
+ * value replaces the original service instance.
+ *
+ * @throws NoSuchServiceException if $name is not a known service.
+ * @throws CannotReplaceActiveServiceException if the service was already instantiated.
+ */
+ public function addServiceManipulator( $name, callable $manipulator ) {
+ Assert::parameterType( 'string', $name, '$name' );
+
+ if ( !$this->hasService( $name ) ) {
+ throw new NoSuchServiceException( $name );
+ }
+
+ if ( isset( $this->services[$name] ) ) {
+ throw new CannotReplaceActiveServiceException( $name );
+ }
+
+ $this->serviceManipulators[$name][] = $manipulator;
+ }
+
+ /**
+ * Disables a service.
+ *
+ * @note Attempts to call getService() for a disabled service will result
+ * in a DisabledServiceException. Calling peekService for a disabled service will
+ * return null. Disabled services are listed by getServiceNames(). A disabled service
+ * can be enabled again using redefineService().
+ *
+ * @note If the service was already active (that is, instantiated) when getting disabled,
+ * and the service instance implements DestructibleService, destroy() is called on the
+ * service instance.
+ *
+ * @see redefineService()
+ * @see resetService()
+ *
+ * @param string $name The name of the service to disable.
+ *
+ * @throws RuntimeException if $name is not a known service.
+ */
+ public function disableService( $name ) {
+ $this->resetService( $name );
+
+ $this->disabled[$name] = true;
+ }
+
+ /**
+ * Resets a service by dropping the service instance.
+ * If the service instances implements DestructibleService, destroy()
+ * is called on the service instance.
+ *
+ * @warning This is generally unsafe! Other services may still retain references
+ * to the stale service instance, leading to failures and inconsistencies. Subclasses
+ * may use this method to reset specific services under specific instances, but
+ * it should not be exposed to application logic.
+ *
+ * @note This is declared final so subclasses can not interfere with the expectations
+ * disableService() has when calling resetService().
+ *
+ * @see redefineService()
+ * @see disableService().
+ *
+ * @param string $name The name of the service to reset.
+ * @param bool $destroy Whether the service instance should be destroyed if it exists.
+ * When set to false, any existing service instance will effectively be detached
+ * from the container.
+ *
+ * @throws RuntimeException if $name is not a known service.
+ */
+ final protected function resetService( $name, $destroy = true ) {
+ Assert::parameterType( 'string', $name, '$name' );
+
+ $instance = $this->peekService( $name );
+
+ if ( $destroy && $instance instanceof DestructibleService ) {
+ $instance->destroy();
+ }
+
+ unset( $this->services[$name] );
+ unset( $this->disabled[$name] );
+ }
+
+ /**
+ * Returns a service object of the kind associated with $name.
+ * Services instances are instantiated lazily, on demand.
+ * This method may or may not return the same service instance
+ * when called multiple times with the same $name.
+ *
+ * @note Rather than calling this method directly, it is recommended to provide
+ * getters with more meaningful names and more specific return types, using
+ * a subclass or wrapper.
+ *
+ * @see redefineService().
+ *
+ * @param string $name The service name
+ *
+ * @throws NoSuchServiceException if $name is not a known service.
+ * @throws ContainerDisabledException if this container has already been destroyed.
+ * @throws ServiceDisabledException if the requested service has been disabled.
+ *
+ * @return object The service instance
+ */
+ public function getService( $name ) {
+ if ( $this->destroyed ) {
+ throw new ContainerDisabledException();
+ }
+
+ if ( isset( $this->disabled[$name] ) ) {
+ throw new ServiceDisabledException( $name );
+ }
+
+ if ( !isset( $this->services[$name] ) ) {
+ $this->services[$name] = $this->createService( $name );
+ }
+
+ return $this->services[$name];
+ }
+
+ /**
+ * @param string $name
+ *
+ * @throws InvalidArgumentException if $name is not a known service.
+ * @return object
+ */
+ private function createService( $name ) {
+ if ( isset( $this->serviceInstantiators[$name] ) ) {
+ $service = ( $this->serviceInstantiators[$name] )(
+ $this,
+ ...$this->extraInstantiationParams
+ );
+
+ if ( isset( $this->serviceManipulators[$name] ) ) {
+ foreach ( $this->serviceManipulators[$name] as $callback ) {
+ $ret = call_user_func_array(
+ $callback,
+ array_merge( [ $service, $this ], $this->extraInstantiationParams )
+ );
+
+ // If the manipulator callback returns an object, that object replaces
+ // the original service instance. This allows the manipulator to wrap
+ // or fully replace the service.
+ if ( $ret !== null ) {
+ $service = $ret;
+ }
+ }
+ }
+
+ // NOTE: when adding more wiring logic here, make sure importWiring() is kept in sync!
+ } else {
+ throw new NoSuchServiceException( $name );
+ }
+
+ return $service;
+ }
+
+ /**
+ * @param string $name
+ * @return bool Whether the service is disabled
+ * @since 1.28
+ */
+ public function isServiceDisabled( $name ) {
+ return isset( $this->disabled[$name] );
+ }
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( ServiceContainer::class, 'MediaWiki\Services\ServiceContainer' );
--- /dev/null
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when trying to access a disabled service.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when trying to access a disabled service.
+ */
+class ServiceDisabledException extends RuntimeException {
+
+ /**
+ * @param string $serviceName
+ * @param Exception|null $previous
+ */
+ public function __construct( $serviceName, Exception $previous = null ) {
+ parent::__construct( "Service disabled: $serviceName", 0, $previous );
+ }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( ServiceDisabledException::class, 'MediaWiki\Services\ServiceDisabledException' );
// If nobody is watching the page, and there are no users notified on all changes
// don't bother creating a job/trying to send emails, unless it's a
// talk page with an applicable notification.
- if ( !count( $watchers ) && !count( $wgUsersNotifiedOnAllChanges ) ) {
+ if ( $watchers === [] && !count( $wgUsersNotifiedOnAllChanges ) ) {
$sendEmail = false;
// Only send notification for non minor edits, unless $wgEnotifMinorEdits
if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) {
public function getMagicPMIDLinks() {
return $this->getOption( 'magicPMIDLinks' );
}
+
/**
* Are magic RFC links enabled?
* @since 1.28
}
public function pop() {
- if ( !count( $this->stack ) ) {
+ if ( $this->stack === [] ) {
throw new MWException( __METHOD__ . ': no elements remaining' );
}
$temp = array_pop( $this->stack );
* @return array
*/
public function getFlags() {
- if ( !count( $this->stack ) ) {
+ if ( $this->stack === [] ) {
return [
'findEquals' => false,
'findPipe' => false,
}
// Save response to file cache unless there are errors
- if ( isset( $fileCache ) && !$this->errors && !count( $missing ) ) {
+ if ( isset( $fileCache ) && !$this->errors && $missing === [] ) {
// Cache single modules and images...and other requests if there are enough hits
if ( ResourceFileCache::useFileCache( $context ) ) {
if ( $fileCache->isCacheWorthy() ) {
$out = '';
$states = [];
- if ( !count( $modules ) && !count( $missing ) ) {
+ if ( $modules === [] && $missing === [] ) {
return <<<MESSAGE
/* This file is the Web entry point for MediaWiki's ResourceLoader:
<https://www.mediawiki.org/wiki/ResourceLoader>. In this request,
$dom = new DOMDocument;
$dom->loadXML( file_get_contents( $this->getPath( $context ) ) );
$root = $dom->documentElement;
- $wrapper = $dom->createElement( 'g' );
+ $titleNode = null;
+ $wrapper = $dom->createElementNS( 'http://www.w3.org/2000/svg', 'g' );
+ // Reattach all direct children of the `<svg>` root node to the `<g>` wrapper
while ( $root->firstChild ) {
- $wrapper->appendChild( $root->firstChild );
+ $node = $root->firstChild;
+ if ( !$titleNode && $node->nodeType === XML_ELEMENT_NODE && $node->tagName === 'title' ) {
+ // Remember the first encountered `<title>` node
+ $titleNode = $node;
+ }
+ $wrapper->appendChild( $node );
+ }
+ if ( $titleNode ) {
+ // Reattach the `<title>` node to the `<svg>` root node rather than the `<g>` wrapper
+ $root->appendChild( $titleNode );
}
$root->appendChild( $wrapper );
$wrapper->setAttribute( 'fill', $variantConf['color'] );
if ( is_null( $this->namespaces ) ) {
return '';
}
- if ( !count( $this->namespaces ) ) {
+ if ( $this->namespaces === [] ) {
$namespaces = '0';
} else {
$namespaces = $this->db->makeList( $this->namespaces );
if ( is_null( $this->namespaces ) ) {
return ''; # search all
}
- if ( !count( $this->namespaces ) ) {
+ if ( $this->namespaces === [] ) {
$namespaces = '0';
} else {
$namespaces = $this->db->makeList( $this->namespaces );
+++ /dev/null
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when trying to replace an already active service.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when trying to replace an already active service.
- */
-class CannotReplaceActiveServiceException extends RuntimeException {
-
- /**
- * @param string $serviceName
- * @param Exception|null $previous
- */
- public function __construct( $serviceName, Exception $previous = null ) {
- parent::__construct( "Cannot replace an active service: $serviceName", 0, $previous );
- }
-
-}
+++ /dev/null
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when trying to access a service on a disabled container or factory.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when trying to access a service on a disabled container or factory.
- */
-class ContainerDisabledException extends RuntimeException {
-
- /**
- * @param Exception|null $previous
- */
- public function __construct( Exception $previous = null ) {
- parent::__construct( 'Container disabled!', 0, $previous );
- }
-
-}
+++ /dev/null
-<?php
-namespace MediaWiki\Services;
-
-/**
- * Interface for destructible services.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @since 1.27
- */
-
-/**
- * DestructibleService defines a standard interface for shutting down a service instance.
- * The intended use is for a service container to be able to shut down services that should
- * no longer be used, and allow such services to release any system resources.
- *
- * @note There is no expectation that services will be destroyed when the process (or web request)
- * terminates.
- */
-interface DestructibleService {
-
- /**
- * Notifies the service object that it should expect to no longer be used, and should release
- * any system resources it may own. The behavior of all service methods becomes undefined after
- * destroy() has been called. It is recommended that implementing classes should throw an
- * exception when service methods are accessed after destroy() has been called.
- */
- public function destroy();
-
-}
+++ /dev/null
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when the requested service is not known.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when the requested service is not known.
- */
-class NoSuchServiceException extends RuntimeException {
-
- /**
- * @param string $serviceName
- * @param Exception|null $previous
- */
- public function __construct( $serviceName, Exception $previous = null ) {
- parent::__construct( "No such service: $serviceName", 0, $previous );
- }
-
-}
+++ /dev/null
-<?php
-namespace MediaWiki\Services;
-
-/**
- * Interface for salvageable services.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @since 1.28
- */
-
-/**
- * SalvageableService defines an interface for services that are able to salvage state from a
- * previous instance of the same class. The intent is to allow new service instances to re-use
- * resources that would be expensive to re-create, such as cached data or network connections.
- *
- * @note There is no expectation that services will be destroyed when the process (or web request)
- * terminates.
- */
-interface SalvageableService {
-
- /**
- * Re-uses state from $other. $other must not be used after being passed to salvage(),
- * and should be considered to be destroyed.
- *
- * @note Implementations are responsible for determining what parts of $other can be re-used
- * safely. In particular, implementations should check that the relevant configuration of
- * $other is the same as in $this before re-using resources from $other.
- *
- * @note Implementations must take care to detach any re-used resources from the original
- * service instance. If $other is destroyed later, resources that are now used by the
- * new service instance must not be affected.
- *
- * @note If $other is a DestructibleService, implementations should make sure that $other
- * is in destroyed state after salvage finished. This may be done by calling $other->destroy()
- * after carefully detaching all relevant resources.
- *
- * @param SalvageableService $other The object to salvage state from. $other must have the
- * exact same type as $this.
- */
- public function salvage( SalvageableService $other );
-
-}
+++ /dev/null
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when a service was already defined, but the
- * caller expected it to not exist.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when a service was already defined, but the
- * caller expected it to not exist.
- */
-class ServiceAlreadyDefinedException extends RuntimeException {
-
- /**
- * @param string $serviceName
- * @param Exception|null $previous
- */
- public function __construct( $serviceName, Exception $previous = null ) {
- parent::__construct( "Service already defined: $serviceName", 0, $previous );
- }
-
-}
+++ /dev/null
-<?php
-namespace MediaWiki\Services;
-
-use InvalidArgumentException;
-use RuntimeException;
-use Wikimedia\Assert\Assert;
-
-/**
- * Generic service container.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @since 1.27
- */
-
-/**
- * ServiceContainer provides a generic service to manage named services using
- * lazy instantiation based on instantiator callback functions.
- *
- * Services managed by an instance of ServiceContainer may or may not implement
- * a common interface.
- *
- * @note When using ServiceContainer to manage a set of services, consider
- * creating a wrapper or a subclass that provides access to the services via
- * getter methods with more meaningful names and more specific return type
- * declarations.
- *
- * @see docs/injection.txt for an overview of using dependency injection in the
- * MediaWiki code base.
- */
-class ServiceContainer implements DestructibleService {
-
- /**
- * @var object[]
- */
- private $services = [];
-
- /**
- * @var callable[]
- */
- private $serviceInstantiators = [];
-
- /**
- * @var callable[][]
- */
- private $serviceManipulators = [];
-
- /**
- * @var bool[] disabled status, per service name
- */
- private $disabled = [];
-
- /**
- * @var array
- */
- private $extraInstantiationParams;
-
- /**
- * @var bool
- */
- private $destroyed = false;
-
- /**
- * @param array $extraInstantiationParams Any additional parameters to be passed to the
- * instantiator function when creating a service. This is typically used to provide
- * access to additional ServiceContainers or Config objects.
- */
- public function __construct( array $extraInstantiationParams = [] ) {
- $this->extraInstantiationParams = $extraInstantiationParams;
- }
-
- /**
- * Destroys all contained service instances that implement the DestructibleService
- * interface. This will render all services obtained from this MediaWikiServices
- * instance unusable. In particular, this will disable access to the storage backend
- * via any of these services. Any future call to getService() will throw an exception.
- *
- * @see resetGlobalInstance()
- */
- public function destroy() {
- foreach ( $this->getServiceNames() as $name ) {
- $service = $this->peekService( $name );
- if ( $service !== null && $service instanceof DestructibleService ) {
- $service->destroy();
- }
- }
-
- // Break circular references due to the $this reference in closures, by
- // erasing the instantiator array. This allows the ServiceContainer to
- // be deleted when it goes out of scope.
- $this->serviceInstantiators = [];
- // Also remove the services themselves, to avoid confusion.
- $this->services = [];
- $this->destroyed = true;
- }
-
- /**
- * @param array $wiringFiles A list of PHP files to load wiring information from.
- * Each file is loaded using PHP's include mechanism. Each file is expected to
- * return an associative array that maps service names to instantiator functions.
- */
- public function loadWiringFiles( array $wiringFiles ) {
- foreach ( $wiringFiles as $file ) {
- // the wiring file is required to return an array of instantiators.
- $wiring = require $file;
-
- Assert::postcondition(
- is_array( $wiring ),
- "Wiring file $file is expected to return an array!"
- );
-
- $this->applyWiring( $wiring );
- }
- }
-
- /**
- * Registers multiple services (aka a "wiring").
- *
- * @param array $serviceInstantiators An associative array mapping service names to
- * instantiator functions.
- */
- public function applyWiring( array $serviceInstantiators ) {
- Assert::parameterElementType( 'callable', $serviceInstantiators, '$serviceInstantiators' );
-
- foreach ( $serviceInstantiators as $name => $instantiator ) {
- $this->defineService( $name, $instantiator );
- }
- }
-
- /**
- * Imports all wiring defined in $container. Wiring defined in $container
- * will override any wiring already defined locally. However, already
- * existing service instances will be preserved.
- *
- * @since 1.28
- *
- * @param ServiceContainer $container
- * @param string[] $skip A list of service names to skip during import
- */
- public function importWiring( ServiceContainer $container, $skip = [] ) {
- $newInstantiators = array_diff_key(
- $container->serviceInstantiators,
- array_flip( $skip )
- );
-
- $this->serviceInstantiators = array_merge(
- $this->serviceInstantiators,
- $newInstantiators
- );
-
- $newManipulators = array_diff(
- array_keys( $container->serviceManipulators ),
- $skip
- );
-
- foreach ( $newManipulators as $name ) {
- if ( isset( $this->serviceManipulators[$name] ) ) {
- $this->serviceManipulators[$name] = array_merge(
- $this->serviceManipulators[$name],
- $container->serviceManipulators[$name]
- );
- } else {
- $this->serviceManipulators[$name] = $container->serviceManipulators[$name];
- }
- }
- }
-
- /**
- * Returns true if a service is defined for $name, that is, if a call to getService( $name )
- * would return a service instance.
- *
- * @param string $name
- *
- * @return bool
- */
- public function hasService( $name ) {
- return isset( $this->serviceInstantiators[$name] );
- }
-
- /**
- * Returns the service instance for $name only if that service has already been instantiated.
- * This is intended for situations where services get destroyed/cleaned up, so we can
- * avoid creating a service just to destroy it again.
- *
- * @note This is intended for internal use and for test fixtures.
- * Application logic should use getService() instead.
- *
- * @see getService().
- *
- * @param string $name
- *
- * @return object|null The service instance, or null if the service has not yet been instantiated.
- * @throws RuntimeException if $name does not refer to a known service.
- */
- public function peekService( $name ) {
- if ( !$this->hasService( $name ) ) {
- throw new NoSuchServiceException( $name );
- }
-
- return $this->services[$name] ?? null;
- }
-
- /**
- * @return string[]
- */
- public function getServiceNames() {
- return array_keys( $this->serviceInstantiators );
- }
-
- /**
- * Define a new service. The service must not be known already.
- *
- * @see getService().
- * @see redefineService().
- *
- * @param string $name The name of the service to register, for use with getService().
- * @param callable $instantiator Callback that returns a service instance.
- * Will be called with this MediaWikiServices instance as the only parameter.
- * Any extra instantiation parameters provided to the constructor will be
- * passed as subsequent parameters when invoking the instantiator.
- *
- * @throws RuntimeException if there is already a service registered as $name.
- */
- public function defineService( $name, callable $instantiator ) {
- Assert::parameterType( 'string', $name, '$name' );
-
- if ( $this->hasService( $name ) ) {
- throw new ServiceAlreadyDefinedException( $name );
- }
-
- $this->serviceInstantiators[$name] = $instantiator;
- }
-
- /**
- * Replace an already defined service.
- *
- * @see defineService().
- *
- * @note This will fail if the service was already instantiated. If the service was previously
- * disabled, it will be re-enabled by this call. Any manipulators registered for the service
- * will remain in place.
- *
- * @param string $name The name of the service to register.
- * @param callable $instantiator Callback function that returns a service instance.
- * Will be called with this MediaWikiServices instance as the only parameter.
- * The instantiator must return a service compatible with the originally defined service.
- * Any extra instantiation parameters provided to the constructor will be
- * passed as subsequent parameters when invoking the instantiator.
- *
- * @throws NoSuchServiceException if $name is not a known service.
- * @throws CannotReplaceActiveServiceException if the service was already instantiated.
- */
- public function redefineService( $name, callable $instantiator ) {
- Assert::parameterType( 'string', $name, '$name' );
-
- if ( !$this->hasService( $name ) ) {
- throw new NoSuchServiceException( $name );
- }
-
- if ( isset( $this->services[$name] ) ) {
- throw new CannotReplaceActiveServiceException( $name );
- }
-
- $this->serviceInstantiators[$name] = $instantiator;
- unset( $this->disabled[$name] );
- }
-
- /**
- * Add a service manipulator callback for the given service.
- * This method may be used by extensions that need to wrap, replace, or re-configure a
- * service. It would typically be called from a MediaWikiServices hook handler.
- *
- * The manipulator callback is called just after the service is instantiated.
- * It can call methods on the service to change configuration, or wrap or otherwise
- * replace it.
- *
- * @see defineService().
- * @see redefineService().
- *
- * @note This will fail if the service was already instantiated.
- *
- * @since 1.32
- *
- * @param string $name The name of the service to manipulate.
- * @param callable $manipulator Callback function that manipulates, wraps or replaces a
- * service instance. The callback receives the new service instance and this the
- * ServiceContainer as parameters, as well as any extra instantiation parameters specified
- * when constructing this ServiceContainer. If the callback returns a value, that
- * value replaces the original service instance.
- *
- * @throws NoSuchServiceException if $name is not a known service.
- * @throws CannotReplaceActiveServiceException if the service was already instantiated.
- */
- public function addServiceManipulator( $name, callable $manipulator ) {
- Assert::parameterType( 'string', $name, '$name' );
-
- if ( !$this->hasService( $name ) ) {
- throw new NoSuchServiceException( $name );
- }
-
- if ( isset( $this->services[$name] ) ) {
- throw new CannotReplaceActiveServiceException( $name );
- }
-
- $this->serviceManipulators[$name][] = $manipulator;
- }
-
- /**
- * Disables a service.
- *
- * @note Attempts to call getService() for a disabled service will result
- * in a DisabledServiceException. Calling peekService for a disabled service will
- * return null. Disabled services are listed by getServiceNames(). A disabled service
- * can be enabled again using redefineService().
- *
- * @note If the service was already active (that is, instantiated) when getting disabled,
- * and the service instance implements DestructibleService, destroy() is called on the
- * service instance.
- *
- * @see redefineService()
- * @see resetService()
- *
- * @param string $name The name of the service to disable.
- *
- * @throws RuntimeException if $name is not a known service.
- */
- public function disableService( $name ) {
- $this->resetService( $name );
-
- $this->disabled[$name] = true;
- }
-
- /**
- * Resets a service by dropping the service instance.
- * If the service instances implements DestructibleService, destroy()
- * is called on the service instance.
- *
- * @warning This is generally unsafe! Other services may still retain references
- * to the stale service instance, leading to failures and inconsistencies. Subclasses
- * may use this method to reset specific services under specific instances, but
- * it should not be exposed to application logic.
- *
- * @note This is declared final so subclasses can not interfere with the expectations
- * disableService() has when calling resetService().
- *
- * @see redefineService()
- * @see disableService().
- *
- * @param string $name The name of the service to reset.
- * @param bool $destroy Whether the service instance should be destroyed if it exists.
- * When set to false, any existing service instance will effectively be detached
- * from the container.
- *
- * @throws RuntimeException if $name is not a known service.
- */
- final protected function resetService( $name, $destroy = true ) {
- Assert::parameterType( 'string', $name, '$name' );
-
- $instance = $this->peekService( $name );
-
- if ( $destroy && $instance instanceof DestructibleService ) {
- $instance->destroy();
- }
-
- unset( $this->services[$name] );
- unset( $this->disabled[$name] );
- }
-
- /**
- * Returns a service object of the kind associated with $name.
- * Services instances are instantiated lazily, on demand.
- * This method may or may not return the same service instance
- * when called multiple times with the same $name.
- *
- * @note Rather than calling this method directly, it is recommended to provide
- * getters with more meaningful names and more specific return types, using
- * a subclass or wrapper.
- *
- * @see redefineService().
- *
- * @param string $name The service name
- *
- * @throws NoSuchServiceException if $name is not a known service.
- * @throws ContainerDisabledException if this container has already been destroyed.
- * @throws ServiceDisabledException if the requested service has been disabled.
- *
- * @return object The service instance
- */
- public function getService( $name ) {
- if ( $this->destroyed ) {
- throw new ContainerDisabledException();
- }
-
- if ( isset( $this->disabled[$name] ) ) {
- throw new ServiceDisabledException( $name );
- }
-
- if ( !isset( $this->services[$name] ) ) {
- $this->services[$name] = $this->createService( $name );
- }
-
- return $this->services[$name];
- }
-
- /**
- * @param string $name
- *
- * @throws InvalidArgumentException if $name is not a known service.
- * @return object
- */
- private function createService( $name ) {
- if ( isset( $this->serviceInstantiators[$name] ) ) {
- $service = ( $this->serviceInstantiators[$name] )(
- $this,
- ...$this->extraInstantiationParams
- );
-
- if ( isset( $this->serviceManipulators[$name] ) ) {
- foreach ( $this->serviceManipulators[$name] as $callback ) {
- $ret = call_user_func_array(
- $callback,
- array_merge( [ $service, $this ], $this->extraInstantiationParams )
- );
-
- // If the manipulator callback returns an object, that object replaces
- // the original service instance. This allows the manipulator to wrap
- // or fully replace the service.
- if ( $ret !== null ) {
- $service = $ret;
- }
- }
- }
-
- // NOTE: when adding more wiring logic here, make sure importWiring() is kept in sync!
- } else {
- throw new NoSuchServiceException( $name );
- }
-
- return $service;
- }
-
- /**
- * @param string $name
- * @return bool Whether the service is disabled
- * @since 1.28
- */
- public function isServiceDisabled( $name ) {
- return isset( $this->disabled[$name] );
- }
-}
+++ /dev/null
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when trying to access a disabled service.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when trying to access a disabled service.
- */
-class ServiceDisabledException extends RuntimeException {
-
- /**
- * @param string $serviceName
- * @param Exception|null $previous
- */
- public function __construct( $serviceName, Exception $previous = null ) {
- parent::__construct( "Service disabled: $serviceName", 0, $previous );
- }
-
-}
$out = $this->getOutput();
$allCats = $out->getCategoryLinks();
- if ( !count( $allCats ) ) {
+ if ( $allCats === [] ) {
return '';
}
$pageRestrictions[] = $restriction->getTitle()->getPrefixedText();
}
+ if ( !$block->isSitewide() && empty( $pageRestrictions ) ) {
+ $fields['Editing']['default'] = false;
+ }
+
// Sort the restrictions so they are in alphabetical order.
sort( $pageRestrictions );
$fields['PageRestrictions']['default'] = implode( "\n", $pageRestrictions );
* @return bool|array True for success, false for didn't-try, array of errors on failure
*/
public function onSubmit( array $data, HTMLForm $form = null ) {
+ // If "Editing" checkbox is unchecked, the block must be a partial block affecting
+ // actions other than editing, and there must be no restrictions.
+ if ( isset( $data['Editing'] ) && $data['Editing'] === false ) {
+ $data['EditingRestriction'] = 'partial';
+ $data['PageRestrictions'] = [];
+ }
return self::processForm( $data, $form->getContext() );
}
* Attempts to clean up broken items
*/
private function cleanupWatchlist() {
- if ( !count( $this->badItems ) ) {
+ if ( $this->badItems === [] ) {
return; // nothing to do
}
'<span class="mw-listgrants-right-name">' . $permission . '</span>'
)->parse();
}
- if ( !count( $descs ) ) {
+ if ( $descs === [] ) {
$grantCellHtml = '';
} else {
sort( $descs );
'<span class="mw-passwordpolicies-policy-name">' . $gp . '</span>'
)->parse();
}
- if ( !count( $ret ) ) {
+ if ( $ret === [] ) {
return '';
} else {
return '<ul><li>' . implode( "</li>\n<li>", $ret ) . '</li></ul>';
$categories = array_map( 'trim', explode( '|', $opts['categories'] ) );
- if ( !count( $categories ) ) {
+ if ( $categories === [] ) {
return;
}
}
# Shortcut?
- if ( !count( $articles ) || !count( $cats ) ) {
+ if ( $articles === [] || $cats === [] ) {
return;
}
# Extract manually requested namespaces
$nslist = $this->powerSearch( $request );
- if ( !count( $nslist ) ) {
+ if ( $nslist === [] ) {
# Fallback to user preference
$nslist = $this->searchConfig->userNamespaces( $user );
}
$profile = null;
- if ( !count( $nslist ) ) {
+ if ( $nslist === [] ) {
$profile = 'default';
}
$pages = MediaWikiServices::getInstance()->getSpecialPageFactory()->
getUsablePages( $this->getUser() );
- if ( !count( $pages ) ) {
+ if ( $pages === [] ) {
# Yeah, that was pointless. Thanks for coming.
return false;
}
}
# Extra message, when no category was found
- if ( !count( $allMsgs ) ) {
+ if ( $allMsgs === [] ) {
$allMsgs[] = $this->msg( 'trackingcategories-disabled' )->parse();
}
if ( is_array( $data ) ) {
if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
- if ( !count( $data['user_groups'] ) ) {
+ if ( $data['user_groups'] === [] ) {
$this->mGroupMemberships = [];
} else {
$firstGroup = reset( $data['user_groups'] );
}
$toPromote = Autopromote::getAutopromoteOnceGroups( $this, $event );
- if ( !count( $toPromote ) ) {
+ if ( $toPromote === [] ) {
return [];
}
$this->output = $specialSearch->getOutput();
$this->showMultimedia = $showMultimedia;
}
+
/**
* @param string $term User provided search term
* @param SearchResultSet|SearchResultSet[] $resultSets List of interwiki
'余威德' => '余威德',
'余子明' => '余子明',
'余思敏' => '余思敏',
+'余杰' => '余杰',
'佛历' => '佛曆',
'佛罗棱萨' => '佛羅稜薩',
'佛钟' => '佛鐘',
'年历次' => '年歷次',
'年谷' => '年穀',
'年里' => '年裡',
+'年里约' => '年里約',
'年鉴' => '年鑑',
'并州' => '并州',
'并日而食' => '并日而食',
'恒生' => '恒生',
'恒隆' => '恒隆',
'恕乏价催' => '恕乏价催',
+'恢复' => '恢復',
'息交绝游' => '息交絕遊',
'息谷' => '息穀',
'悒郁' => '悒鬱',
'复试' => '複試',
'复课' => '複課',
'复议' => '複議',
+'复读机' => '複讀機',
'复变函数' => '複變函數',
'复赛' => '複賽',
'复辅音' => '複輔音',
'趲' => '趱',
'跡' => '迹',
'跥' => '跺',
-'跴' => '踩',
'踁' => '胫',
'踐' => '践',
'踫' => '碰',
'釐正' => '厘正',
'釐毫' => '厘毫',
'釐清' => '厘清',
+'釐米' => '厘米',
'釐訂' => '厘订',
'釐革' => '厘革',
'原著' => '原著',
'克林顿' => '柯林頓',
'戴卓爾' => '柴契爾',
'撒切尔' => '柴契爾',
+'格林納丁斯' => '格瑞那丁',
+'格林纳丁斯' => '格瑞那丁',
'格林納達' => '格瑞那達',
'格林纳达' => '格瑞那達',
'桃金娘' => '桃金孃',
'標準杆' => '標準桿',
'毛里求斯' => '模里西斯',
'毛里裘斯' => '模里西斯',
+'樸茨茅夫' => '樸茨茅斯',
'機械人' => '機器人',
'概率' => '機率',
'電單車' => '機車',
'昂山素季' => '翁山蘇姬',
'圣基茨和尼维斯' => '聖克里斯多福及尼維斯',
'聖吉斯納域斯' => '聖克里斯多福及尼維斯',
+'聖佐治' => '聖喬治',
'圣文森特和格林纳丁斯' => '聖文森及格瑞那丁',
'聖文森特和格林納丁斯' => '聖文森及格瑞那丁',
'圣赫勒拿' => '聖赫倫那',
'链接' => '連結',
'連結他' => '連結他',
'进制' => '進位',
+'道琼斯' => '道瓊',
+'道瓊斯' => '道瓊',
'达·芬奇' => '達·文西',
'达芬奇' => '達文西',
'溫納圖萬' => '那杜',
'高清电视' => '高畫質電視',
'斗着' => '鬥著',
'魯賓斯·巴里切羅' => '魯本·巴瑞切羅',
-'咪高峰' => '麥克風',
'迈克尔' => '麥可',
'麦克尔' => '麥可',
'迈凯轮' => '麥拿輪',
'味著述' => '味著述',
'味著錄' => '味著錄',
'咖哩' => '咖喱',
-'麥克風' => '咪高峰',
-'麦克风' => '咪高峰',
'咬著' => '咬着',
'哥特式' => '哥德式',
'哥斯大黎加' => '哥斯達黎加',
'奥黛丽·赫本' => '柯德莉·夏萍',
'奧黛麗·赫本' => '柯德莉·夏萍',
'哥廷根' => '格丁根',
+'格瑞那丁' => '格林納丁斯',
'格瑞那達' => '格林納達',
'格莱美奖' => '格林美獎',
'葛萊美獎' => '格林美獎',
'標志著' => '標志着',
'寶獅' => '標致',
'標誌著' => '標誌着',
+'朴茨茅斯' => '樸茨茅夫',
+'樸茨茅斯' => '樸茨茅夫',
'树林里' => '樹林裏',
'工具機' => '機床',
'机器人' => '機械人',
'考著者' => '考著者',
'考著述' => '考著述',
'考著錄' => '考著錄',
+'圣乔治' => '聖佐治',
+'聖喬治' => '聖佐治',
'圣基茨和尼维斯' => '聖吉斯納域斯',
'聖克里斯多福及尼維斯' => '聖吉斯納域斯',
'聖文森及格瑞那丁' => '聖文森特和格林納丁斯',
'遍佈著' => '遍佈着',
'遍布著' => '遍佈着',
'過著' => '過着',
+'道瓊' => '道瓊斯',
+'道瓊斯' => '道瓊斯',
'达·芬奇' => '達·文西',
'达芬奇' => '達文西',
'達著' => '達着',
'土魯斯' => '图卢兹',
'吐瓦魯' => '图瓦卢',
'原子筆' => '圆珠笔',
+'聖佐治' => '圣乔治',
'汕埠' => '圣佩德罗苏拉',
'聖露西亞' => '圣卢西亚',
'聖克里斯多福及尼維斯' => '圣基茨和尼维斯',
'本著者' => '本著者',
'本著述' => '本著述',
'本帳' => '本账',
+'樸茨茅夫' => '朴茨茅斯',
'機械人' => '机器人',
'工具機' => '机床',
'殺著' => '杀着',
'查維茲' => '查韦斯',
'標志著' => '标志着',
'標誌著' => '标志着',
+'格瑞那丁' => '格林纳丁斯',
'格瑞那達' => '格林纳达',
'格林美獎' => '格莱美奖',
'葛萊美獎' => '格莱美奖',
'畫著稱' => '画著称',
'畫著者' => '画著者',
'介面' => '界面',
+'留尼旺' => '留尼汪',
'留著' => '留着',
'留著書' => '留着书',
'留著作' => '留著作',
'遇著述' => '遇著述',
'遍佈著' => '遍布着',
'遍布著' => '遍布着',
+'道瓊' => '道琼斯',
+'道瓊斯' => '道琼斯',
'部份' => '部分',
'配合著' => '配合着',
'配合著名' => '配合著名',
'高著述' => '高著述',
'魚雷' => '鱼雷',
'鱼雷' => '鱼雷',
-'咪高峰' => '麦克风',
'黏著' => '黏着',
'黏著書' => '黏著书',
'黏著作' => '黏著作',
NS_FILE_TALK => 'फायल_चर्चा',
NS_MEDIAWIKI => 'मिडियाविकी',
NS_MEDIAWIKI_TALK => 'मिडियाविकी_चर्चा',
- NS_TEMPLATE => 'पà¥\8dरारà¥\82प',
- NS_TEMPLATE_TALK => 'पà¥\8dरारà¥\82प_चर्चा',
+ NS_TEMPLATE => 'साà¤\82à¤\9aà¥\8b',
+ NS_TEMPLATE_TALK => 'साà¤\82à¤\9aà¥\8b_चर्चा',
NS_HELP => 'आदार',
NS_HELP_TALK => 'आदार_चर्चा',
NS_CATEGORY => 'वर्ग',
'श्रेणी_चर्चा' => NS_CATEGORY_TALK,
'मिडिया' => NS_MEDIA,
'उपेगकर्तो' => NS_USER,
- 'उपेगकर्तो_चर्चा' => NS_USER_TALK
+ 'उपेगकर्तो_चर्चा' => NS_USER_TALK,
+ 'प्रारूप' => NS_TEMPLATE,
+ 'प्रारूप_चर्चा' => NS_TEMPLATE_TALK,
];
數碼訊號 数字信号
數位音樂 数字音乐
數位化 数字化
-咪高峰 麦克风
幫浦 泵
電單車 摩托车
演化論 进化论
皮特肯 皮特凯恩
安地卡 安提瓜
撒拉威阿拉伯 阿拉伯撒哈拉
+樸茨茅夫 朴茨茅斯
+留尼旺 留尼汪
+道瓊斯 道琼斯
+道瓊 道琼斯
+聖佐治 圣乔治
+格瑞那丁 格林纳丁斯
數位音樂 數碼音樂
数字化 數碼化
數位化 數碼化
-麥克風 咪高峰
-麦克风 咪高峰
幫浦 泵
朝鲜战争 韓戰
万历朝鲜战争 萬曆朝鮮戰爭
皮特凯恩 皮特肯
安地卡 安提瓜
撒拉威阿拉伯 阿拉伯撒哈拉
+朴茨茅斯 樸茨茅夫
+樸茨茅斯 樸茨茅夫
+道瓊斯 道瓊斯
+道瓊 道瓊斯
+圣乔治 聖佐治
+聖喬治 聖佐治
+格瑞那丁 格林納丁斯
釐正 厘正
毫釐 毫厘
釐毫 厘毫
+釐米 厘米
剖釐 剖厘
一釐 一厘
昇平 升平
網絡遊戲 網路遊戲
电脑网络 電腦網路
電腦網絡 電腦網路
-咪高峰 麥克風
電單車 機車
搜索引擎 搜尋引擎
福尔马林 福馬林
皮特凯恩 皮特肯
安提瓜 安地卡
阿拉伯撒哈拉 撒拉威阿拉伯
+樸茨茅夫 樸茨茅斯
+留尼汪 留尼旺
+道琼斯 道瓊
+道瓊斯 道瓊
+聖佐治 聖喬治
+格林纳丁斯 格瑞那丁
+格林納丁斯 格瑞那丁
陳杰 陳杰
黃杰 黃杰
謝杰 謝杰
+余杰 余杰
博杰普爾 博杰普爾
寶曆 寶曆
涂謹申 涂謹申
U+08D1C贜|U+08D43赃|
U+08D82趂|U+08D81趁|
U+08DE5跥|U+08DFA跺|
-U+08DF4跴|U+08E29踩|
U+08E01踁|U+080EB胫|
U+08E2B踫|U+078B0碰|
U+08E30踰|U+0903E逾|
張三丰
復始
往復式
+恢復 #分詞用
複分析
複輔音
複元音
複合 #因複合詞頻遠高於復合
複方
複穗
+複讀機
撥穀
扁擬穀盜蟲
不穀
孛里海
布里海
公里海
+年里約 #里约奧運
地圖裡
版圖裡
配圖裡
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <title>remove</title>
<g id="remove">
<path id="trash-can" d="M12 10h-1v6h1v-6zm-2 0h-1v6h1v-6zm4 0h-1v6h1v-6zm0-4v-1h-5v1h-3v3h1v7.966l1 1.031v-.074.077h6.984l.016-.018v.015l1-1.031v-7.966h1v-3h-3zm1 11h-7v-8h7v8zm1-9h-9v-1h9v1z"/>
</g>
<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="red">
- <g xmlns:default="http://www.w3.org/2000/svg" id="remove">
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>remove</title><g fill="red">
+
+ <g id="remove">
<path id="trash-can" d="M12 10h-1v6h1v-6zm-2 0h-1v6h1v-6zm4 0h-1v6h1v-6zm0-4v-1h-5v1h-3v3h1v7.966l1 1.031v-.074.077h6.984l.016-.018v.015l1-1.031v-7.966h1v-3h-3zm1 11h-7v-8h7v8zm1-9h-9v-1h9v1z"/>
</g>
</g></svg>
<?php
use MediaWiki\MediaWikiServices;
-use MediaWiki\Services\DestructibleService;
-use MediaWiki\Services\SalvageableService;
-use MediaWiki\Services\ServiceDisabledException;
+use Wikimedia\Services\DestructibleService;
+use Wikimedia\Services\SalvageableService;
+use Wikimedia\Services\ServiceDisabledException;
/**
* @covers MediaWiki\MediaWikiServices
'Test',
function () use ( &$serviceCounter ) {
$serviceCounter++;
- $service = $this->createMock( MediaWiki\Services\DestructibleService::class );
+ $service = $this->createMock( Wikimedia\Services\DestructibleService::class );
$service->expects( $this->once() )->method( 'destroy' );
return $service;
}
$services->defineService(
'Test',
function () {
- $service = $this->createMock( MediaWiki\Services\DestructibleService::class );
+ $service = $this->createMock( Wikimedia\Services\DestructibleService::class );
$service->expects( $this->never() )->method( 'destroy' );
return $service;
}
$this->testPageTitle = Title::newFromText( 'UTPage-' . __CLASS__ );
return $this->testPageTitle;
}
+
/**
* @return WikiPage
*/
--- /dev/null
+<?php
+use Wikimedia\Services\ServiceContainer;
+
+/**
+ * @covers Wikimedia\Services\ServiceContainer
+ */
+class ServiceContainerTest extends PHPUnit\Framework\TestCase {
+
+ use MediaWikiCoversValidator; // TODO this library is supposed to be independent of MediaWiki
+ use PHPUnit4And6Compat;
+
+ private function newServiceContainer( $extraArgs = [] ) {
+ return new ServiceContainer( $extraArgs );
+ }
+
+ public function testGetServiceNames() {
+ $services = $this->newServiceContainer();
+ $names = $services->getServiceNames();
+
+ $this->assertInternalType( 'array', $names );
+ $this->assertEmpty( $names );
+
+ $name = 'TestService92834576';
+ $services->defineService( $name, function () {
+ return null;
+ } );
+
+ $names = $services->getServiceNames();
+ $this->assertContains( $name, $names );
+ }
+
+ public function testHasService() {
+ $services = $this->newServiceContainer();
+
+ $name = 'TestService92834576';
+ $this->assertFalse( $services->hasService( $name ) );
+
+ $services->defineService( $name, function () {
+ return null;
+ } );
+
+ $this->assertTrue( $services->hasService( $name ) );
+ }
+
+ public function testGetService() {
+ $services = $this->newServiceContainer( [ 'Foo' ] );
+
+ $theService = new stdClass();
+ $name = 'TestService92834576';
+ $count = 0;
+
+ $services->defineService(
+ $name,
+ function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
+ $count++;
+ PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+ PHPUnit_Framework_Assert::assertSame( $extra, 'Foo' );
+ return $theService;
+ }
+ );
+
+ $this->assertSame( $theService, $services->getService( $name ) );
+
+ $services->getService( $name );
+ $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
+ }
+
+ public function testGetService_fail_unknown() {
+ $services = $this->newServiceContainer();
+
+ $name = 'TestService92834576';
+
+ $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+ $services->getService( $name );
+ }
+
+ public function testPeekService() {
+ $services = $this->newServiceContainer();
+
+ $services->defineService(
+ 'Foo',
+ function () {
+ return new stdClass();
+ }
+ );
+
+ $services->defineService(
+ 'Bar',
+ function () {
+ return new stdClass();
+ }
+ );
+
+ // trigger instantiation of Foo
+ $services->getService( 'Foo' );
+
+ $this->assertInternalType(
+ 'object',
+ $services->peekService( 'Foo' ),
+ 'Peek should return the service object if it had been accessed before.'
+ );
+
+ $this->assertNull(
+ $services->peekService( 'Bar' ),
+ 'Peek should return null if the service was never accessed.'
+ );
+ }
+
+ public function testPeekService_fail_unknown() {
+ $services = $this->newServiceContainer();
+
+ $name = 'TestService92834576';
+
+ $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+ $services->peekService( $name );
+ }
+
+ public function testDefineService() {
+ $services = $this->newServiceContainer();
+
+ $theService = new stdClass();
+ $name = 'TestService92834576';
+
+ $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
+ PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+ return $theService;
+ } );
+
+ $this->assertTrue( $services->hasService( $name ) );
+ $this->assertSame( $theService, $services->getService( $name ) );
+ }
+
+ public function testDefineService_fail_duplicate() {
+ $services = $this->newServiceContainer();
+
+ $theService = new stdClass();
+ $name = 'TestService92834576';
+
+ $services->defineService( $name, function () use ( $theService ) {
+ return $theService;
+ } );
+
+ $this->setExpectedException( Wikimedia\Services\ServiceAlreadyDefinedException::class );
+
+ $services->defineService( $name, function () use ( $theService ) {
+ return $theService;
+ } );
+ }
+
+ public function testApplyWiring() {
+ $services = $this->newServiceContainer();
+
+ $wiring = [
+ 'Foo' => function () {
+ return 'Foo!';
+ },
+ 'Bar' => function () {
+ return 'Bar!';
+ },
+ ];
+
+ $services->applyWiring( $wiring );
+
+ $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
+ $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
+ }
+
+ public function testImportWiring() {
+ $services = $this->newServiceContainer();
+
+ $wiring = [
+ 'Foo' => function () {
+ return 'Foo!';
+ },
+ 'Bar' => function () {
+ return 'Bar!';
+ },
+ 'Car' => function () {
+ return 'FUBAR!';
+ },
+ ];
+
+ $services->applyWiring( $wiring );
+
+ $services->addServiceManipulator( 'Foo', function ( $service ) {
+ return $service . '+X';
+ } );
+
+ $services->addServiceManipulator( 'Car', function ( $service ) {
+ return $service . '+X';
+ } );
+
+ $newServices = $this->newServiceContainer();
+
+ // create a service with manipulator
+ $newServices->defineService( 'Foo', function () {
+ return 'Foo!';
+ } );
+
+ $newServices->addServiceManipulator( 'Foo', function ( $service ) {
+ return $service . '+Y';
+ } );
+
+ // create a service before importing, so we can later check that
+ // existing service instances survive importWiring()
+ $newServices->defineService( 'Car', function () {
+ return 'Car!';
+ } );
+
+ // force instantiation
+ $newServices->getService( 'Car' );
+
+ // Define another service, so we can later check that extra wiring
+ // is not lost.
+ $newServices->defineService( 'Xar', function () {
+ return 'Xar!';
+ } );
+
+ // import wiring, but skip `Bar`
+ $newServices->importWiring( $services, [ 'Bar' ] );
+
+ $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
+ $this->assertSame( 'Foo!+Y+X', $newServices->getService( 'Foo' ) );
+
+ // import all wiring, but preserve existing service instance
+ $newServices->importWiring( $services );
+
+ $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
+ $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
+ $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
+ $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
+ }
+
+ public function testLoadWiringFiles() {
+ $services = $this->newServiceContainer();
+
+ $wiringFiles = [
+ __DIR__ . '/TestWiring1.php',
+ __DIR__ . '/TestWiring2.php',
+ ];
+
+ $services->loadWiringFiles( $wiringFiles );
+
+ $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
+ $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
+ }
+
+ public function testLoadWiringFiles_fail_duplicate() {
+ $services = $this->newServiceContainer();
+
+ $wiringFiles = [
+ __DIR__ . '/TestWiring1.php',
+ __DIR__ . '/./TestWiring1.php',
+ ];
+
+ // loading the same file twice should fail, because
+ $this->setExpectedException( Wikimedia\Services\ServiceAlreadyDefinedException::class );
+
+ $services->loadWiringFiles( $wiringFiles );
+ }
+
+ public function testRedefineService() {
+ $services = $this->newServiceContainer( [ 'Foo' ] );
+
+ $theService1 = new stdClass();
+ $name = 'TestService92834576';
+
+ $services->defineService( $name, function () {
+ PHPUnit_Framework_Assert::fail(
+ 'The original instantiator function should not get called'
+ );
+ } );
+
+ // redefine before instantiation
+ $services->redefineService(
+ $name,
+ function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
+ PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+ PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
+ return $theService1;
+ }
+ );
+
+ // force instantiation, check result
+ $this->assertSame( $theService1, $services->getService( $name ) );
+ }
+
+ public function testRedefineService_disabled() {
+ $services = $this->newServiceContainer( [ 'Foo' ] );
+
+ $theService1 = new stdClass();
+ $name = 'TestService92834576';
+
+ $services->defineService( $name, function () {
+ return 'Foo';
+ } );
+
+ // disable the service. we should be able to redefine it anyway.
+ $services->disableService( $name );
+
+ $services->redefineService( $name, function () use ( $theService1 ) {
+ return $theService1;
+ } );
+
+ // force instantiation, check result
+ $this->assertSame( $theService1, $services->getService( $name ) );
+ }
+
+ public function testRedefineService_fail_undefined() {
+ $services = $this->newServiceContainer();
+
+ $theService = new stdClass();
+ $name = 'TestService92834576';
+
+ $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+ $services->redefineService( $name, function () use ( $theService ) {
+ return $theService;
+ } );
+ }
+
+ public function testRedefineService_fail_in_use() {
+ $services = $this->newServiceContainer( [ 'Foo' ] );
+
+ $theService = new stdClass();
+ $name = 'TestService92834576';
+
+ $services->defineService( $name, function () {
+ return 'Foo';
+ } );
+
+ // create the service, so it can no longer be redefined
+ $services->getService( $name );
+
+ $this->setExpectedException( Wikimedia\Services\CannotReplaceActiveServiceException::class );
+
+ $services->redefineService( $name, function () use ( $theService ) {
+ return $theService;
+ } );
+ }
+
+ public function testAddServiceManipulator() {
+ $services = $this->newServiceContainer( [ 'Foo' ] );
+
+ $theService1 = new stdClass();
+ $theService2 = new stdClass();
+ $name = 'TestService92834576';
+
+ $services->defineService(
+ $name,
+ function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
+ PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+ PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
+ return $theService1;
+ }
+ );
+
+ $services->addServiceManipulator(
+ $name,
+ function (
+ $theService, $actualLocator, $extra
+ ) use (
+ $services, $theService1, $theService2
+ ) {
+ PHPUnit_Framework_Assert::assertSame( $theService1, $theService );
+ PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+ PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
+ return $theService2;
+ }
+ );
+
+ // force instantiation, check result
+ $this->assertSame( $theService2, $services->getService( $name ) );
+ }
+
+ public function testAddServiceManipulator_fail_undefined() {
+ $services = $this->newServiceContainer();
+
+ $theService = new stdClass();
+ $name = 'TestService92834576';
+
+ $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+ $services->addServiceManipulator( $name, function () use ( $theService ) {
+ return $theService;
+ } );
+ }
+
+ public function testAddServiceManipulator_fail_in_use() {
+ $services = $this->newServiceContainer( [ 'Foo' ] );
+
+ $theService = new stdClass();
+ $name = 'TestService92834576';
+
+ $services->defineService( $name, function () use ( $theService ) {
+ return $theService;
+ } );
+
+ // create the service, so it can no longer be redefined
+ $services->getService( $name );
+
+ $this->setExpectedException( Wikimedia\Services\CannotReplaceActiveServiceException::class );
+
+ $services->addServiceManipulator( $name, function () {
+ return 'Foo';
+ } );
+ }
+
+ public function testDisableService() {
+ $services = $this->newServiceContainer( [ 'Foo' ] );
+
+ $destructible = $this->getMockBuilder( Wikimedia\Services\DestructibleService::class )
+ ->getMock();
+ $destructible->expects( $this->once() )
+ ->method( 'destroy' );
+
+ $services->defineService( 'Foo', function () use ( $destructible ) {
+ return $destructible;
+ } );
+ $services->defineService( 'Bar', function () {
+ return new stdClass();
+ } );
+ $services->defineService( 'Qux', function () {
+ return new stdClass();
+ } );
+
+ // instantiate Foo and Bar services
+ $services->getService( 'Foo' );
+ $services->getService( 'Bar' );
+
+ // disable service, should call destroy() once.
+ $services->disableService( 'Foo' );
+
+ // disabled service should still be listed
+ $this->assertContains( 'Foo', $services->getServiceNames() );
+
+ // getting other services should still work
+ $services->getService( 'Bar' );
+
+ // disable non-destructible service, and not-yet-instantiated service
+ $services->disableService( 'Bar' );
+ $services->disableService( 'Qux' );
+
+ $this->assertNull( $services->peekService( 'Bar' ) );
+ $this->assertNull( $services->peekService( 'Qux' ) );
+
+ // disabled service should still be listed
+ $this->assertContains( 'Bar', $services->getServiceNames() );
+ $this->assertContains( 'Qux', $services->getServiceNames() );
+
+ $this->setExpectedException( Wikimedia\Services\ServiceDisabledException::class );
+ $services->getService( 'Qux' );
+ }
+
+ public function testDisableService_fail_undefined() {
+ $services = $this->newServiceContainer();
+
+ $theService = new stdClass();
+ $name = 'TestService92834576';
+
+ $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+ $services->redefineService( $name, function () use ( $theService ) {
+ return $theService;
+ } );
+ }
+
+ public function testDestroy() {
+ $services = $this->newServiceContainer();
+
+ $destructible = $this->getMockBuilder( Wikimedia\Services\DestructibleService::class )
+ ->getMock();
+ $destructible->expects( $this->once() )
+ ->method( 'destroy' );
+
+ $services->defineService( 'Foo', function () use ( $destructible ) {
+ return $destructible;
+ } );
+
+ $services->defineService( 'Bar', function () {
+ return new stdClass();
+ } );
+
+ // create the service
+ $services->getService( 'Foo' );
+
+ // destroy the container
+ $services->destroy();
+
+ $this->setExpectedException( Wikimedia\Services\ContainerDisabledException::class );
+ $services->getService( 'Bar' );
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Test file for testing ServiceContainer::loadWiringFiles
+ */
+
+return [
+ 'Foo' => function () {
+ return 'Foo!';
+ },
+];
--- /dev/null
+<?php
+/**
+ * Test file for testing ServiceContainer::loadWiringFiles
+ */
+
+return [
+ 'Bar' => function () {
+ return 'Bar!';
+ },
+];
// Allocated file for testing
$this->tempFileName = tempnam( wfTempDir(), 'WEBP' );
}
+
public function tearDown() {
parent::tearDown();
unlink( $this->tempFileName );
}
+
/**
* @dataProvider provideTestExtractMetaData
*/
$this->assertEquals( $expectedResult, WebPHandler::extractMetadata( $this->tempFileName ) );
}
+
public function provideTestExtractMetaData() {
// phpcs:disable Generic.Files.LineLength
return [
public function testWithFileExtractMetaData( $filename, $expectedResult ) {
$this->assertEquals( $expectedResult, WebPHandler::extractMetadata( $filename ) );
}
+
public function provideTestWithFileExtractMetaData() {
return [
[ __DIR__ . '/../../data/media/2_webp_ll.webp',
$handler = new WebPHandler();
$this->assertEquals( $expectedResult, $handler->getImageSize( null, $path ) );
}
+
public function provideTestGetImageSize() {
return [
// Public domain files from https://developers.google.com/speed/webp/gallery2
$mime = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
$this->assertEquals( 'image/webp', $mime->guessMimeType( $path, false ) );
}
+
public function provideTestGetMimeType() {
return [
// Public domain files from https://developers.google.com/speed/webp/gallery2
}
];
}
+
/**
* @dataProvider provideMagicWords
*/
]
];
}
+
/**
* @dataProvider providerWrapAndEscapeMessage
* @covers ResourceLoaderLessVarFileModule::wrapAndEscapeMessage
] ]
];
}
+
/**
* @covers ResourceLoaderStartUpModule::getModuleRegistrations
* @dataProvider provideRegistrations
$response
);
}
+
/**
* Verify that when building the startup module response,
* an exception from one module class will not break the entire
+++ /dev/null
-<?php
-use MediaWiki\Services\ServiceContainer;
-
-/**
- * @covers MediaWiki\Services\ServiceContainer
- *
- * @group MediaWiki
- */
-class ServiceContainerTest extends PHPUnit\Framework\TestCase {
-
- use MediaWikiCoversValidator;
- use PHPUnit4And6Compat;
-
- private function newServiceContainer( $extraArgs = [] ) {
- return new ServiceContainer( $extraArgs );
- }
-
- public function testGetServiceNames() {
- $services = $this->newServiceContainer();
- $names = $services->getServiceNames();
-
- $this->assertInternalType( 'array', $names );
- $this->assertEmpty( $names );
-
- $name = 'TestService92834576';
- $services->defineService( $name, function () {
- return null;
- } );
-
- $names = $services->getServiceNames();
- $this->assertContains( $name, $names );
- }
-
- public function testHasService() {
- $services = $this->newServiceContainer();
-
- $name = 'TestService92834576';
- $this->assertFalse( $services->hasService( $name ) );
-
- $services->defineService( $name, function () {
- return null;
- } );
-
- $this->assertTrue( $services->hasService( $name ) );
- }
-
- public function testGetService() {
- $services = $this->newServiceContainer( [ 'Foo' ] );
-
- $theService = new stdClass();
- $name = 'TestService92834576';
- $count = 0;
-
- $services->defineService(
- $name,
- function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
- $count++;
- PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
- PHPUnit_Framework_Assert::assertSame( $extra, 'Foo' );
- return $theService;
- }
- );
-
- $this->assertSame( $theService, $services->getService( $name ) );
-
- $services->getService( $name );
- $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
- }
-
- public function testGetService_fail_unknown() {
- $services = $this->newServiceContainer();
-
- $name = 'TestService92834576';
-
- $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
- $services->getService( $name );
- }
-
- public function testPeekService() {
- $services = $this->newServiceContainer();
-
- $services->defineService(
- 'Foo',
- function () {
- return new stdClass();
- }
- );
-
- $services->defineService(
- 'Bar',
- function () {
- return new stdClass();
- }
- );
-
- // trigger instantiation of Foo
- $services->getService( 'Foo' );
-
- $this->assertInternalType(
- 'object',
- $services->peekService( 'Foo' ),
- 'Peek should return the service object if it had been accessed before.'
- );
-
- $this->assertNull(
- $services->peekService( 'Bar' ),
- 'Peek should return null if the service was never accessed.'
- );
- }
-
- public function testPeekService_fail_unknown() {
- $services = $this->newServiceContainer();
-
- $name = 'TestService92834576';
-
- $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
- $services->peekService( $name );
- }
-
- public function testDefineService() {
- $services = $this->newServiceContainer();
-
- $theService = new stdClass();
- $name = 'TestService92834576';
-
- $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
- PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
- return $theService;
- } );
-
- $this->assertTrue( $services->hasService( $name ) );
- $this->assertSame( $theService, $services->getService( $name ) );
- }
-
- public function testDefineService_fail_duplicate() {
- $services = $this->newServiceContainer();
-
- $theService = new stdClass();
- $name = 'TestService92834576';
-
- $services->defineService( $name, function () use ( $theService ) {
- return $theService;
- } );
-
- $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException::class );
-
- $services->defineService( $name, function () use ( $theService ) {
- return $theService;
- } );
- }
-
- public function testApplyWiring() {
- $services = $this->newServiceContainer();
-
- $wiring = [
- 'Foo' => function () {
- return 'Foo!';
- },
- 'Bar' => function () {
- return 'Bar!';
- },
- ];
-
- $services->applyWiring( $wiring );
-
- $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
- $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
- }
-
- public function testImportWiring() {
- $services = $this->newServiceContainer();
-
- $wiring = [
- 'Foo' => function () {
- return 'Foo!';
- },
- 'Bar' => function () {
- return 'Bar!';
- },
- 'Car' => function () {
- return 'FUBAR!';
- },
- ];
-
- $services->applyWiring( $wiring );
-
- $services->addServiceManipulator( 'Foo', function ( $service ) {
- return $service . '+X';
- } );
-
- $services->addServiceManipulator( 'Car', function ( $service ) {
- return $service . '+X';
- } );
-
- $newServices = $this->newServiceContainer();
-
- // create a service with manipulator
- $newServices->defineService( 'Foo', function () {
- return 'Foo!';
- } );
-
- $newServices->addServiceManipulator( 'Foo', function ( $service ) {
- return $service . '+Y';
- } );
-
- // create a service before importing, so we can later check that
- // existing service instances survive importWiring()
- $newServices->defineService( 'Car', function () {
- return 'Car!';
- } );
-
- // force instantiation
- $newServices->getService( 'Car' );
-
- // Define another service, so we can later check that extra wiring
- // is not lost.
- $newServices->defineService( 'Xar', function () {
- return 'Xar!';
- } );
-
- // import wiring, but skip `Bar`
- $newServices->importWiring( $services, [ 'Bar' ] );
-
- $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
- $this->assertSame( 'Foo!+Y+X', $newServices->getService( 'Foo' ) );
-
- // import all wiring, but preserve existing service instance
- $newServices->importWiring( $services );
-
- $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
- $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
- $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
- $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
- }
-
- public function testLoadWiringFiles() {
- $services = $this->newServiceContainer();
-
- $wiringFiles = [
- __DIR__ . '/TestWiring1.php',
- __DIR__ . '/TestWiring2.php',
- ];
-
- $services->loadWiringFiles( $wiringFiles );
-
- $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
- $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
- }
-
- public function testLoadWiringFiles_fail_duplicate() {
- $services = $this->newServiceContainer();
-
- $wiringFiles = [
- __DIR__ . '/TestWiring1.php',
- __DIR__ . '/./TestWiring1.php',
- ];
-
- // loading the same file twice should fail, because
- $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException::class );
-
- $services->loadWiringFiles( $wiringFiles );
- }
-
- public function testRedefineService() {
- $services = $this->newServiceContainer( [ 'Foo' ] );
-
- $theService1 = new stdClass();
- $name = 'TestService92834576';
-
- $services->defineService( $name, function () {
- PHPUnit_Framework_Assert::fail(
- 'The original instantiator function should not get called'
- );
- } );
-
- // redefine before instantiation
- $services->redefineService(
- $name,
- function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
- PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
- PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
- return $theService1;
- }
- );
-
- // force instantiation, check result
- $this->assertSame( $theService1, $services->getService( $name ) );
- }
-
- public function testRedefineService_disabled() {
- $services = $this->newServiceContainer( [ 'Foo' ] );
-
- $theService1 = new stdClass();
- $name = 'TestService92834576';
-
- $services->defineService( $name, function () {
- return 'Foo';
- } );
-
- // disable the service. we should be able to redefine it anyway.
- $services->disableService( $name );
-
- $services->redefineService( $name, function () use ( $theService1 ) {
- return $theService1;
- } );
-
- // force instantiation, check result
- $this->assertSame( $theService1, $services->getService( $name ) );
- }
-
- public function testRedefineService_fail_undefined() {
- $services = $this->newServiceContainer();
-
- $theService = new stdClass();
- $name = 'TestService92834576';
-
- $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
- $services->redefineService( $name, function () use ( $theService ) {
- return $theService;
- } );
- }
-
- public function testRedefineService_fail_in_use() {
- $services = $this->newServiceContainer( [ 'Foo' ] );
-
- $theService = new stdClass();
- $name = 'TestService92834576';
-
- $services->defineService( $name, function () {
- return 'Foo';
- } );
-
- // create the service, so it can no longer be redefined
- $services->getService( $name );
-
- $this->setExpectedException( MediaWiki\Services\CannotReplaceActiveServiceException::class );
-
- $services->redefineService( $name, function () use ( $theService ) {
- return $theService;
- } );
- }
-
- public function testAddServiceManipulator() {
- $services = $this->newServiceContainer( [ 'Foo' ] );
-
- $theService1 = new stdClass();
- $theService2 = new stdClass();
- $name = 'TestService92834576';
-
- $services->defineService(
- $name,
- function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
- PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
- PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
- return $theService1;
- }
- );
-
- $services->addServiceManipulator(
- $name,
- function (
- $theService, $actualLocator, $extra
- ) use (
- $services, $theService1, $theService2
- ) {
- PHPUnit_Framework_Assert::assertSame( $theService1, $theService );
- PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
- PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
- return $theService2;
- }
- );
-
- // force instantiation, check result
- $this->assertSame( $theService2, $services->getService( $name ) );
- }
-
- public function testAddServiceManipulator_fail_undefined() {
- $services = $this->newServiceContainer();
-
- $theService = new stdClass();
- $name = 'TestService92834576';
-
- $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
- $services->addServiceManipulator( $name, function () use ( $theService ) {
- return $theService;
- } );
- }
-
- public function testAddServiceManipulator_fail_in_use() {
- $services = $this->newServiceContainer( [ 'Foo' ] );
-
- $theService = new stdClass();
- $name = 'TestService92834576';
-
- $services->defineService( $name, function () use ( $theService ) {
- return $theService;
- } );
-
- // create the service, so it can no longer be redefined
- $services->getService( $name );
-
- $this->setExpectedException( MediaWiki\Services\CannotReplaceActiveServiceException::class );
-
- $services->addServiceManipulator( $name, function () {
- return 'Foo';
- } );
- }
-
- public function testDisableService() {
- $services = $this->newServiceContainer( [ 'Foo' ] );
-
- $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService::class )
- ->getMock();
- $destructible->expects( $this->once() )
- ->method( 'destroy' );
-
- $services->defineService( 'Foo', function () use ( $destructible ) {
- return $destructible;
- } );
- $services->defineService( 'Bar', function () {
- return new stdClass();
- } );
- $services->defineService( 'Qux', function () {
- return new stdClass();
- } );
-
- // instantiate Foo and Bar services
- $services->getService( 'Foo' );
- $services->getService( 'Bar' );
-
- // disable service, should call destroy() once.
- $services->disableService( 'Foo' );
-
- // disabled service should still be listed
- $this->assertContains( 'Foo', $services->getServiceNames() );
-
- // getting other services should still work
- $services->getService( 'Bar' );
-
- // disable non-destructible service, and not-yet-instantiated service
- $services->disableService( 'Bar' );
- $services->disableService( 'Qux' );
-
- $this->assertNull( $services->peekService( 'Bar' ) );
- $this->assertNull( $services->peekService( 'Qux' ) );
-
- // disabled service should still be listed
- $this->assertContains( 'Bar', $services->getServiceNames() );
- $this->assertContains( 'Qux', $services->getServiceNames() );
-
- $this->setExpectedException( MediaWiki\Services\ServiceDisabledException::class );
- $services->getService( 'Qux' );
- }
-
- public function testDisableService_fail_undefined() {
- $services = $this->newServiceContainer();
-
- $theService = new stdClass();
- $name = 'TestService92834576';
-
- $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
- $services->redefineService( $name, function () use ( $theService ) {
- return $theService;
- } );
- }
-
- public function testDestroy() {
- $services = $this->newServiceContainer();
-
- $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService::class )
- ->getMock();
- $destructible->expects( $this->once() )
- ->method( 'destroy' );
-
- $services->defineService( 'Foo', function () use ( $destructible ) {
- return $destructible;
- } );
-
- $services->defineService( 'Bar', function () {
- return new stdClass();
- } );
-
- // create the service
- $services->getService( 'Foo' );
-
- // destroy the container
- $services->destroy();
-
- $this->setExpectedException( MediaWiki\Services\ContainerDisabledException::class );
- $services->getService( 'Bar' );
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Test file for testing ServiceContainer::loadWiringFiles
- */
-
-return [
- 'Foo' => function () {
- return 'Foo!';
- },
-];
+++ /dev/null
-<?php
-/**
- * Test file for testing ServiceContainer::loadWiringFiles
- */
-
-return [
- 'Bar' => function () {
- return 'Bar!';
- },
-];
] ) ],
];
}
+
/**
* @dataProvider provideValidRequests
*/