Add support for i8
authorgggeek <giunta.gaetano@gmail.com>
Sun, 26 Jun 2016 19:50:45 +0000 (20:50 +0100)
committergggeek <giunta.gaetano@gmail.com>
Sun, 26 Jun 2016 19:50:45 +0000 (20:50 +0100)
NEWS
doc/manual/phpxmlrpc_manual.adoc
src/Helper/XMLParser.php
src/Server.php
src/Value.php
tests/1ParsingBugsTest.php

diff --git a/NEWS b/NEWS
index 93b4402..9868a1e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,12 @@
+XML-RPC for PHP version 4.1.0 - 2016/6/26
+
+* improved: Added support for receiving <I8> and <EX:I8> integers, sending <I8>
+
+    If php is compiled in 32 bit mode, and an i8 int is received from a 3rd party, and error will be emitted.
+    Integers sent from the library to 3rd parties can be encoded using the i8 tag, but default to using 'int' by default;
+    the developer will have to create values as i8 explicitly if needed.
+    The library does *not* check if an outgoing integer is too big to fit in 4 bytes and convert it to an i8 automatically.
+
 XML-RPC for PHP version 4.0.1 - 2016/3/27
 
 * improved: all of the API documentation has been moved out of the manual and into the source code phpdoc comments
index 50af655..63ac427 100644 (file)
@@ -220,12 +220,16 @@ If you've benefited from the effort that has been put into writing this software
 
 ===== int
 
-The type i4 and i8 are accepted as a synonym
+The type i4 is accepted as a synonym
           for int when creating xmlrpcval objects. The
-          xml parsing code will always convert i4 and i8 to
+          xml parsing code will always convert i4 to
           int: int is regarded
           by this implementation as the canonical name for this type.
 
+The type i8 on the other hand is considered as a separate type.
+          Note that the library will never output integers as 'i8' on its own,
+          even when php is compiled in 64-bit mode.
+
 ===== base64
 
 Base 64 encoding is performed transparently to the caller when
index b4798cc..b7d137f 100644 (file)
@@ -41,6 +41,7 @@ class XMLParser
         'BOOLEAN' => array('VALUE'),
         'I4' => array('VALUE'),
         'I8' => array('VALUE'),
+        'EX:I8' => array('VALUE'),
         'INT' => array('VALUE'),
         'STRING' => array('VALUE'),
         'DOUBLE' => array('VALUE'),
@@ -101,8 +102,17 @@ class XMLParser
                     $this->_xh['lv'] = 1;
                     $this->_xh['php_class'] = null;
                     break;
-                case 'I4':
                 case 'I8':
+                case 'EX:I8':
+                    if (PHP_INT_SIZE === 4) {
+                        /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
+                        $this->_xh['isf'] = 2;
+                        $this->_xh['isf_reason'] = "Received i8 element but php is compiled in 32 bit mode";
+
+                        return;
+                    }
+                // fall through voluntarily
+                case 'I4':
                 case 'INT':
                 case 'STRING':
                 case 'BOOLEAN':
@@ -110,7 +120,7 @@ class XMLParser
                 case 'DATETIME.ISO8601':
                 case 'BASE64':
                     if ($this->_xh['vt'] != 'value') {
-                        //two data elements inside a value: an error occurred!
+                        // two data elements inside a value: an error occurred!
                         $this->_xh['isf'] = 2;
                         $this->_xh['isf_reason'] = "$name element following a {$this->_xh['vt']} element inside a single value";
 
@@ -262,6 +272,7 @@ class XMLParser
                 case 'BOOLEAN':
                 case 'I4':
                 case 'I8':
+                case 'EX:I8':
                 case 'INT':
                 case 'STRING':
                 case 'DOUBLE':
@@ -310,7 +321,7 @@ class XMLParser
                             $this->_xh['value'] = (double)$this->_xh['ac'];
                         }
                     } else {
-                        // we have an I4/INT
+                        // we have an I4/I8/INT
                         // we must check that only 0123456789-<space> are characters here
                         if (!preg_match('/^[+-]?[0123456789 \t]+$/', $this->_xh['ac'])) {
                             /// @todo find a better way of throwing an error than this!
index a0e0cb1..1a52fe6 100644 (file)
@@ -335,7 +335,7 @@ class Server
                             $pt = $p->kindOf();
                         }
                     } else {
-                        $pt = ($in[$n] == 'i4' || $in[$n] == 'i8') ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
+                        $pt = ($in[$n] == 'i4') ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
                     }
 
                     // param index is $n+1, as first member of sig is return type
index d48f0b8..97852b0 100644 (file)
@@ -113,7 +113,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
         }
 
         // coerce booleans into correct values
