diff --git a/NEWS b/NEWS index 93b44020..9868a1ed 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,12 @@ +XML-RPC for PHP version 4.1.0 - 2016/6/26 + +* improved: Added support for receiving and integers, sending + + 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 diff --git a/doc/manual/phpxmlrpc_manual.adoc b/doc/manual/phpxmlrpc_manual.adoc index 50af655a..63ac4276 100644 --- a/doc/manual/phpxmlrpc_manual.adoc +++ b/doc/manual/phpxmlrpc_manual.adoc @@ -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 diff --git a/src/Helper/XMLParser.php b/src/Helper/XMLParser.php index b4798ccf..b7d137f8 100644 --- a/src/Helper/XMLParser.php +++ b/src/Helper/XMLParser.php @@ -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 @@ public function xmlrpc_se($parser, $name, $attrs, $acceptSingleVals = false) $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 @@ public function xmlrpc_se($parser, $name, $attrs, $acceptSingleVals = false) 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 @@ public function xmlrpc_ee($parser, $name, $rebuildXmlrpcvals = true) case 'BOOLEAN': case 'I4': case 'I8': + case 'EX:I8': case 'INT': case 'STRING': case 'DOUBLE': @@ -310,7 +321,7 @@ public function xmlrpc_ee($parser, $name, $rebuildXmlrpcvals = true) $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- are characters here if (!preg_match('/^[+-]?[0123456789 \t]+$/', $this->_xh['ac'])) { /// @todo find a better way of throwing an error than this! diff --git a/src/Server.php b/src/Server.php index a0e0cb15..1a52fe6c 100644 --- a/src/Server.php +++ b/src/Server.php @@ -335,7 +335,7 @@ protected function verifySignature($in, $sigs) $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 diff --git a/src/Value.php b/src/Value.php index d48f0b8f..97852b05 100644 --- a/src/Value.php +++ b/src/Value.php @@ -113,7 +113,7 @@ public function addScalar($val, $type = 'string') } // 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 @@ protected function serializedata($typ, $val, $charsetEncoding = '') */ 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 '' . $this->serializedata($typ, $val, $charsetEncoding) . "\n"; - //} } /** @@ -407,7 +403,7 @@ public function scalarval() /** * 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 @@ public function scalartyp() { 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 @@ public function getIterator() { return new \ArrayIterator(); } - public function offsetSet($offset, $value) { switch ($this->mytype) { @@ -528,7 +523,7 @@ public function offsetSet($offset, $value) { } 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 @@ public function offsetExists($offset) { 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; diff --git a/tests/1ParsingBugsTest.php b/tests/1ParsingBugsTest.php index f32bd2e5..343fcb03 100644 --- a/tests/1ParsingBugsTest.php +++ b/tests/1ParsingBugsTest.php @@ -116,14 +116,18 @@ public function testValidNumbers() 01 -float1 -01.10 - - integer2 +1 +integer3 +1 + + +float1 +01.10 + + float2 +1.10 @@ -139,15 +143,49 @@ public function testValidNumbers() $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 = + ' + + + + + + +integer1 +1 + + + + + + +'; + $r = $m->parseResponse($fp); + $v = $r->value(); + $s = $v->structmem('integer1'); + $this->assertEquals(1, $s->scalarval()); } public function testAddScalarToStruct()