MediaWiki  master
PageProps.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Page;
24 
25 use MapCacheLRU;
28 use Title;
30 
36 class PageProps {
37 
39  private $linkBatchFactory;
40 
42  private $loadBalancer;
43 
45  private const CACHE_TTL = 10; // integer; TTL in seconds
46  private const CACHE_SIZE = 100; // integer; max cached pages
47 
49  private $cache;
50 
55  public function __construct(
56  LinkBatchFactory $linkBatchFactory,
57  ILoadBalancer $loadBalancer
58  ) {
59  $this->linkBatchFactory = $linkBatchFactory;
60  $this->loadBalancer = $loadBalancer;
61  $this->cache = new MapCacheLRU( self::CACHE_SIZE );
62  }
63 
68  public function ensureCacheSize( $size ) {
69  if ( $this->cache->getMaxSize() < $size ) {
70  $this->cache->setMaxSize( $size );
71  }
72  }
73 
92  public function getProperties( $titles, $propertyNames ) {
93  if ( is_array( $propertyNames ) ) {
94  $gotArray = true;
95  } else {
96  $propertyNames = [ $propertyNames ];
97  $gotArray = false;
98  }
99 
100  $values = [];
101  $goodIDs = $this->getGoodIDs( $titles );
102  $queryIDs = [];
103  foreach ( $goodIDs as $pageID ) {
104  foreach ( $propertyNames as $propertyName ) {
105  $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
106  if ( $propertyValue === false ) {
107  $queryIDs[] = $pageID;
108  break;
109  } elseif ( $gotArray ) {
110  $values[$pageID][$propertyName] = $propertyValue;
111  } else {
112  $values[$pageID] = $propertyValue;
113  }
114  }
115  }
116 
117  if ( $queryIDs ) {
118  $queryBuilder = $this->loadBalancer->getConnectionRef( DB_REPLICA )->newSelectQueryBuilder();
119  $queryBuilder->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
120  ->from( 'page_props' )
121  ->where( [ 'pp_page' => $queryIDs, 'pp_propname' => $propertyNames ] )
122  ->caller( __METHOD__ );
123  $result = $queryBuilder->fetchResultSet();
124 
125  foreach ( $result as $row ) {
126  $pageID = $row->pp_page;
127  $propertyName = $row->pp_propname;
128  $propertyValue = $row->pp_value;
129  $this->cache->setField( $pageID, $propertyName, $propertyValue );
130  if ( $gotArray ) {
131  $values[$pageID][$propertyName] = $propertyValue;
132  } else {
133  $values[$pageID] = $propertyValue;
134  }
135  }
136  }
137 
138  return $values;
139  }
140 
154  public function getAllProperties( $titles ) {
155  $values = [];
156  $goodIDs = $this->getGoodIDs( $titles );
157  $queryIDs = [];
158  foreach ( $goodIDs as $pageID ) {
159  $pageProperties = $this->getCachedProperties( $pageID );
160  if ( $pageProperties === false ) {
161  $queryIDs[] = $pageID;
162  } else {
163  $values[$pageID] = $pageProperties;
164  }
165  }
166 
167  if ( $queryIDs != [] ) {
168  $queryBuilder = $this->loadBalancer->getConnectionRef( DB_REPLICA )->newSelectQueryBuilder();
169  $queryBuilder->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
170  ->from( 'page_props' )
171  ->where( [ 'pp_page' => $queryIDs ] )
172  ->caller( __METHOD__ );
173  $result = $queryBuilder->fetchResultSet();
174 
175  $currentPageID = 0;
176  $pageProperties = [];
177  foreach ( $result as $row ) {
178  $pageID = $row->pp_page;
179  if ( $currentPageID != $pageID ) {
180  if ( $pageProperties ) {
181  // @phan-suppress-next-line PhanTypeMismatchArgument False positive
182  $this->cacheProperties( $currentPageID, $pageProperties );
183  $values[$currentPageID] = $pageProperties;
184  }
185  $currentPageID = $pageID;
186  $pageProperties = [];
187  }
188  $pageProperties[$row->pp_propname] = $row->pp_value;
189  }
190  if ( $pageProperties != [] ) {
191  // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable pageID set when used
192  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable pageID set when used
193  $this->cacheProperties( $pageID, $pageProperties );
194  // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable pageID set when used
195  // @phan-suppress-next-line PhanTypeMismatchDimAssignment pageID set when used
196  $values[$pageID] = $pageProperties;
197  }
198  }
199 
200  return $values;
201  }
202 
207  private function getGoodIDs( $titles ) {
208  $result = [];
209  if ( is_iterable( $titles ) ) {
210  if ( $titles instanceof TitleArray ||
211  ( is_array( $titles ) && reset( $titles ) instanceof Title
212  ) ) {
213  // If the first element is a Title, assume all elements are Titles,
214  // and pre-fetch their IDs using a batch query. For PageIdentityValues
215  // or PageStoreRecords, this is not necessary, since they already
216  // know their ID.
217  $this->linkBatchFactory->newLinkBatch( $titles )->execute();
218  }
219 
220  foreach ( $titles as $title ) {
221  // Until we only allow ProperPageIdentity, Title objects
222  // can deceive us with an unexpected Special page
223  if ( $title->canExist() ) {
224  $pageID = $title->getId();
225  if ( $pageID > 0 ) {
226  $result[] = $pageID;
227  }
228  }
229  }
230  } else {
231  // Until we only allow ProperPageIdentity, Title objects
232  // can deceive us with an unexpected Special page
233  if ( $titles->canExist() ) {
234  $pageID = $titles->getId();
235  if ( $pageID > 0 ) {
236  $result[] = $pageID;
237  }
238  }
239  }
240  return $result;
241  }
242 
250  private function getCachedProperty( $pageID, $propertyName ) {
251  if ( $this->cache->hasField( $pageID, $propertyName, self::CACHE_TTL ) ) {
252  return $this->cache->getField( $pageID, $propertyName );
253  }
254  if ( $this->cache->hasField( 0, $pageID, self::CACHE_TTL ) ) {
255  $pageProperties = $this->cache->getField( 0, $pageID );
256  if ( isset( $pageProperties[$propertyName] ) ) {
257  return $pageProperties[$propertyName];
258  }
259  }
260  return false;
261  }
262 
269  private function getCachedProperties( $pageID ) {
270  if ( $this->cache->hasField( 0, $pageID, self::CACHE_TTL ) ) {
271  return $this->cache->getField( 0, $pageID );
272  }
273  return false;
274  }
275 
282  private function cacheProperties( $pageID, $pageProperties ) {
283  $this->cache->clear( $pageID );
284  $this->cache->setField( 0, $pageID, $pageProperties );
285  }
286 }
287 
288 class_alias( PageProps::class, 'PageProps' );
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:36
Gives access to properties of a page.
Definition: PageProps.php:36
ensureCacheSize( $size)
Ensure that cache has at least this size.
Definition: PageProps.php:68
getAllProperties( $titles)
Get all page property values.
Definition: PageProps.php:154
__construct(LinkBatchFactory $linkBatchFactory, ILoadBalancer $loadBalancer)
Definition: PageProps.php:55
getProperties( $titles, $propertyNames)
Given one or more Titles and one or more names of properties, returns an associative array mapping pa...
Definition: PageProps.php:92
The TitleArray class only exists to provide the newFromResult method at pre- sent.
Definition: TitleArray.php:41
Represents a title within MediaWiki.
Definition: Title.php:52
This class is a delegate to ILBFactory for a given database cluster.
const DB_REPLICA
Definition: defines.php:26