Open Monograph Press  3.3.0
Value.php
1 <?php
2 
3 namespace PhpXmlRpc;
4 
6 
7 class Value implements \Countable, \IteratorAggregate, \ArrayAccess
8 {
9  public static $xmlrpcI4 = "i4";
10  public static $xmlrpcInt = "int";
11  public static $xmlrpcBoolean = "boolean";
12  public static $xmlrpcDouble = "double";
13  public static $xmlrpcString = "string";
14  public static $xmlrpcDateTime = "dateTime.iso8601";
15  public static $xmlrpcBase64 = "base64";
16  public static $xmlrpcArray = "array";
17  public static $xmlrpcStruct = "struct";
18  public static $xmlrpcValue = "undefined";
19  public static $xmlrpcNull = "null";
20 
21  public static $xmlrpcTypes = array(
22  "i4" => 1,
23  "int" => 1,
24  "boolean" => 1,
25  "double" => 1,
26  "string" => 1,
27  "dateTime.iso8601" => 1,
28  "base64" => 1,
29  "array" => 2,
30  "struct" => 3,
31  "null" => 1,
32  );
33 
35  public $me = array();
36  public $mytype = 0;
37  public $_php_class = null;
38 
46  public function __construct($val = -1, $type = '')
47  {
48  // optimization creep - do not call addXX, do it all inline.
49  // downside: booleans will not be coerced anymore
50  if ($val !== -1 || $type != '') {
51  switch ($type) {
52  case '':
53  $this->mytype = 1;
54  $this->me['string'] = $val;
55  break;
56  case 'i4':
57  case 'int':
58  case 'double':
59  case 'string':
60  case 'boolean':
61  case 'dateTime.iso8601':
62  case 'base64':
63  case 'null':
64  $this->mytype = 1;
65  $this->me[$type] = $val;
66  break;
67  case 'array':
68  $this->mytype = 2;
69  $this->me['array'] = $val;
70  break;
71  case 'struct':
72  $this->mytype = 3;
73  $this->me['struct'] = $val;
74  break;
75  default:
76  error_log("XML-RPC: " . __METHOD__ . ": not a known type ($type)");
77  }
78  }
79  }
80 
89  public function addScalar($val, $type = 'string')
90  {
91  $typeOf = null;
92  if (isset(static::$xmlrpcTypes[$type])) {
93  $typeOf = static::$xmlrpcTypes[$type];
94  }
95 
96  if ($typeOf !== 1) {
97  error_log("XML-RPC: " . __METHOD__ . ": not a scalar type ($type)");
98  return 0;
99  }
100 
101  // coerce booleans into correct values
102  // NB: we should either do it for datetimes, integers and doubles, too,
103  // or just plain remove this check, implemented on booleans only...
104  if ($type == static::$xmlrpcBoolean) {
105  if (strcasecmp($val, 'true') == 0 || $val == 1 || ($val == true && strcasecmp($val, 'false'))) {
106  $val = true;
107  } else {
108  $val = false;
109  }
110  }
111 
112  switch ($this->mytype) {
113  case 1:
114  error_log('XML-RPC: ' . __METHOD__ . ': scalar xmlrpc value can have only one value');
115  return 0;
116  case 3:
117  error_log('XML-RPC: ' . __METHOD__ . ': cannot add anonymous scalar to struct xmlrpc value');
118  return 0;
119  case 2:
120  // we're adding a scalar value to an array here
121  $this->me['array'][] = new Value($val, $type);
122 
123  return 1;
124  default:
125  // a scalar, so set the value and remember we're scalar
126  $this->me[$type] = $val;
127  $this->mytype = $typeOf;
128 
129  return 1;
130  }
131  }
132 
142  public function addArray($values)
143  {
144  if ($this->mytype == 0) {
145  $this->mytype = static::$xmlrpcTypes['array'];
146  $this->me['array'] = $values;
147 
148  return 1;
149  } elseif ($this->mytype == 2) {
150  // we're adding to an array here
151  $this->me['array'] = array_merge($this->me['array'], $values);
152 
153  return 1;
154  } else {
155  error_log('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
156  return 0;
157  }
158  }
159 
169  public function addStruct($values)
170  {
171  if ($this->mytype == 0) {
172  $this->mytype = static::$xmlrpcTypes['struct'];
173  $this->me['struct'] = $values;
174 
175  return 1;
176  } elseif ($this->mytype == 3) {
177  // we're adding to a struct here
178  $this->me['struct'] = array_merge($this->me['struct'], $values);
179 
180  return 1;
181  } else {
182  error_log('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
183  return 0;
184  }
185  }
186 
192  public function kindOf()
193  {
194  switch ($this->mytype) {
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  protected function serializedata($typ, $val, $charsetEncoding = '')
210  {
211  $rs = '';
212 
213  if (!isset(static::$xmlrpcTypes[$typ])) {
214  return $rs;
215  }
216 
217  switch (static::$xmlrpcTypes[$typ]) {
218  case 1:
219  switch ($typ) {
220  case static::$xmlrpcBase64:
221  $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
222  break;
223  case static::$xmlrpcBoolean:
224  $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
225  break;
226  case static::$xmlrpcString:
227  // G. Giunta 2005/2/13: do NOT use htmlentities, since
228  // it will produce named html entities, which are invalid xml
229  $rs .= "<${typ}>" . Charset::instance()->encodeEntities($val, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</${typ}>";
230  break;
231  case static::$xmlrpcInt:
232  case static::$xmlrpcI4:
233  $rs .= "<${typ}>" . (int)$val . "</${typ}>";
234  break;
235  case static::$xmlrpcDouble:
236  // avoid using standard conversion of float to string because it is locale-dependent,
237  // and also because the xmlrpc spec forbids exponential notation.
238  // sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
239  // The code below tries its best at keeping max precision while avoiding exp notation,
240  // but there is of course no limit in the number of decimal places to be used...
241  $rs .= "<${typ}>" . preg_replace('/\\.?0+$/', '', number_format((double)$val, 128, '.', '')) . "</${typ}>";
242  break;
243  case static::$xmlrpcDateTime:
244  if (is_string($val)) {
245  $rs .= "<${typ}>${val}</${typ}>";
246  } elseif (is_a($val, 'DateTime')) {
247  $rs .= "<${typ}>" . $val->format('Ymd\TH:i:s') . "</${typ}>";
248  } elseif (is_int($val)) {
249  $rs .= "<${typ}>" . strftime("%Y%m%dT%H:%M:%S", $val) . "</${typ}>";
250  } else {
251  // not really a good idea here: but what shall we output anyway? left for backward compat...
252  $rs .= "<${typ}>${val}</${typ}>";
253  }
254  break;
255  case static::$xmlrpcNull:
256  if (PhpXmlRpc::$xmlrpc_null_apache_encoding) {
257  $rs .= "<ex:nil/>";
258  } else {
259  $rs .= "<nil/>";
260  }
261  break;
262  default:
263  // no standard type value should arrive here, but provide a possibility
264  // for xmlrpc values of unknown type...
265  $rs .= "<${typ}>${val}</${typ}>";
266  }
267  break;
268  case 3:
269  // struct
270  if ($this->_php_class) {
271  $rs .= '<struct php_class="' . $this->_php_class . "\">\n";
272  } else {
273  $rs .= "<struct>\n";
274  }
275  $charsetEncoder = Charset::instance();
276  foreach ($val as $key2 => $val2) {
277  $rs .= '<member><name>' . $charsetEncoder->encodeEntities($key2, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</name>\n";
278  //$rs.=$this->serializeval($val2);
279  $rs .= $val2->serialize($charsetEncoding);
280  $rs .= "</member>\n";
281  }
282  $rs .= '</struct>';
283  break;
284  case 2:
285  // array
286  $rs .= "<array>\n<data>\n";
287  foreach ($val as $element) {
288  //$rs.=$this->serializeval($val[$i]);
289  $rs .= $element->serialize($charsetEncoding);
290  }
291  $rs .= "</data>\n</array>";
292  break;
293  default:
294  break;
295  }
296 
297  return $rs;
298  }
299 
307  public function serialize($charsetEncoding = '')
308  {
309  // add check? slower, but helps to avoid recursion in serializing broken xmlrpc values...
310  //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
311  //{
312  reset($this->me);
313  list($typ, $val) = each($this->me);
314 
315  return '<value>' . $this->serializedata($typ, $val, $charsetEncoding) . "</value>\n";
316  //}
317  }
318 
329  public function structmemexists($key)
330  {
331  return array_key_exists($key, $this->me['struct']);
332  }
333 
344  public function structmem($key)
345  {
346  return $this->me['struct'][$key];
347  }
348 
353  public function structreset()
354  {
355  reset($this->me['struct']);
356  }
357 
365  public function structeach()
366  {
367  return each($this->me['struct']);
368  }
369 
375  public function scalarval()
376  {
377  reset($this->me);
378  list(, $b) = each($this->me);
379 
380  return $b;
381  }
382 
389  public function scalartyp()
390  {
391  reset($this->me);
392  list($a,) = each($this->me);
393  if ($a == static::$xmlrpcI4) {
394  $a = static::$xmlrpcInt;
395  }
396 
397  return $a;
398  }
399 
409  public function arraymem($key)
410  {
411  return $this->me['array'][$key];
412  }
413 
421  public function arraysize()
422  {
423  return count($this->me['array']);
424  }
425 
433  public function structsize()
434  {
435  return count($this->me['struct']);
436  }
437 
446  public function count()
447  {
448  switch ($this->mytype) {
449  case 3:
450  return count($this->me['struct']);
451  case 2:
452  return count($this->me['array']);
453  case 1:
454  return 1;
455  default:
456  return 0;
457  }
458  }
459 
465  public function getIterator() {
466  switch ($this->mytype) {
467  case 3:
468  return new \ArrayIterator($this->me['struct']);
469  case 2:
470  return new \ArrayIterator($this->me['array']);
471  case 1:
472  return new \ArrayIterator($this->me);
473  default:
474  return new \ArrayIterator();
475  }
476  return new \ArrayIterator();
477  }
478 
479 
480  public function offsetSet($offset, $value) {
481 
482  switch ($this->mytype) {
483  case 3:
484  if (!($value instanceof \PhpXmlRpc\Value)) {
485  throw new \Exception('It is only possible to add Value objects to an XML-RPC Struct');
486  }
487  if (is_null($offset)) {
488  // disallow struct members with empty names
489  throw new \Exception('It is not possible to add anonymous members to an XML-RPC Struct');
490  } else {
491  $this->me['struct'][$offset] = $value;
492  }
493  return;
494  case 2:
495  if (!($value instanceof \PhpXmlRpc\Value)) {
496  throw new \Exception('It is only possible to add Value objects to an XML-RPC Array');
497  }
498  if (is_null($offset)) {
499  $this->me['array'][] = $value;
500  } else {
501  // nb: we are not checking that $offset is above the existing array range...
502  $this->me['array'][$offset] = $value;
503  }
504  return;
505  case 1:
506 // todo: handle i4 vs int
507  reset($this->me);
508  list($type,) = each($this->me);
509  if ($type != $offset) {
510  throw new \Exception('');
511  }
512  $this->me[$type] = $value;
513  return;
514  default:
515  // it would be nice to allow empty values to be be turned into non-empty ones this way, but we miss info to do so
516  throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be set using array index");
517  }
518  }
519 
520  public function offsetExists($offset) {
521  switch ($this->mytype) {
522  case 3:
523  return isset($this->me['struct'][$offset]);
524  case 2:
525  return isset($this->me['array'][$offset]);
526  case 1:
527 // todo: handle i4 vs int
528  return $offset == $this->scalartyp();
529  default:
530  return false;
531  }
532  }
533 
534  public function offsetUnset($offset) {
535  switch ($this->mytype) {
536  case 3:
537  unset($this->me['struct'][$offset]);
538  return;
539  case 2:
540  unset($this->me['array'][$offset]);
541  return;
542  case 1:
543  // can not remove value from a scalar
544  throw new \Exception("XML-RPC Value is of type 'scalar' and its value can not be unset using array index");
545  default:
546  throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be unset using array index");
547  }
548  }
549 
550  public function offsetGet($offset) {
551  switch ($this->mytype) {
552  case 3:
553  return isset($this->me['struct'][$offset]) ? $this->me['struct'][$offset] : null;
554  case 2:
555  return isset($this->me['array'][$offset]) ? $this->me['array'][$offset] : null;
556  case 1:
557 // on bad type: null or exception?
558  reset($this->me);
559  list($type, $value) = each($this->me);
560  return $type == $offset ? $value : null;
561  default:
562 // return null or exception?
563  throw new \Exception("XML-RPC Value is of type 'undef' and can not be accessed using array index");
564  }
565  }
566 }
PhpXmlRpc\Value\arraymem
arraymem($key)
Definition: Value.php:409
PhpXmlRpc\Helper\Charset
Definition: Charset.php:7
PhpXmlRpc\Value\arraysize
arraysize()
Definition: Value.php:421
PhpXmlRpc\Value\structmem
structmem($key)
Definition: Value.php:344
PhpXmlRpc\Value\addArray
addArray($values)
Definition: Value.php:142
PhpXmlRpc\Value\structsize
structsize()
Definition: Value.php:433
PhpXmlRpc\Value\count
count()
Definition: Value.php:446
PhpXmlRpc\Value\__construct
__construct($val=-1, $type='')
Definition: Value.php:46
PhpXmlRpc\Value\structeach
structeach()
Definition: Value.php:365
PhpXmlRpc\Value\scalartyp
scalartyp()
Definition: Value.php:389
PhpXmlRpc\Value\structmemexists
structmemexists($key)
Definition: Value.php:329
PhpXmlRpc\Value\addScalar
addScalar($val, $type='string')
Definition: Value.php:89
PhpXmlRpc\Value\offsetExists
offsetExists($offset)
Definition: Value.php:520
PhpXmlRpc\Value\offsetGet
offsetGet($offset)
Definition: Value.php:550
PhpXmlRpc\Value\offsetSet
offsetSet($offset, $value)
Definition: Value.php:480
PhpXmlRpc\Value
Definition: Value.php:7
PhpXmlRpc\Value\offsetUnset
offsetUnset($offset)
Definition: Value.php:534
PhpXmlRpc\Value\scalarval
scalarval()
Definition: Value.php:375
PhpXmlRpc\Value\structreset
structreset()
Definition: Value.php:353
PhpXmlRpc\Value\serializedata
serializedata($typ, $val, $charsetEncoding='')
Definition: Value.php:209
PhpXmlRpc\Value\addStruct
addStruct($values)
Definition: Value.php:169
PhpXmlRpc\Value\getIterator
getIterator()
Definition: Value.php:465
PhpXmlRpc\Value\serialize
serialize($charsetEncoding='')
Definition: Value.php:307
PhpXmlRpc
Definition: Autoloader.php:3
PhpXmlRpc\Value\kindOf
kindOf()
Definition: Value.php:192