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