X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fexternalstore%2FExternalStoreFactory.php;h=9998640dddc1874fb11b92aec75bd925c499506d;hb=9ddd146c262806e993ea66994f367a0a795e762d;hp=940fb2e20c4c608f06b8b0839c29e5ef7a531a60;hpb=15f6eff90c305d405fe4331c8a8dc8caa842e5b3;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/externalstore/ExternalStoreFactory.php b/includes/externalstore/ExternalStoreFactory.php index 940fb2e20c..9998640ddd 100644 --- a/includes/externalstore/ExternalStoreFactory.php +++ b/includes/externalstore/ExternalStoreFactory.php @@ -3,40 +3,174 @@ * @defgroup ExternalStorage ExternalStorage */ +use MediaWiki\MediaWikiServices; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Wikimedia\Assert\Assert; + /** * @ingroup ExternalStorage */ -class ExternalStoreFactory { +class ExternalStoreFactory implements LoggerAwareInterface { + /** @var string[] List of storage access protocols */ + private $protocols; + /** @var string[] List of base storage URLs that define locations for writes */ + private $writeBaseUrls; + /** @var string Default database domain to store content under */ + private $localDomainId; + /** @var LoggerInterface */ + private $logger; /** - * @var array + * @param string[] $externalStores See $wgExternalStores + * @param string[] $defaultStores See $wgDefaultExternalStore + * @param string $localDomainId Local database/wiki ID + * @param LoggerInterface|null $logger */ - private $externalStores; + public function __construct( + array $externalStores, + array $defaultStores, + $localDomainId, + LoggerInterface $logger = null + ) { + Assert::parameterType( 'string', $localDomainId, '$localDomainId' ); + + $this->protocols = array_map( 'strtolower', $externalStores ); + $this->writeBaseUrls = $defaultStores; + $this->localDomainId = $localDomainId; + $this->logger = $logger ?: new NullLogger(); + } + + public function setLogger( LoggerInterface $logger ) { + $this->logger = $logger; + } /** - * @param array $externalStores See $wgExternalStores + * @return string[] List of active store types/protocols (lowercased), e.g. [ "db" ] + * @since 1.34 */ - public function __construct( array $externalStores ) { - $this->externalStores = array_map( 'strtolower', $externalStores ); + public function getProtocols() { + return $this->protocols; + } + + /** + * @return string[] List of default base URLs for writes, e.g. [ "DB://cluster1" ] + * @since 1.34 + */ + public function getWriteBaseUrls() { + return $this->writeBaseUrls; } /** * Get an external store object of the given type, with the given parameters * + * The 'domain' field in $params will be set to the local DB domain if it is unset + * or false. A special 'isDomainImplicit' flag is set when this happens, which should + * only be used to handle legacy DB domain configuration concerns (e.g. T200471). + * * @param string $proto Type of external storage, should be a value in $wgExternalStores - * @param array $params Associative array of ExternalStoreMedium parameters - * @return ExternalStoreMedium|bool The store class or false on error + * @param array $params Map of ExternalStoreMedium::__construct context parameters. + * @return ExternalStoreMedium The store class or false on error + * @throws ExternalStoreException When $proto is not recognized */ - public function getStoreObject( $proto, array $params = [] ) { - if ( !$this->externalStores || !in_array( strtolower( $proto ), $this->externalStores ) ) { - // Protocol not enabled - return false; + public function getStore( $proto, array $params = [] ) { + $protoLowercase = strtolower( $proto ); // normalize + if ( !$this->protocols || !in_array( $protoLowercase, $this->protocols ) ) { + throw new ExternalStoreException( "Protocol '$proto' is not enabled." ); } $class = 'ExternalStore' . ucfirst( $proto ); + if ( isset( $params['wiki'] ) ) { + $params += [ 'domain' => $params['wiki'] ]; // b/c + } + if ( !isset( $params['domain'] ) || $params['domain'] === false ) { + $params['domain'] = $this->localDomainId; // default + $params['isDomainImplicit'] = true; // b/c for ExternalStoreDB + } + // @TODO: ideally, this class should not hardcode what classes need what backend factory + // objects. For now, inject the factory instances into __construct() for those that do. + if ( $protoLowercase === 'db' ) { + $params['lbFactory'] = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); + } elseif ( $protoLowercase === 'mwstore' ) { + $params['fbGroup'] = FileBackendGroup::singleton(); + } + $params['logger'] = $this->logger; + + if ( !class_exists( $class ) ) { + throw new ExternalStoreException( "Class '$class' is not defined." ); + } // Any custom modules should be added to $wgAutoLoadClasses for on-demand loading - return class_exists( $class ) ? new $class( $params ) : false; + return new $class( $params ); } + /** + * Get the ExternalStoreMedium for a given URL + * + * $url is either of the form: + * - a) ":///", for retrieval, or + * - b) "://", for storage + * + * @param string $url + * @param array $params Map of ExternalStoreMedium::__construct context parameters + * @return ExternalStoreMedium + * @throws ExternalStoreException When the protocol is missing or not recognized + * @since 1.34 + */ + public function getStoreForUrl( $url, array $params = [] ) { + list( $proto, $path ) = self::splitStorageUrl( $url ); + if ( $path == '' ) { // bad URL + throw new ExternalStoreException( "Invalid URL '$url'" ); + } + + return $this->getStore( $proto, $params ); + } + + /** + * Get the location within the appropriate store for a given a URL + * + * @param string $url + * @return string + * @throws ExternalStoreException + * @since 1.34 + */ + public function getStoreLocationFromUrl( $url ) { + list( , $location ) = self::splitStorageUrl( $url ); + if ( $location == '' ) { // bad URL + throw new ExternalStoreException( "Invalid URL '$url'" ); + } + + return $location; + } + + /** + * @param string[] $urls + * @return array[] Map of (protocol => list of URLs) + * @throws ExternalStoreException + * @since 1.34 + */ + public function getUrlsByProtocol( array $urls ) { + $urlsByProtocol = []; + foreach ( $urls as $url ) { + list( $proto, ) = self::splitStorageUrl( $url ); + $urlsByProtocol[$proto][] = $url; + } + + return $urlsByProtocol; + } + + /** + * @param string $storeUrl + * @return string[] (protocol, store location or location-qualified path) + * @throws ExternalStoreException + */ + private static function splitStorageUrl( $storeUrl ) { + $parts = explode( '://', $storeUrl ); + if ( count( $parts ) != 2 || $parts[0] === '' || $parts[1] === '' ) { + throw new ExternalStoreException( "Invalid storage URL '$storeUrl'" ); + } + + return $parts; + } }