Splitted classes from xmlrpc.php to separate files
[plcapi.git] / lib / xmlrpcval.php
1 <?php
2
3 class xmlrpcval
4 {
5     var $me=array();
6     var $mytype=0;
7     var $_php_class=null;
8
9     /**
10     * @param mixed $val
11     * @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
12     */
13     function xmlrpcval($val=-1, $type='')
14     {
15         /// @todo: optimization creep - do not call addXX, do it all inline.
16         /// downside: booleans will not be coerced anymore
17         if($val!==-1 || $type!='')
18         {
19             // optimization creep: inlined all work done by constructor
20             switch($type)
21             {
22                 case '':
23                     $this->mytype=1;
24                     $this->me['string']=$val;
25                     break;
26                 case 'i4':
27                 case 'int':
28                 case 'double':
29                 case 'string':
30                 case 'boolean':
31                 case 'dateTime.iso8601':
32                 case 'base64':
33                 case 'null':
34                     $this->mytype=1;
35                     $this->me[$type]=$val;
36                     break;
37                 case 'array':
38                     $this->mytype=2;
39                     $this->me['array']=$val;
40                     break;
41                 case 'struct':
42                     $this->mytype=3;
43                     $this->me['struct']=$val;
44                     break;
45                 default:
46                     error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
47             }
48             /*if($type=='')
49             {
50                 $type='string';
51             }
52             if($GLOBALS['xmlrpcTypes'][$type]==1)
53             {
54                 $this->addScalar($val,$type);
55             }
56             elseif($GLOBALS['xmlrpcTypes'][$type]==2)
57             {
58                 $this->addArray($val);
59             }
60             elseif($GLOBALS['xmlrpcTypes'][$type]==3)
61             {
62                 $this->addStruct($val);
63             }*/
64         }
65     }
66
67     /**
68     * Add a single php value to an (unitialized) xmlrpcval
69     * @param mixed $val
70     * @param string $type
71     * @return int 1 or 0 on failure
72     */
73     function addScalar($val, $type='string')
74     {
75         $xmlrpc = Xmlrpc::instance();
76
77         $typeof = null;
78         if(isset($xmlrpc->xmlrpcTypes[$type])) {
79             $typeof = $xmlrpc->xmlrpcTypes[$type];
80         }
81
82         if($typeof!=1)
83         {
84             error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
85             return 0;
86         }
87
88         // coerce booleans into correct values
89         // NB: we should either do it for datetimes, integers and doubles, too,
90         // or just plain remove this check, implemented on booleans only...
91         if($type==$xmlrpc->xmlrpcBoolean)
92         {
93             if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
94             {
95                 $val=true;
96             }
97             else
98             {
99                 $val=false;
100             }
101         }
102
103         switch($this->mytype)
104         {
105             case 1:
106                 error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
107                 return 0;
108             case 3:
109                 error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
110                 return 0;
111             case 2:
112                 // we're adding a scalar value to an array here
113                 //$ar=$this->me['array'];
114                 //$ar[]=new xmlrpcval($val, $type);
115                 //$this->me['array']=$ar;
116                 // Faster (?) avoid all the costly array-copy-by-val done here...
117                 $this->me['array'][]=new xmlrpcval($val, $type);
118                 return 1;
119             default:
120                 // a scalar, so set the value and remember we're scalar
121                 $this->me[$type]=$val;
122                 $this->mytype=$typeof;
123                 return 1;
124         }
125     }
126
127     /**
128     * Add an array of xmlrpcval objects to an xmlrpcval
129     * @param array $vals
130     * @return int 1 or 0 on failure
131     * @access public
132     *
133     * @todo add some checking for $vals to be an array of xmlrpcvals?
134     */
135     function addArray($vals)
136     {
137         $xmlrpc = Xmlrpc::instance();
138         if($this->mytype==0)
139         {
140             $this->mytype=$xmlrpc->xmlrpcTypes['array'];
141             $this->me['array']=$vals;
142             return 1;
143         }
144         elseif($this->mytype==2)
145         {
146             // we're adding to an array here
147             $this->me['array'] = array_merge($this->me['array'], $vals);
148             return 1;
149         }
150         else
151         {
152             error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
153             return 0;
154         }
155     }
156
157     /**
158     * Add an array of named xmlrpcval objects to an xmlrpcval
159     * @param array $vals
160     * @return int 1 or 0 on failure
161     * @access public
162     *
163     * @todo add some checking for $vals to be an array?
164     */
165     function addStruct($vals)
166     {
167         $xmlrpc = Xmlrpc::instance();
168
169         if($this->mytype==0)
170         {
171             $this->mytype=$xmlrpc->xmlrpcTypes['struct'];
172             $this->me['struct']=$vals;
173             return 1;
174         }
175         elseif($this->mytype==3)
176         {
177             // we're adding to a struct here
178             $this->me['struct'] = array_merge($this->me['struct'], $vals);
179             return 1;
180         }
181         else
182         {
183             error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
184             return 0;
185         }
186     }
187
188     // poor man's version of print_r ???
189     // DEPRECATED!
190     function dump($ar)
191     {
192         foreach($ar as $key => $val)
193         {
194             echo "$key => $val<br />";
195             if($key == 'array')
196             {
197                 while(list($key2, $val2) = each($val))
198                 {
199                     echo "-- $key2 => $val2<br />";
200                 }
201             }
202         }
203     }
204
205     /**
206     * Returns a string containing "struct", "array" or "scalar" describing the base type of the value
207     * @return string
208     * @access public
209     */
210     function kindOf()
211     {
212         switch($this->mytype)
213         {
214             case 3:
215                 return 'struct';
216                 break;
217             case 2:
218                 return 'array';
219                 break;
220             case 1:
221                 return 'scalar';
222                 break;
223             default:
224                 return 'undef';
225         }
226     }
227
228     /**
229     * @access private
230     */
231     function serializedata($typ, $val, $charset_encoding='')
232     {
233         $xmlrpc = Xmlrpc::instance();
234         $rs='';
235
236         if(!isset($xmlrpc->xmlrpcTypes[$typ])) {
237             return $rs;
238         }
239
240         switch($xmlrpc->xmlrpcTypes[$typ])
241         {
242             case 1:
243                 switch($typ)
244                 {
245                     case $xmlrpc->xmlrpcBase64:
246                         $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
247                         break;
248                     case $xmlrpc->xmlrpcBoolean:
249                         $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
250                         break;
251                     case $xmlrpc->xmlrpcString:
252                         // G. Giunta 2005/2/13: do NOT use htmlentities, since
253                         // it will produce named html entities, which are invalid xml
254                         $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $xmlrpc->xmlrpc_internalencoding, $charset_encoding). "</${typ}>";
255                         break;
256                     case $xmlrpc->xmlrpcInt:
257                     case $xmlrpc->xmlrpcI4:
258                         $rs.="<${typ}>".(int)$val."</${typ}>";
259                         break;
260                     case $xmlrpc->xmlrpcDouble:
261                         // avoid using standard conversion of float to string because it is locale-dependent,
262                         // and also because the xmlrpc spec forbids exponential notation.
263                         // sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
264                         // The code below tries its best at keeping max precision while avoiding exp notation,
265                         // but there is of course no limit in the number of decimal places to be used...
266                         $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
267                         break;
268                     case $xmlrpc->xmlrpcDateTime:
269                         if (is_string($val))
270                         {
271                             $rs.="<${typ}>${val}</${typ}>";
272                         }
273                         else if(is_a($val, 'DateTime'))
274                         {
275                             $rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
276                         }
277                         else if(is_int($val))
278                         {
279                             $rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
280                         }
281                         else
282                         {
283                             // not really a good idea here: but what shall we output anyway? left for backward compat...
284                             $rs.="<${typ}>${val}</${typ}>";
285                         }
286                         break;
287                     case $xmlrpc->xmlrpcNull:
288                         if ($xmlrpc->xmlrpc_null_apache_encoding)
289                         {
290                             $rs.="<ex:nil/>";
291                         }
292                         else
293                         {
294                             $rs.="<nil/>";
295                         }
296                         break;
297                     default:
298                         // no standard type value should arrive here, but provide a possibility
299                         // for xmlrpcvals of unknown type...
300                         $rs.="<${typ}>${val}</${typ}>";
301                 }
302                 break;
303             case 3:
304                 // struct
305                 if ($this->_php_class)
306                 {
307                     $rs.='<struct php_class="' . $this->_php_class . "\">\n";
308                 }
309                 else
310                 {
311                     $rs.="<struct>\n";
312                 }
313                 foreach($val as $key2 => $val2)
314                 {
315                     $rs.='<member><name>'.xmlrpc_encode_entitites($key2, $xmlrpc->xmlrpc_internalencoding, $charset_encoding)."</name>\n";
316                     //$rs.=$this->serializeval($val2);
317                     $rs.=$val2->serialize($charset_encoding);
318                     $rs.="</member>\n";
319                 }
320                 $rs.='</struct>';
321                 break;
322             case 2:
323                 // array
324                 $rs.="<array>\n<data>\n";
325                 for($i=0; $i<count($val); $i++)
326                 {
327                     //$rs.=$this->serializeval($val[$i]);
328                     $rs.=$val[$i]->serialize($charset_encoding);
329                 }
330                 $rs.="</data>\n</array>";
331                 break;
332             default:
333                 break;
334         }
335         return $rs;
336     }
337
338     /**
339     * Returns xml representation of the value. XML prologue not included
340     * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
341     * @return string
342     * @access public
343     */
344     function serialize($charset_encoding='')
345     {
346         // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
347         //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
348         //{
349             reset($this->me);
350             list($typ, $val) = each($this->me);
351             return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
352         //}
353     }
354
355     // DEPRECATED
356     function serializeval($o)
357     {
358         // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
359         //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
360         //{
361             $ar=$o->me;
362             reset($ar);
363             list($typ, $val) = each($ar);
364             return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
365         //}
366     }
367
368     /**
369     * Checks whether a struct member with a given name is present.
370     * Works only on xmlrpcvals of type struct.
371     * @param string $m the name of the struct member to be looked up
372     * @return boolean
373     * @access public
374     */
375     function structmemexists($m)
376     {
377         return array_key_exists($m, $this->me['struct']);
378     }
379
380     /**
381     * Returns the value of a given struct member (an xmlrpcval object in itself).
382     * Will raise a php warning if struct member of given name does not exist
383     * @param string $m the name of the struct member to be looked up
384     * @return xmlrpcval
385     * @access public
386     */
387     function structmem($m)
388     {
389         return $this->me['struct'][$m];
390     }
391
392     /**
393     * Reset internal pointer for xmlrpcvals of type struct.
394     * @access public
395     */
396     function structreset()
397     {
398         reset($this->me['struct']);
399     }
400
401     /**
402     * Return next member element for xmlrpcvals of type struct.
403     * @return xmlrpcval
404     * @access public
405     */
406     function structeach()
407     {
408         return each($this->me['struct']);
409     }
410
411     // DEPRECATED! this code looks like it is very fragile and has not been fixed
412     // for a long long time. Shall we remove it for 2.0?
413     function getval()
414     {
415         // UNSTABLE
416         reset($this->me);
417         list($a,$b)=each($this->me);
418         // contributed by I Sofer, 2001-03-24
419         // add support for nested arrays to scalarval
420         // i've created a new method here, so as to
421         // preserve back compatibility
422
423         if(is_array($b))
424         {
425             @reset($b);
426             while(list($id,$cont) = @each($b))
427             {
428                 $b[$id] = $cont->scalarval();
429             }
430         }
431
432         // add support for structures directly encoding php objects
433         if(is_object($b))
434         {
435             $t = get_object_vars($b);
436             @reset($t);
437             while(list($id,$cont) = @each($t))
438             {
439                 $t[$id] = $cont->scalarval();
440             }
441             @reset($t);
442             while(list($id,$cont) = @each($t))
443             {
444                 @$b->$id = $cont;
445             }
446         }
447         // end contrib
448         return $b;
449     }
450
451     /**
452     * Returns the value of a scalar xmlrpcval
453     * @return mixed
454     * @access public
455     */
456     function scalarval()
457     {
458         reset($this->me);
459         list(,$b)=each($this->me);
460         return $b;
461     }
462
463     /**
464     * Returns the type of the xmlrpcval.
465     * For integers, 'int' is always returned in place of 'i4'
466     * @return string
467     * @access public
468     */
469     function scalartyp()
470     {
471         $xmlrpc = Xmlrpc::instance();
472
473         reset($this->me);
474         list($a,)=each($this->me);
475         if($a==$xmlrpc->xmlrpcI4)
476         {
477             $a=$xmlrpc->xmlrpcInt;
478         }
479         return $a;
480     }
481
482     /**
483     * Returns the m-th member of an xmlrpcval of struct type
484     * @param integer $m the index of the value to be retrieved (zero based)
485     * @return xmlrpcval
486     * @access public
487     */
488     function arraymem($m)
489     {
490         return $this->me['array'][$m];
491     }
492
493     /**
494     * Returns the number of members in an xmlrpcval of array type
495     * @return integer
496     * @access public
497     */
498     function arraysize()
499     {
500         return count($this->me['array']);
501     }
502
503     /**
504     * Returns the number of members in an xmlrpcval of struct type
505     * @return integer
506     * @access public
507     */
508     function structsize()
509     {
510         return count($this->me['struct']);
511     }
512 }
513
514 ?>