MediaWiki REL1_35
ExternalStoreAccess.php
Go to the documentation of this file.
1<?php
6use Psr\Log\LoggerAwareInterface;
7use Psr\Log\LoggerInterface;
8use Psr\Log\NullLogger;
9
22class ExternalStoreAccess implements LoggerAwareInterface {
26 private $logger;
27
32 public function __construct( ExternalStoreFactory $factory, LoggerInterface $logger = null ) {
33 $this->storeFactory = $factory;
34 $this->logger = $logger ?: new NullLogger();
35 }
36
37 public function setLogger( LoggerInterface $logger ) {
38 $this->logger = $logger;
39 }
40
51 public function fetchFromURL( $url, array $params = [] ) {
52 return $this->storeFactory->getStoreForUrl( $url, $params )->fetchFromURL( $url );
53 }
54
65 public function fetchFromURLs( array $urls, array $params = [] ) {
66 $batches = $this->storeFactory->getUrlsByProtocol( $urls );
67 $retval = [];
68 foreach ( $batches as $proto => $batchedUrls ) {
69 $store = $this->storeFactory->getStore( $proto, $params );
70 $retval += $store->batchFetchFromURLs( $batchedUrls );
71 }
72 // invalid, not found, db dead, etc.
73 $missing = array_diff( $urls, array_keys( $retval ) );
74 foreach ( $missing as $url ) {
75 $retval[$url] = false;
76 }
77
78 return $retval;
79 }
80
96 public function insert( $data, array $params = [], array $tryStores = null ) {
97 $tryStores = $tryStores ?? $this->storeFactory->getWriteBaseUrls();
98 if ( !$tryStores ) {
99 throw new ExternalStoreException( "List of external stores provided is empty." );
100 }
101
102 $error = false; // track the last exception thrown
103 $readOnlyCount = 0; // track if a store was read-only
104 while ( count( $tryStores ) > 0 ) {
105 $index = mt_rand( 0, count( $tryStores ) - 1 );
106 $storeUrl = $tryStores[$index];
107
108 $this->logger->debug( __METHOD__ . ": trying $storeUrl" );
109
110 $store = $this->storeFactory->getStoreForUrl( $storeUrl, $params );
111 if ( $store === false ) {
112 throw new ExternalStoreException( "Invalid external storage protocol - $storeUrl" );
113 }
114
115 $location = $this->storeFactory->getStoreLocationFromUrl( $storeUrl );
116 try {
117 if ( $store->isReadOnly( $location ) ) {
118 $readOnlyCount++;
119 $msg = 'read only';
120 } else {
121 $url = $store->store( $location, $data );
122 if ( strlen( $url ) ) {
123 // A store accepted the write; done!
124 return $url;
125 }
126 throw new ExternalStoreException(
127 "No URL returned by storage medium ($storeUrl)"
128 );
129 }
130 } catch ( Exception $ex ) {
131 $error = $ex;
132 $msg = 'caught ' . get_class( $error ) . ' exception: ' . $error->getMessage();
133 }
134
135 unset( $tryStores[$index] ); // Don't try this one again!
136 $tryStores = array_values( $tryStores ); // Must have consecutive keys
137 $this->logger->error(
138 "Unable to store text to external storage {store_path} ({failure})",
139 [ 'store_path' => $storeUrl, 'failure' => $msg ]
140 );
141 }
142
143 // We only get here when all stores failed.
144 if ( $error ) {
145 // At least one store threw an exception. Re-throw the most recent one.
146 throw $error;
147 } elseif ( $readOnlyCount ) {
148 // If no exceptions where thrown and we get here,
149 // this should mean that all stores were in read-only mode.
150 throw new ReadOnlyError();
151 } else {
152 // We shouldn't get here. If there were no failures, this method should have returned
153 // from inside the body of the loop.
154 throw new LogicException( "Unexpected failure to store text to external store" );
155 }
156 }
157
163 public function isReadOnly( $storeUrls = null ) {
164 if ( $storeUrls === null ) {
165 $storeUrls = $this->storeFactory->getWriteBaseUrls();
166 } else {
167 $storeUrls = is_array( $storeUrls ) ? $storeUrls : [ $storeUrls ];
168 }
169
170 if ( !$storeUrls ) {
171 return false; // no stores exists which can be "read only"
172 }
173
174 foreach ( $storeUrls as $storeUrl ) {
175 $store = $this->storeFactory->getStoreForUrl( $storeUrl );
176 $location = $this->storeFactory->getStoreLocationFromUrl( $storeUrl );
177 if ( $store !== false && !$store->isReadOnly( $location ) ) {
178 return false; // at least one store is not read-only
179 }
180 }
181
182 return true; // all stores are read-only
183 }
184}
Key/value blob storage for a collection of storage medium types (e.g.
insert( $data, array $params=[], array $tryStores=null)
Insert data into storage and return the assigned URL.
setLogger(LoggerInterface $logger)
fetchFromURL( $url, array $params=[])
Fetch data from given URL.
ExternalStoreFactory $storeFactory
fetchFromURLs(array $urls, array $params=[])
Fetch data from multiple URLs with a minimum of round trips.
isReadOnly( $storeUrls=null)
__construct(ExternalStoreFactory $factory, LoggerInterface $logger=null)
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...