]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/api/ApiResult.php
MediaWiki 1.15.0
[autoinstallsdev/mediawiki.git] / includes / api / ApiResult.php
1 <?php
2
3 /*
4  * Created on Sep 4, 2006
5  *
6  * API for MediaWiki 1.8+
7  *
8  * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23  * http://www.gnu.org/copyleft/gpl.html
24  */
25
26 if (!defined('MEDIAWIKI')) {
27         // Eclipse helper - will be ignored in production
28         require_once ('ApiBase.php');
29 }
30
31 /**
32  * This class represents the result of the API operations.
33  * It simply wraps a nested array() structure, adding some functions to simplify array's modifications.
34  * As various modules execute, they add different pieces of information to this result,
35  * structuring it as it will be given to the client.
36  *
37  * Each subarray may either be a dictionary - key-value pairs with unique keys,
38  * or lists, where the items are added using $data[] = $value notation.
39  *
40  * There are two special key values that change how XML output is generated:
41  *   '_element' This key sets the tag name for the rest of the elements in the current array.
42  *              It is only inserted if the formatter returned true for getNeedsRawData()
43  *   '*'        This key has special meaning only to the XML formatter, and is outputed as is
44  *                              for all others. In XML it becomes the content of the current element.
45  *
46  * @ingroup API
47  */
48 class ApiResult extends ApiBase {
49
50         private $mData, $mIsRawMode, $mSize, $mCheckingSize;
51
52         /**
53          * Constructor
54          * @param $main ApiMain object
55          */
56         public function __construct($main) {
57                 parent :: __construct($main, 'result');
58                 $this->mIsRawMode = false;
59                 $this->mCheckingSize = true;
60                 $this->reset();
61         }
62
63         /**
64          * Clear the current result data.
65          */
66         public function reset() {
67                 $this->mData = array ();
68                 $this->mSize = 0;
69         }
70
71         /**
72          * Call this function when special elements such as '_element'
73          * are needed by the formatter, for example in XML printing.
74          */
75         public function setRawMode() {
76                 $this->mIsRawMode = true;
77         }
78
79         /**
80          * Returns true whether the formatter requested raw data.
81          * @return bool
82          */
83         public function getIsRawMode() {
84                 return $this->mIsRawMode;
85         }
86
87         /**
88          * Get the result's internal data array (read-only)
89          * @return array
90          */
91         public function getData() {
92                 return $this->mData;
93         }
94         
95         /**
96          * Get the 'real' size of a result item. This means the strlen() of the item,
97          * or the sum of the strlen()s of the elements if the item is an array.
98          * @param $value mixed
99          * @return int
100          */
101         public static function size($value) {
102                 $s = 0;
103                 if(is_array($value))
104                         foreach($value as $v)
105                                 $s += self::size($v);
106                 else if(!is_object($value))
107                         // Objects can't always be cast to string
108                         $s = strlen($value);
109                 return $s;
110         }
111
112         /**
113          * Get the size of the result, i.e. the amount of bytes in it
114          * @return int
115          */
116         public function getSize() {
117                 return $this->mSize;
118         }
119         
120         /**
121          * Disable size checking in addValue(). Don't use this unless you
122          * REALLY know what you're doing. Values added while size checking
123          * was disabled will not be counted (ever)
124          */
125         public function disableSizeCheck() {
126                 $this->mCheckingSize = false;
127         }
128         
129         /**
130          * Re-enable size checking in addValue()
131          */
132         public function enableSizeCheck() {
133                 $this->mCheckingSize = true;
134         }
135
136         /**
137          * Add an output value to the array by name.
138          * Verifies that value with the same name has not been added before.
139          * @param $arr array to add $value to
140          * @param $name string Index of $arr to add $value at
141          * @param $value mixed
142          */
143         public static function setElement(& $arr, $name, $value) {
144                 if ($arr === null || $name === null || $value === null || !is_array($arr) || is_array($name))
145                         ApiBase :: dieDebug(__METHOD__, 'Bad parameter');
146
147                 if (!isset ($arr[$name])) {
148                         $arr[$name] = $value;
149                 }
150                 elseif (is_array($arr[$name]) && is_array($value)) {
151                         $merged = array_intersect_key($arr[$name], $value);
152                         if (!count($merged))
153                                 $arr[$name] += $value;
154                         else
155                                 ApiBase :: dieDebug(__METHOD__, "Attempting to merge element $name");
156                 } else
157                         ApiBase :: dieDebug(__METHOD__, "Attempting to add element $name=$value, existing value is {$arr[$name]}");
158         }
159
160         /**
161          * Adds a content element to an array.
162          * Use this function instead of hardcoding the '*' element.
163          * @param $arr array to add the content element to
164          * @param $subElemName string when present, content element is created
165          *  as a sub item of $arr. Use this parameter to create elements in
166          *  format <elem>text</elem> without attributes
167          */
168         public static function setContent(& $arr, $value, $subElemName = null) {
169                 if (is_array($value))
170                         ApiBase :: dieDebug(__METHOD__, 'Bad parameter');
171                 if (is_null($subElemName)) {
172                         ApiResult :: setElement($arr, '*', $value);
173                 } else {
174                         if (!isset ($arr[$subElemName]))
175                                 $arr[$subElemName] = array ();
176                         ApiResult :: setElement($arr[$subElemName], '*', $value);
177                 }
178         }
179
180         /**
181          * In case the array contains indexed values (in addition to named),
182          * give all indexed values the given tag name. This function MUST be
183          * called on every arrray that has numerical indexes.
184          * @param $arr array
185          * @param $tag string Tag name
186          */
187         public function setIndexedTagName(& $arr, $tag) {
188                 // In raw mode, add the '_element', otherwise just ignore
189                 if (!$this->getIsRawMode())
190                         return;
191                 if ($arr === null || $tag === null || !is_array($arr) || is_array($tag))
192                         ApiBase :: dieDebug(__METHOD__, 'Bad parameter');
193                 // Do not use setElement() as it is ok to call this more than once
194                 $arr['_element'] = $tag;
195         }
196
197         /**
198          * Calls setIndexedTagName() on each sub-array of $arr
199          * @param $arr array
200          * @param $tag string Tag name
201          */
202         public function setIndexedTagName_recursive(&$arr, $tag)
203         {
204                         if(!is_array($arr))
205                                         return;
206                         foreach($arr as &$a)
207                         {
208                                         if(!is_array($a))
209                                                         continue;
210                                         $this->setIndexedTagName($a, $tag);
211                                         $this->setIndexedTagName_recursive($a, $tag);
212                         }
213         }
214
215         /**
216          * Calls setIndexedTagName() on an array already in the result.
217          * Don't specify a path to a value that's not in the result, or
218          * you'll get nasty errors.
219          * @param $path array Path to the array, like addValue()'s $path
220          * @param $tag string
221          */
222         public function setIndexedTagName_internal( $path, $tag ) {
223                 $data = & $this->mData;
224                 foreach((array)$path as $p) {
225                         if ( !isset( $data[$p] ) ) {
226                                 $data[$p] = array();
227                         }
228                         $data = & $data[$p];
229                 }
230                 if(is_null($data))
231                         return;
232                 $this->setIndexedTagName($data, $tag);
233         }
234
235         /**
236          * Add value to the output data at the given path.
237          * Path is an indexed array, each element specifing the branch at which to add the new value
238          * Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value
239          * If $name is empty, the $value is added as a next list element data[] = $value
240          * @return bool True if $value fits in the result, false if not
241          */
242         public function addValue($path, $name, $value) {
243                 global $wgAPIMaxResultSize;
244                 $data = & $this->mData;
245                 if( $this->mCheckingSize ) {
246                         $newsize = $this->mSize + self::size($value);
247                         if($newsize > $wgAPIMaxResultSize)
248                                 return false;
249                         $this->mSize = $newsize;
250                 }
251
252                 if (!is_null($path)) {
253                         if (is_array($path)) {
254                                 foreach ($path as $p) {
255                                         if (!isset ($data[$p]))
256                                                 $data[$p] = array ();
257                                         $data = & $data[$p];
258                                 }
259                         } else {
260                                 if (!isset ($data[$path]))
261                                         $data[$path] = array ();
262                                 $data = & $data[$path];
263                         }
264                 }
265
266                 if (!$name)
267                         $data[] = $value;       // Add list element
268                 else
269                         ApiResult :: setElement($data, $name, $value);  // Add named element
270                 return true;
271         }
272
273         /**
274          * Unset a value previously added to the result set.
275          * Fails silently if the value isn't found.
276          * For parameters, see addValue()
277          * @param $path array
278          * @param $name string
279          */
280         public function unsetValue($path, $name) {
281                 $data = & $this->mData;
282                 if(!is_null($path))
283                         foreach((array)$path as $p) {
284                                 if(!isset($data[$p]))
285                                         return;
286                                 $data = & $data[$p];
287                         }
288                 $this->mSize -= self::size($data[$name]);
289                 unset($data[$name]);
290         }
291
292         /**
293          * Ensure all values in this result are valid UTF-8.
294          */
295         public function cleanUpUTF8()
296         {
297                 array_walk_recursive($this->mData, array('ApiResult', 'cleanUp_helper'));
298         }
299
300         /**
301          * Callback function for cleanUpUTF8()
302          */
303         private static function cleanUp_helper(&$s)
304         {
305                 if(!is_string($s))
306                         return;
307                 $s = UtfNormal::cleanUp($s);
308         }
309
310         public function execute() {
311                 ApiBase :: dieDebug(__METHOD__, 'execute() is not supported on Result object');
312         }
313
314         public function getVersion() {
315                 return __CLASS__ . ': $Id: ApiResult.php 47447 2009-02-18 12:41:28Z tstarling $';
316         }
317 }
318
319 /* For compatibility with PHP versions < 5.1.0, define our own array_intersect_key function. */
320 if (!function_exists('array_intersect_key')) {
321         function array_intersect_key($isec, $keys) {
322                 $argc = func_num_args();
323
324                 if ($argc > 2) {
325                         for ($i = 1; $isec && $i < $argc; $i++) {
326                                 $arr = func_get_arg($i);
327
328                                 foreach (array_keys($isec) as $key) {
329                                         if (!isset($arr[$key]))
330                                                 unset($isec[$key]);
331                                 }
332                         }
333
334                         return $isec;
335                 } else {
336                         $res = array();
337                         foreach (array_keys($isec) as $key) {
338                                 if (isset($keys[$key]))
339                                         $res[$key] = $isec[$key];
340                         }
341
342                         return $res;
343                 }
344         }
345 }