MediaWiki  master
PageProps.php
Go to the documentation of this file.
1 <?php
21 namespace MediaWiki\Page;
22 
23 use MapCacheLRU;
28 
35 class PageProps {
36  /* TTL in seconds */
37  private const CACHE_TTL = 10;
38  /* max cached pages */
39  private const CACHE_SIZE = 100;
40 
41  private LinkBatchFactory $linkBatchFactory;
42  private IConnectionProvider $dbProvider;
43  private MapCacheLRU $cache;
44 
49  public function __construct(
50  LinkBatchFactory $linkBatchFactory,
51  IConnectionProvider $dbProvider
52  ) {
53  $this->linkBatchFactory = $linkBatchFactory;
54  $this->dbProvider = $dbProvider;
55  $this->cache = new MapCacheLRU( self::CACHE_SIZE );
56  }
57 
62  public function ensureCacheSize( $size ) {
63  if ( $this->cache->getMaxSize() < $size ) {
64  $this->cache->setMaxSize( $size );
65  }
66  }
67 
91  public function getProperties( $titles, $propertyNames ) {
92  if ( is_array( $propertyNames ) ) {
93  $gotArray = true;
94  } else {
95  $propertyNames = [ $propertyNames ];
96  $gotArray = false;
97  }
98 
99  $values = [];
100  $goodIDs = $this->getGoodIDs( $titles );
101  $queryIDs = [];
102  foreach ( $goodIDs as $pageID ) {
103  foreach ( $propertyNames as $propertyName ) {
104  $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
105  if ( $propertyValue === false ) {
106  $queryIDs[] = $pageID;
107  break;
108  } elseif ( $gotArray ) {
109  $values[$pageID][$propertyName] = $propertyValue;
110  } else {
111  $values[$pageID] = $propertyValue;
112  }
113  }
114  }
115 
116  if ( $queryIDs ) {
117  $queryBuilder = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder();
118  $queryBuilder->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
119  ->from( 'page_props' )
120  ->where( [ 'pp_page' => $queryIDs, 'pp_propname' => $propertyNames ] )
121  ->caller( __METHOD__ );
122  $result = $queryBuilder->fetchResultSet();
123 
124  foreach ( $result as $row ) {
125  $pageID = $row->pp_page;
126  $propertyName = $row->pp_propname;
127  $propertyValue = $row->pp_value;
128  $this->cache->setField( $pageID, $propertyName, $propertyValue );
129  if ( $gotArray ) {
130  $values[$pageID][$propertyName] = $propertyValue;
131  } else {
132  $values[$pageID] = $propertyValue;
133  }
134  }
135  }
136 
137  return $values;
138  }
139 
157  public function getAllProperties( $titles ) {
158  $values = [];
159  $goodIDs = $this->getGoodIDs( $titles );
160  $queryIDs = [];
161  foreach ( $goodIDs as $pageID ) {
162  $pageProperties = $this->getCachedProperties( $pageID );
163  if ( $pageProperties === false ) {
164  $queryIDs[] = $pageID;
165  } else {
166  $values[$pageID] = $pageProperties;
167  }
168  }
169 
170  if ( $queryIDs != [] ) {
171  $queryBuilder = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder();
172  $queryBuilder->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
173  ->from( 'page_props' )
174  ->where( [ 'pp_page' => $queryIDs ] )
175  ->caller( __METHOD__ );
176  $result = $queryBuilder->fetchResultSet();
177 
178  $currentPageID = 0;
179  $pageProperties = [];
180  foreach ( $result as $row ) {
181  $pageID = $row->pp_page;
182  if ( $currentPageID != $pageID ) {
183  if ( $pageProperties ) {
184  // @phan-suppress-next-line PhanTypeMismatchArgument False positive
185  $this->cacheProperties( $currentPageID, $pageProperties );
186  $values[$currentPageID] = $pageProperties;
187  }
188  $currentPageID = $pageID;
189  $pageProperties = [];
190  }
191  $pageProperties[$row->pp_propname] = $row->pp_value;
192  }
193  if ( $pageProperties != [] ) {
194  // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable pageID set when used
195  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable pageID set when used
196  $this->cacheProperties( $pageID, $pageProperties );
197  // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable pageID set when used
198  // @phan-suppress-next-line PhanTypeMismatchDimAssignment pageID set when used
199  $values[$pageID] = $pageProperties;
200  }
201  }
202 
203  return $values;
204  }
205 
210  private function getGoodIDs( $titles ) {
211  $result = [];
212  if ( is_iterable( $titles ) ) {
213  if ( $titles instanceof TitleArray ||
214  ( is_array( $titles ) && reset( $titles ) instanceof Title
215  ) ) {
216  // If the first element is a Title, assume all elements are Titles,
217  // and pre-fetch their IDs using a batch query. For PageIdentityValues
218  // or PageStoreRecords, this is not necessary, since they already
219  // know their ID.
220  $this->linkBatchFactory->newLinkBatch( $titles )->execute();
221  }
222 
223  foreach ( $titles as $title ) {
224  // Until we only allow ProperPageIdentity, Title objects
225  // can deceive us with an unexpected Special page
226  if ( $title->canExist() ) {
227  $pageID = $title->getId();
228  if ( $pageID > 0 ) {
229  $result[] = $pageID;
230  }
231  }
232  }
233  } else {
234  // Until we only allow ProperPageIdentity, Title objects
235  // can deceive us with an unexpected Special page
236  if ( $titles->canExist() ) {
237  $pageID = $titles->getId();
238  if ( $pageID > 0 ) {
239  $result[] = $pageID;
240  }
241  }
242  }
243  return $result;
244  }
245 
253  private function getCachedProperty( $pageID, $propertyName ) {
254  if ( $this->cache->hasField( $pageID, $propertyName, self::CACHE_TTL ) ) {
255  return $this->cache->getField( $pageID, $propertyName );
256  }
257  if ( $this->cache->hasField( 0, $pageID, self::CACHE_TTL ) ) {
258  $pageProperties = $this->cache->getField( 0, $pageID );
259  if ( isset( $pageProperties[$propertyName] ) ) {
260  return $pageProperties[$propertyName];
261  }
262  }
263  return false;
264  }
265 
272  private function getCachedProperties( $pageID ) {
273  if ( $this->cache->hasField( 0, $pageID, self::CACHE_TTL ) ) {
274  return $this->cache->getField( 0, $pageID );
275  }
276  return false;
277  }
278 
285  private function cacheProperties( $pageID, $pageProperties ) {
286  $this->cache->clear( $pageID );
287  $this->cache->setField( 0, $pageID, $pageProperties );
288  }
289 }
290 
294 class_alias( PageProps::class, 'PageProps' );
Store key-value entries in a size-limited in-memory LRU cache.
Definition: MapCacheLRU.php:34
Gives access to properties of a page.
Definition: PageProps.php:35
ensureCacheSize( $size)
Ensure that cache has at least this size.
Definition: PageProps.php:62
__construct(LinkBatchFactory $linkBatchFactory, IConnectionProvider $dbProvider)
Definition: PageProps.php:49
getAllProperties( $titles)
Get all page properties of one or more page titles.
Definition: PageProps.php:157
getProperties( $titles, $propertyNames)
Fetch one or more properties for one or more Titles.
Definition: PageProps.php:91
The TitleArray class only exists to provide the newFromResult method at pre- sent.
Definition: TitleArray.php:42
Represents a title within MediaWiki.
Definition: Title.php:76
Provide primary and replica IDatabase connections.