]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/PageProps.php
MediaWiki 1.30.2-scripts
[autoinstallsdev/mediawiki.git] / includes / PageProps.php
1 <?php
2 /**
3  * Access to properties of a page.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  */
22 use Wikimedia\ScopedCallback;
23
24 /**
25  * Gives access to properties of a page.
26  *
27  * @since 1.27
28  */
29 class PageProps {
30
31         /**
32          * @var PageProps
33          */
34         private static $instance;
35
36         /**
37          * Overrides the default instance of this class
38          * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined.
39          *
40          * If this method is used it MUST also be called with null after a test to ensure a new
41          * default instance is created next time getInstance is called.
42          *
43          * @since 1.27
44          *
45          * @param PageProps|null $store
46          *
47          * @return ScopedCallback to reset the overridden value
48          * @throws MWException
49          */
50         public static function overrideInstance( PageProps $store = null ) {
51                 if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
52                         throw new MWException(
53                                 'Cannot override ' . __CLASS__ . 'default instance in operation.'
54                         );
55                 }
56                 $previousValue = self::$instance;
57                 self::$instance = $store;
58                 return new ScopedCallback( function () use ( $previousValue ) {
59                         self::$instance = $previousValue;
60                 } );
61         }
62
63         /**
64          * @return PageProps
65          */
66         public static function getInstance() {
67                 if ( self::$instance === null ) {
68                         self::$instance = new self();
69                 }
70                 return self::$instance;
71         }
72
73         /** Cache parameters */
74         const CACHE_TTL = 10; // integer; TTL in seconds
75         const CACHE_SIZE = 100; // integer; max cached pages
76
77         /** Property cache */
78         private $cache = null;
79
80         /**
81          * Create a PageProps object
82          */
83         private function __construct() {
84                 $this->cache = new ProcessCacheLRU( self::CACHE_SIZE );
85         }
86
87         /**
88          * Ensure that cache has at least this size
89          * @param int $size
90          */
91         public function ensureCacheSize( $size ) {
92                 if ( $this->cache->getSize() < $size ) {
93                         $this->cache->resize( $size );
94                 }
95         }
96
97         /**
98          * Given one or more Titles and one or more names of properties,
99          * returns an associative array mapping page ID to property value.
100          * Pages in the provided set of Titles that do not have a value for
101          * the given properties will not appear in the returned array. If a
102          * single Title is provided, it does not need to be passed in an array,
103          * but an array will always be returned. If a single property name is
104          * provided, it does not need to be passed in an array. In that case,
105          * an associative array mapping page ID to property value will be
106          * returned; otherwise, an associative array mapping page ID to
107          * an associative array mapping property name to property value will be
108          * returned. An empty array will be returned if no matching properties
109          * were found.
110          *
111          * @param Title[]|Title $titles
112          * @param string[]|string $propertyNames
113          * @return array associative array mapping page ID to property value
114          */
115         public function getProperties( $titles, $propertyNames ) {
116                 if ( is_array( $propertyNames ) ) {
117                         $gotArray = true;
118                 } else {
119                         $propertyNames = [ $propertyNames ];
120                         $gotArray = false;
121                 }
122
123                 $values = [];
124                 $goodIDs = $this->getGoodIDs( $titles );
125                 $queryIDs = [];
126                 foreach ( $goodIDs as $pageID ) {
127                         foreach ( $propertyNames as $propertyName ) {
128                                 $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
129                                 if ( $propertyValue === false ) {
130                                         $queryIDs[] = $pageID;
131                                         break;
132                                 } else {
133                                         if ( $gotArray ) {
134                                                 $values[$pageID][$propertyName] = $propertyValue;
135                                         } else {
136                                                 $values[$pageID] = $propertyValue;
137                                         }
138                                 }
139                         }
140                 }
141
142                 if ( $queryIDs ) {
143                         $dbr = wfGetDB( DB_REPLICA );
144                         $result = $dbr->select(
145                                 'page_props',
146                                 [
147                                         'pp_page',
148                                         'pp_propname',
149                                         'pp_value'
150                                 ],
151                                 [
152                                         'pp_page' => $queryIDs,
153                                         'pp_propname' => $propertyNames
154                                 ],
155                                 __METHOD__
156                         );
157
158                         foreach ( $result as $row ) {
159                                 $pageID = $row->pp_page;
160                                 $propertyName = $row->pp_propname;
161                                 $propertyValue = $row->pp_value;
162                                 $this->cacheProperty( $pageID, $propertyName, $propertyValue );
163                                 if ( $gotArray ) {
164                                         $values[$pageID][$propertyName] = $propertyValue;
165                                 } else {
166                                         $values[$pageID] = $propertyValue;
167                                 }
168                         }
169                 }
170
171                 return $values;
172         }
173
174         /**
175          * Get all page property values.
176          * Given one or more Titles, returns an associative array mapping page
177          * ID to an associative array mapping property names to property
178          * values. Pages in the provided set of Titles that do not have any
179          * properties will not appear in the returned array. If a single Title
180          * is provided, it does not need to be passed in an array, but an array
181          * will always be returned. An empty array will be returned if no
182          * matching properties were found.
183          *
184          * @param Title[]|Title $titles
185          * @return array associative array mapping page ID to property value array
186          */
187         public function getAllProperties( $titles ) {
188                 $values = [];
189                 $goodIDs = $this->getGoodIDs( $titles );
190                 $queryIDs = [];
191                 foreach ( $goodIDs as $pageID ) {
192                         $pageProperties = $this->getCachedProperties( $pageID );
193                         if ( $pageProperties === false ) {
194                                 $queryIDs[] = $pageID;
195                         } else {
196                                 $values[$pageID] = $pageProperties;
197                         }
198                 }
199
200                 if ( $queryIDs != [] ) {
201                         $dbr = wfGetDB( DB_REPLICA );
202                         $result = $dbr->select(
203                                 'page_props',
204                                 [
205                                         'pp_page',
206                                         'pp_propname',
207                                         'pp_value'
208                                 ],
209                                 [
210                                         'pp_page' => $queryIDs,
211                                 ],
212                                 __METHOD__
213                         );
214
215                         $currentPageID = 0;
216                         $pageProperties = [];
217                         foreach ( $result as $row ) {
218                                 $pageID = $row->pp_page;
219                                 if ( $currentPageID != $pageID ) {
220                                         if ( $pageProperties != [] ) {
221                                                 $this->cacheProperties( $currentPageID, $pageProperties );
222                                                 $values[$currentPageID] = $pageProperties;
223                                         }
224                                         $currentPageID = $pageID;
225                                         $pageProperties = [];
226                                 }
227                                 $pageProperties[$row->pp_propname] = $row->pp_value;
228                         }
229                         if ( $pageProperties != [] ) {
230                                 $this->cacheProperties( $pageID, $pageProperties );
231                                 $values[$pageID] = $pageProperties;
232                         }
233                 }
234
235                 return $values;
236         }
237
238         /**
239          * @param Title[]|Title $titles
240          * @return array array of good page IDs
241          */
242         private function getGoodIDs( $titles ) {
243                 $result = [];
244                 if ( is_array( $titles ) ) {
245                         ( new LinkBatch( $titles ) )->execute();
246
247                         foreach ( $titles as $title ) {
248                                 $pageID = $title->getArticleID();
249                                 if ( $pageID > 0 ) {
250                                         $result[] = $pageID;
251                                 }
252                         }
253                 } else {
254                         $pageID = $titles->getArticleID();
255                         if ( $pageID > 0 ) {
256                                 $result[] = $pageID;
257                         }
258                 }
259                 return $result;
260         }
261
262         /**
263          * Get a property from the cache.
264          *
265          * @param int $pageID page ID of page being queried
266          * @param string $propertyName name of property being queried
267          * @return string|bool property value array or false if not found
268          */
269         private function getCachedProperty( $pageID, $propertyName ) {
270                 if ( $this->cache->has( $pageID, $propertyName, self::CACHE_TTL ) ) {
271                         return $this->cache->get( $pageID, $propertyName );
272                 }
273                 if ( $this->cache->has( 0, $pageID, self::CACHE_TTL ) ) {
274                         $pageProperties = $this->cache->get( 0, $pageID );
275                         if ( isset( $pageProperties[$propertyName] ) ) {
276                                 return $pageProperties[$propertyName];
277                         }
278                 }
279                 return false;
280         }
281
282         /**
283          * Get properties from the cache.
284          *
285          * @param int $pageID page ID of page being queried
286          * @return string|bool property value array or false if not found
287          */
288         private function getCachedProperties( $pageID ) {
289                 if ( $this->cache->has( 0, $pageID, self::CACHE_TTL ) ) {
290                         return $this->cache->get( 0, $pageID );
291                 }
292                 return false;
293         }
294
295         /**
296          * Save a property to the cache.
297          *
298          * @param int $pageID page ID of page being cached
299          * @param string $propertyName name of property being cached
300          * @param mixed $propertyValue value of property
301          */
302         private function cacheProperty( $pageID, $propertyName, $propertyValue ) {
303                 $this->cache->set( $pageID, $propertyName, $propertyValue );
304         }
305
306         /**
307          * Save properties to the cache.
308          *
309          * @param int $pageID page ID of page being cached
310          * @param string[] $pageProperties associative array of page properties to be cached
311          */
312         private function cacheProperties( $pageID, $pageProperties ) {
313                 $this->cache->clear( $pageID );
314                 $this->cache->set( 0, $pageID, $pageProperties );
315         }
316 }