-        // NB: we should either do it for datetimes, integers and doubles, too,
+        // NB: we should either do it for datetimes, integers, i8 and doubles, too,
         // or just plain remove this check, implemented on booleans only...
         if ($type == static::$xmlrpcBoolean) {
             if (strcasecmp($val, 'true') == 0 || $val == 1 || ($val == true && strcasecmp($val, 'false'))) {
@@ -329,14 +329,10 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
      */
     public function serialize($charsetEncoding = '')
     {
-        // add check? slower, but helps to avoid recursion in serializing broken xmlrpc values...
-        //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
-        //{
         reset($this->me);
         list($typ, $val) = each($this->me);
 
         return '<value>' . $this->serializedata($typ, $val, $charsetEncoding) . "</value>\n";
-        //}
     }
 
     /**
@@ -407,7 +403,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
     /**
      * Returns the type of the xmlrpc value.
      *
-     * For integers, 'int' is always returned in place of 'i4' or 'i8'.
+     * For integers, 'int' is always returned in place of 'i4'. 'i8' is considered a separate type and returned as such
      *
      * @return string
      */
@@ -415,7 +411,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
     {
         reset($this->me);
         list($a,) = each($this->me);
-        if ($a == static::$xmlrpcI4 || $a == static::$xmlrpcI8) {
+        if ($a == static::$xmlrpcI4) {
             $a = static::$xmlrpcInt;
         }
 
@@ -501,7 +497,6 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
         return new \ArrayIterator();
     }
 
-
     public function offsetSet($offset, $value) {
 
         switch ($this->mytype) {
@@ -528,7 +523,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
                 }
                 return;
             case 1:
-// todo: handle i4/i8 vs int
+// todo: handle i4 vs int
                 reset($this->me);
                 list($type,) = each($this->me);
                 if ($type != $offset) {
@@ -549,7 +544,7 @@ class Value implements \Countable, \IteratorAggregate, \ArrayAccess
             case 2:
                 return isset($this->me['array'][$offset]);
             case 1:
-// todo: handle i4/i8 vs int
+// todo: handle i4 vs int
                 return $offset == $this->scalartyp();
             default:
                 return false;
index f32bd2e..343fcb0 100644 (file)
@@ -116,14 +116,18 @@ class ParsingBugsTests extends PHPUnit_Framework_TestCase
 <value><int>01</int></value>
 </member>
 <member>
-<name>float1</name>
-<value><double>01.10</double></value>
-</member>
-<member>
 <name>integer2</name>
 <value><int>+1</int></value>
 </member>
 <member>
+<name>integer3</name>
+<value><i4>1</i4></value>
+</member>
+<member>
+<name>float1</name>
+<value><double>01.10</double></value>
+</member>
+<member>
 <name>float2</name>
 <value><double>+1.10</double></value>
 </member>
@@ -139,15 +143,49 @@ class ParsingBugsTests extends PHPUnit_Framework_TestCase
         $r = $m->parseResponse($fp);
         $v = $r->value();
         $s = $v->structmem('integer1');
-        $t = $v->structmem('float1');
-        $u = $v->structmem('integer2');
-        $w = $v->structmem('float2');
-        $x = $v->structmem('float3');
+        $t = $v->structmem('integer2');
+        $u = $v->structmem('integer3');
+        $x = $v->structmem('float1');
+        $y = $v->structmem('float2');
+        $z = $v->structmem('float3');
         $this->assertEquals(1, $s->scalarval());
-        $this->assertEquals(1.1, $t->scalarval());
+        $this->assertEquals(1, $t->scalarval());
         $this->assertEquals(1, $u->scalarval());
-        $this->assertEquals(1.1, $w->scalarval());
-        $this->assertEquals(-110.0, $x->scalarval());
+
+        $this->assertEquals(1.1, $x->scalarval());
+        $this->assertEquals(1.1, $y->scalarval());
+        $this->assertEquals(-110.0, $z->scalarval());
+    }
+
+    public function testI8()
+    {
+        if (PHP_INT_SIZE == 4 ) {
+            $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
+            return;
+        }
+
+        $m = $this->newMsg('dummy');
+        $fp =
+            '<?xml version="1.0"?>
+<methodResponse>
+<params>
+<param>
+<value>
+<struct>
+<member>
+<name>integer1</name>
+<value><i8>1</i8></value>
+</member>
+</member>
+</struct>
+</value>
+</param>
+</params>
+</methodResponse>';
+        $r = $m->parseResponse($fp);
+        $v = $r->value();
+        $s = $v->structmem('integer1');
+        $this->assertEquals(1, $s->scalarval());
     }
 
     public function testAddScalarToStruct()