Merge "Http::getProxy() method to get proxy configuration"
[lhc/web/wiklou.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
23 /**
24 * Gives access to properties of a page.
25 *
26 * @since 1.27
27 *
28 */
29 class PageProps {
30
31 /**
32 * @var PageProps
33 */
34 private static $instance;
35
36 /**
37 * @return PageProps
38 */
39 public static function getInstance() {
40 if ( self::$instance === null ) {
41 self::$instance = new self();
42 }
43 return self::$instance;
44 }
45
46 /** Cache parameters */
47 const CACHE_TTL = 10; // integer; TTL in seconds
48 const CACHE_SIZE = 100; // integer; max cached pages
49
50 /** Property cache */
51 private $cache = null;
52
53 /**
54 * Create a PageProps object
55 */
56 private function __construct() {
57 $this->cache = new ProcessCacheLRU( self::CACHE_SIZE );
58 }
59
60 /**
61 * Given one or more Titles and one or more names of properties,
62 * returns an associative array mapping page ID to property value.
63 * Pages in the provided set of Titles that do not have a value for
64 * the given properties will not appear in the returned array. If a
65 * single Title is provided, it does not need to be passed in an array,
66 * but an array will always be returned. If a single property name is
67 * provided, it does not need to be passed in an array. In that case,
68 * an associtive array mapping page ID to property value will be
69 * returned; otherwise, an associative array mapping page ID to
70 * an associative array mapping property name to property value will be
71 * returned. An empty array will be returned if no matching properties
72 * were found.
73 *
74 * @param Title[]|Title $titles
75 * @param string[]|string $propertyNames
76 * @return array associative array mapping page ID to property value
77 */
78 public function getProperties( $titles, $propertyNames ) {
79 if ( is_array( $propertyNames ) ) {
80 $gotArray = true;
81 } else {
82 $propertyNames = [ $propertyNames ];
83 $gotArray = false;
84 }
85
86 $values = [];
87 $goodIDs = $this->getGoodIDs( $titles );
88 $queryIDs = [];
89 foreach ( $goodIDs as $pageID ) {
90 foreach ( $propertyNames as $propertyName ) {
91 $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
92 if ( $propertyValue === false ) {
93 $queryIDs[] = $pageID;
94 break;
95 } else {
96 if ( $gotArray ) {
97 $values[$pageID][$propertyName] = $propertyValue;
98 } else {
99 $values[$pageID] = $propertyValue;
100 }
101 }
102 }
103 }
104
105 if ( $queryIDs ) {
106 $dbr = wfGetDB( DB_SLAVE );
107 $result = $dbr->select(
108 'page_props',
109 [
110 'pp_page',
111 'pp_propname',
112 'pp_value'
113 ],
114 [
115 'pp_page' => $queryIDs,
116 'pp_propname' => $propertyNames
117 ],
118 __METHOD__
119 );
120
121 foreach ( $result as $row ) {
122 $pageID = $row->pp_page;
123 $propertyName = $row->pp_propname;
124 $propertyValue = $row->pp_value;
125 $this->cacheProperty( $pageID, $propertyName, $propertyValue );
126 if ( $gotArray ) {
127 $values[$pageID][$propertyName] = $propertyValue;
128 } else {
129 $values[$pageID] = $propertyValue;
130 }
131 }
132 }
133
134 return $values;
135 }
136
137 /**
138 * Get all page property values.
139 * Given one or more Titles, returns an associative array mapping page
140 * ID to an associative array mapping property names to property
141 * values. Pages in the provided set of Titles that do not have any
142 * properties will not appear in the returned array. If a single Title
143 * is provided, it does not need to be passed in an array, but an array
144 * will always be returned. An empty array will be returned if no
145 * matching properties were found.
146 *
147 * @param Title[]|Title $titles
148 * @return array associative array mapping page ID to property value array
149 */
150 public function getAllProperties( $titles ) {
151 $values = [];
152 $goodIDs = $this->getGoodIDs( $titles );
153 $queryIDs = [];
154 foreach ( $goodIDs as $pageID ) {
155 $pageProperties = $this->getCachedProperties( $pageID );
156 if ( $pageProperties === false ) {
157 $queryIDs[] = $pageID;
158 } else {
159 $values[$pageID] = $pageProperties;
160 }
161 }
162
163 if ( $queryIDs != [] ) {
164 $dbr = wfGetDB( DB_SLAVE );
165 $result = $dbr->select(
166 'page_props',
167 [
168 'pp_page',
169 'pp_propname',
170 'pp_value'
171 ],
172 [
173 'pp_page' => $queryIDs,
174 ],
175 __METHOD__
176 );
177
178 $currentPageID = 0;
179 $pageProperties = [];
180 foreach ( $result as $row ) {
181 $pageID = $row->pp_page;
182 if ( $currentPageID != $pageID ) {
183 if ( $pageProperties != [] ) {
184 $this->cacheProperties( $currentPageID, $pageProperties );
185 $values[$currentPageID] = $pageProperties;
186 }
187 $currentPageID = $pageID;
188 $pageProperties = [];
189 }
190 $pageProperties[$row->pp_propname] = $row->pp_value;
191 }
192 if ( $pageProperties != [] ) {
193 $this->cacheProperties( $pageID, $pageProperties );
194 $values[$pageID] = $pageProperties;
195 }
196 }
197
198 return $values;
199 }
200
201 /**
202 * @param Title[]|Title $titles
203 * @return array array of good page IDs
204 */
205 private function getGoodIDs( $titles ) {
206 $result = [];
207 if ( is_array( $titles ) ) {
208 foreach ( $titles as $title ) {
209 $pageID = $title->getArticleID();
210 if ( $pageID > 0 ) {
211 $result[] = $pageID;
212 }
213 }
214 } else {
215 $pageID = $titles->getArticleID();
216 if ( $pageID > 0 ) {
217 $result[] = $pageID;
218 }
219 }
220 return $result;
221 }
222
223 /**
224 * Get a property from the cache.
225 *
226 * @param int $pageID page ID of page being queried
227 * @param string $propertyName name of property being queried
228 * @return string|bool property value array or false if not found
229 */
230 private function getCachedProperty( $pageID, $propertyName ) {
231 if ( $this->cache->has( $pageID, $propertyName, self::CACHE_TTL ) ) {
232 return $this->cache->get( $pageID, $propertyName );
233 }
234 if ( $this->cache->has( 0, $pageID, self::CACHE_TTL ) ) {
235 $pageProperties = $this->cache->get( 0, $pageID );
236 if ( isset( $pageProperties[$propertyName] ) ) {
237 return $pageProperties[$propertyName];
238 }
239 }
240 return false;
241 }
242
243 /**
244 * Get properties from the cache.
245 *
246 * @param int $pageID page ID of page being queried
247 * @return string|bool property value array or false if not found
248 */
249 private function getCachedProperties( $pageID ) {
250 if ( $this->cache->has( 0, $pageID, self::CACHE_TTL ) ) {
251 return $this->cache->get( 0, $pageID );
252 }
253 return false;
254 }
255
256 /**
257 * Save a property to the cache.
258 *
259 * @param int $pageID page ID of page being cached
260 * @param string $propertyName name of property being cached
261 * @param mixed $propertyValue value of property
262 */
263 private function cacheProperty( $pageID, $propertyName, $propertyValue ) {
264 $this->cache->set( $pageID, $propertyName, $propertyValue );
265 }
266
267 /**
268 * Save properties to the cache.
269 *
270 * @param int $pageID page ID of page being cached
271 * @param string[] $pageProperties associative array of page properties to be cached
272 */
273 private function cacheProperties( $pageID, $pageProperties ) {
274 $this->cache->clear( $pageID );
275 $this->cache->set( 0, $pageID, $pageProperties );
276 }
277 }