MediaWiki master
PageProps.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Page;
8
13
20class PageProps {
21 /* max cached pages */
22 private const CACHE_SIZE = 100;
23 /* key group where we store complete per-page data from PageProps::getAllProperties */
24 private const ALL_PROPS_KEY = 0;
25
26 private LinkBatchFactory $linkBatchFactory;
27 private IConnectionProvider $dbProvider;
28 private MapCacheLRU $cache;
29
30 public function __construct(
31 LinkBatchFactory $linkBatchFactory,
32 IConnectionProvider $dbProvider
33 ) {
34 $this->linkBatchFactory = $linkBatchFactory;
35 $this->dbProvider = $dbProvider;
36 $this->cache = new MapCacheLRU( self::CACHE_SIZE );
37 }
38
62 public function getProperties( $titles, $propertyNames ) {
63 if ( is_array( $propertyNames ) ) {
64 $gotArray = true;
65 } else {
66 $propertyNames = [ $propertyNames ];
67 $gotArray = false;
68 }
69
70 $values = [];
71 $goodIDs = $this->getGoodIDs( $titles );
72 $queryIDs = [];
73 foreach ( $goodIDs as $pageID ) {
74 foreach ( $propertyNames as $propertyName ) {
75 $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
76 if ( $propertyValue === null ) {
77 // Unknown, fetch from database
78 $queryIDs[$pageID] = true;
79 // Optimization: Cache absence of results (T297300)
80 // Assume and remember absence, then replace it below if found
81 $this->cache->setField( $pageID, $propertyName, false );
82 } elseif ( $propertyValue !== false ) {
83 if ( $gotArray ) {
84 $values[$pageID][$propertyName] = $propertyValue;
85 } else {
86 $values[$pageID] = $propertyValue;
87 }
88 }
89 }
90 }
91
92 if ( $queryIDs ) {
93 $queryBuilder = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder();
94 $queryBuilder->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
95 ->from( 'page_props' )
96 ->where( [ 'pp_page' => array_keys( $queryIDs ), 'pp_propname' => $propertyNames ] )
97 ->caller( __METHOD__ );
98 $result = $queryBuilder->fetchResultSet();
99
100 foreach ( $result as $row ) {
101 $pageID = $row->pp_page;
102 $propertyName = $row->pp_propname;
103 $propertyValue = $row->pp_value;
104 $this->cache->setField( $pageID, $propertyName, $propertyValue );
105 if ( $gotArray ) {
106 $values[$pageID][$propertyName] = $propertyValue;
107 } else {
108 $values[$pageID] = $propertyValue;
109 }
110 }
111 }
112
113 return $values;
114 }
115
133 public function getAllProperties( $titles ) {
134 $values = [];
135 $goodIDs = $this->getGoodIDs( $titles );
136 $queryIDs = [];
137 foreach ( $goodIDs as $pageID ) {
138 $pageProperties = $this->getCachedProperties( $pageID );
139 if ( $pageProperties === false ) {
140 $queryIDs[] = $pageID;
141 } else {
142 $values[$pageID] = $pageProperties;
143 }
144 }
145
146 if ( $queryIDs ) {
147 $queryBuilder = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder();
148 $queryBuilder->select( [ 'pp_page', 'pp_propname', 'pp_value' ] )
149 ->from( 'page_props' )
150 ->where( [ 'pp_page' => $queryIDs ] )
151 ->caller( __METHOD__ );
152 $result = $queryBuilder->fetchResultSet();
153
154 $currentPageID = 0;
155 $pageProperties = [];
156 foreach ( $result as $row ) {
157 $pageID = $row->pp_page;
158 if ( $currentPageID != $pageID ) {
159 if ( $pageProperties ) {
160 // @phan-suppress-next-line PhanTypeMismatchArgument False positive
161 $this->cacheProperties( $currentPageID, $pageProperties );
162 $values[$currentPageID] = $pageProperties;
163 }
164 $currentPageID = $pageID;
165 $pageProperties = [];
166 }
167 $pageProperties[$row->pp_propname] = $row->pp_value;
168 }
169 if ( $pageProperties ) {
170 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable pageID set when used
171 $this->cacheProperties( $pageID, $pageProperties );
172 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable pageID set when used
173 $values[$pageID] = $pageProperties;
174 }
175 }
176
177 return $values;
178 }
179
184 private function getGoodIDs( $titles ) {
185 $result = [];
186 if ( is_iterable( $titles ) ) {
187 if ( $titles instanceof TitleArrayFromResult ||
188 ( is_array( $titles ) && reset( $titles ) instanceof Title
189 ) ) {
190 // If the first element is a Title, assume all elements are Titles,
191 // and pre-fetch their IDs using a batch query. For PageIdentityValues
192 // or PageStoreRecords, this is not necessary, since they already
193 // know their ID.
194 $this->linkBatchFactory->newLinkBatch( $titles )->execute();
195 }
196
197 foreach ( $titles as $title ) {
198 // Until we only allow ProperPageIdentity, Title objects
199 // can deceive us with an unexpected Special page
200 if ( $title->canExist() ) {
201 $pageID = $title->getId();
202 if ( $pageID > 0 ) {
203 $result[] = $pageID;
204 }
205 }
206 }
207 } else {
208 // Until we only allow ProperPageIdentity, Title objects
209 // can deceive us with an unexpected Special page
210 if ( $titles->canExist() ) {
211 $pageID = $titles->getId();
212 if ( $pageID > 0 ) {
213 $result[] = $pageID;
214 }
215 }
216 }
217 return $result;
218 }
219
227 private function getCachedProperty( $pageID, $propertyName ) {
228 if ( $this->cache->hasField( $pageID, $propertyName ) ) {
229 return $this->cache->getField( $pageID, $propertyName );
230 }
231 if ( $this->cache->hasField( self::ALL_PROPS_KEY, $pageID ) ) {
232 $pageProperties = $this->cache->getField( self::ALL_PROPS_KEY, $pageID );
233 if ( isset( $pageProperties[$propertyName] ) ) {
234 return $pageProperties[$propertyName];
235 }
236 }
237 return null;
238 }
239
246 private function getCachedProperties( $pageID ) {
247 if ( $this->cache->hasField( self::ALL_PROPS_KEY, $pageID ) ) {
248 return $this->cache->getField( self::ALL_PROPS_KEY, $pageID );
249 }
250 return false;
251 }
252
259 private function cacheProperties( $pageID, array $pageProperties ) {
260 // Remove any partial data for this page
261 $this->cache->clear( $pageID );
262 // Set complete data for this page
263 $this->cache->setField( self::ALL_PROPS_KEY, $pageID, $pageProperties );
264 }
265
269 public function clear(): void {
270 $this->cache->clear();
271 }
272}
Factory for LinkBatch objects to batch query page metadata.
Gives access to properties of a page.
Definition PageProps.php:20
__construct(LinkBatchFactory $linkBatchFactory, IConnectionProvider $dbProvider)
Definition PageProps.php:30
getAllProperties( $titles)
Get all page properties of one or more page titles.
getProperties( $titles, $propertyNames)
Fetch one or more properties for one or more Titles.
Definition PageProps.php:62
Represents a title within MediaWiki.
Definition Title.php:69
Store key-value entries in a size-limited in-memory LRU cache.
Provide primary and replica IDatabase connections.