MediaWiki  master
PageProps.php
Go to the documentation of this file.
1 <?php
27 
33 class PageProps {
34 
36  private $linkBatchFactory;
37 
39  private $loadBalancer;
40 
42  private const CACHE_TTL = 10; // integer; TTL in seconds
43  private const CACHE_SIZE = 100; // integer; max cached pages
44 
46  private $cache;
47 
54  public static function getInstance() {
55  wfDeprecated( __METHOD__, '1.38' );
56  return MediaWikiServices::getInstance()->getPageProps();
57  }
58 
63  public function __construct(
64  LinkBatchFactory $linkBatchFactory,
65  ILoadBalancer $loadBalancer
66  ) {
67  $this->linkBatchFactory = $linkBatchFactory;
68  $this->loadBalancer = $loadBalancer;
69  $this->cache = new MapCacheLRU( self::CACHE_SIZE );
70  }
71 
76  public function ensureCacheSize( $size ) {
77  if ( $this->cache->getMaxSize() < $size ) {
78  $this->cache->setMaxSize( $size );
79  }
80  }
81 
100  public function getProperties( $titles, $propertyNames ) {
101  if ( is_array( $propertyNames ) ) {
102  $gotArray = true;
103  } else {
104  $propertyNames = [ $propertyNames ];
105  $gotArray = false;
106  }
107 
108  $values = [];
109  $goodIDs = $this->getGoodIDs( $titles );
110  $queryIDs = [];
111  foreach ( $goodIDs as $pageID ) {
112  foreach ( $propertyNames as $propertyName ) {
113  $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
114  if ( $propertyValue === false ) {
115  $queryIDs[] = $pageID;
116  break;
117  } elseif ( $gotArray ) {
118  $values[$pageID][$propertyName] = $propertyValue;
119  } else {
120  $values[$pageID] = $propertyValue;
121  }
122  }
123  }
124 
125  if ( $queryIDs ) {
126  $queryBuilder = $this->loadBalancer->getConnectionRef( DB_REPLICA )->newSelectQueryBuilder();
127  $queryBuilder->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
128  ->from( 'page_props' )
129  ->where( [ 'pp_page' => $queryIDs, 'pp_propname' => $propertyNames ] )
130  ->caller( __METHOD__ );
131  $result = $queryBuilder->fetchResultSet();
132 
133  foreach ( $result as $row ) {
134  $pageID = $row->pp_page;
135  $propertyName = $row->pp_propname;
136  $propertyValue = $row->pp_value;
137  $this->cache->setField( $pageID, $propertyName, $propertyValue );
138  if ( $gotArray ) {
139  $values[$pageID][$propertyName] = $propertyValue;
140  } else {
141  $values[$pageID] = $propertyValue;
142  }
143  }
144  }
145 
146  return $values;
147  }
148 
162  public function getAllProperties( $titles ) {
163  $values = [];
164  $goodIDs = $this->getGoodIDs( $titles );
165  $queryIDs = [];
166  foreach ( $goodIDs as $pageID ) {
167  $pageProperties = $this->getCachedProperties( $pageID );
168  if ( $pageProperties === false ) {
169  $queryIDs[] = $pageID;
170  } else {
171  $values[$pageID] = $pageProperties;
172  }
173  }
174 
175  if ( $queryIDs != [] ) {
176  $queryBuilder = $this->loadBalancer->getConnectionRef( DB_REPLICA )->newSelectQueryBuilder();
177  $queryBuilder->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
178  ->from( 'page_props' )
179  ->where( [ 'pp_page' => $queryIDs ] )
180  ->caller( __METHOD__ );
181  $result = $queryBuilder->fetchResultSet();
182 
183  $currentPageID = 0;
184  $pageProperties = [];
185  foreach ( $result as $row ) {
186  $pageID = $row->pp_page;
187  if ( $currentPageID != $pageID ) {
188  if ( $pageProperties ) {
189  // @phan-suppress-next-line PhanTypeMismatchArgument False positive
190  $this->cacheProperties( $currentPageID, $pageProperties );
191  $values[$currentPageID] = $pageProperties;
192  }
193  $currentPageID = $pageID;
194  $pageProperties = [];
195  }
196  $pageProperties[$row->pp_propname] = $row->pp_value;
197  }
198  if ( $pageProperties != [] ) {
199  // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable pageID set when used
200  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable pageID set when used
201  $this->cacheProperties( $pageID, $pageProperties );
202  // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable pageID set when used
203  // @phan-suppress-next-line PhanTypeMismatchDimAssignment pageID set when used
204  $values[$pageID] = $pageProperties;
205  }
206  }
207 
208  return $values;
209  }
210 
215  private function getGoodIDs( $titles ) {
216  $result = [];
217  if ( is_iterable( $titles ) ) {
218  if ( $titles instanceof TitleArray ||
219  ( is_array( $titles ) && reset( $titles ) instanceof Title
220  ) ) {
221  // If the first element is a Title, assume all elements are Titles,
222  // and pre-fetch their IDs using a batch query. For PageIdentityValues
223  // or PageStoreRecords, this is not necessary, since they already
224  // know their ID.
225  $this->linkBatchFactory->newLinkBatch( $titles )->execute();
226  }
227 
228  foreach ( $titles as $title ) {
229  // Until we only allow ProperPageIdentity, Title objects
230  // can deceive us with an unexpected Special page
231  if ( $title->canExist() ) {
232  $pageID = $title->getId();
233  if ( $pageID > 0 ) {
234  $result[] = $pageID;
235  }
236  }
237  }
238  } else {
239  // Until we only allow ProperPageIdentity, Title objects
240  // can deceive us with an unexpected Special page
241  if ( $titles->canExist() ) {
242  $pageID = $titles->getId();
243  if ( $pageID > 0 ) {
244  $result[] = $pageID;
245  }
246  }
247  }
248  return $result;
249  }
250 
258  private function getCachedProperty( $pageID, $propertyName ) {
259  if ( $this->cache->hasField( $pageID, $propertyName, self::CACHE_TTL ) ) {
260  return $this->cache->getField( $pageID, $propertyName );
261  }
262  if ( $this->cache->hasField( 0, $pageID, self::CACHE_TTL ) ) {
263  $pageProperties = $this->cache->getField( 0, $pageID );
264  if ( isset( $pageProperties[$propertyName] ) ) {
265  return $pageProperties[$propertyName];
266  }
267  }
268  return false;
269  }
270 
277  private function getCachedProperties( $pageID ) {
278  if ( $this->cache->hasField( 0, $pageID, self::CACHE_TTL ) ) {
279  return $this->cache->getField( 0, $pageID );
280  }
281  return false;
282  }
283 
290  private function cacheProperties( $pageID, $pageProperties ) {
291  $this->cache->clear( $pageID );
292  $this->cache->setField( 0, $pageID, $pageProperties );
293  }
294 }
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:36
Service locator for MediaWiki core services.
Gives access to properties of a page.
Definition: PageProps.php:33
__construct(LinkBatchFactory $linkBatchFactory, ILoadBalancer $loadBalancer)
Definition: PageProps.php:63
ensureCacheSize( $size)
Ensure that cache has at least this size.
Definition: PageProps.php:76
getProperties( $titles, $propertyNames)
Given one or more Titles and one or more names of properties, returns an associative array mapping pa...
Definition: PageProps.php:100
static getInstance()
Definition: PageProps.php:54
getAllProperties( $titles)
Get all page property values.
Definition: PageProps.php:162
The TitleArray class only exists to provide the newFromResult method at pre- sent.
Definition: TitleArray.php:37
Represents a title within MediaWiki.
Definition: Title.php:49
Interface for objects (potentially) representing an editable wiki page.
Create and track the database connections and transactions for a given database cluster.
$cache
Definition: mcc.php:33
const DB_REPLICA
Definition: defines.php:26