Implement interface ArrayAccess in the Value class
authorgggeek <giunta.gaetano@gmail.com>
Sun, 12 Jul 2015 17:59:39 +0000 (18:59 +0100)
committergggeek <giunta.gaetano@gmail.com>
Sun, 12 Jul 2015 17:59:39 +0000 (18:59 +0100)
NEWS
debugger/action.php
demo/client/agesort.php
demo/client/introspect.php
demo/server/server.php
doc/api_changes_v4.md
src/Client.php
src/Encoder.php
src/Request.php
src/Server.php
src/Value.php

diff --git a/NEWS b/NEWS
index 3b9b53f..b35859b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@ PLEASE READ CAREFULLY THE NOTES BELOW to insure a smooth upgrade.
   All php classes have been renamed and moved to separate files.
   Class autoloading can now be done in accord with the PSR-4 standard.
   All global variables and global functions have been removed.
+  Iterating over xmlrpc value objects is now easier thank to support for ArrayAccess and Traversable interfaces.
 
   Backward compatibility is maintained via lib/xmlrpc.inc, lib/xmlrpcs.inc and lib/xmlrpc_wrappers.inc.
   For more details, head on to doc/api_changes_v4.md
index 5427f67..3b01444 100644 (file)
@@ -287,8 +287,9 @@ if ($action) {
                         $max = $v->count();
                         echo "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n";
                         echo "<thead>\n<tr><th>Method ($max)</th><th>Description</th></tr>\n</thead>\n<tbody>\n";
-                        for ($i = 0; $i < $max; $i++) {
-                            $rec = $v->arraymem($i);
+                        //for ($i = 0; $i < $max; $i++) {
+                        foreach($v as $i => $rec) {
+                            //$rec = $v->arraymem($i);
                             if ($i % 2) {
                                 $class = ' class="oddrow"';
                             } else {
@@ -354,7 +355,8 @@ if ($action) {
                     if ($r2->kindOf() != "array") {
                         echo "<tr><td class=\"oddrow\">Signature</td><td class=\"oddrow\">Unknown</td><td class=\"oddrow\">&nbsp;</td></tr>\n";
                     } else {
-                        for ($i = 0; $i < $r2->arraysize(); $i++) {
+                        //for ($i = 0; $i < $r2->arraysize(); $i++) {
+                        foreach($r2 as $i => $x) {
                             $payload = "";
                             $alt_payload = "";
                             if ($i + 1 % 2) {
@@ -363,13 +365,16 @@ if ($action) {
                                 $class = ' class="evenrow"';
                             }
                             echo "<tr><td$class>Signature&nbsp;" . ($i + 1) . "</td><td$class>";
-                            $x = $r2->arraymem($i);
+                            //$x = $r2->arraymem($i);
                             if ($x->kindOf() == "array") {
-                                $ret = $x->arraymem(0);
+                                //$ret = $x->arraymem(0);
+                                $ret = $x[0];
                                 echo "<code>OUT:&nbsp;" . htmlspecialchars($ret->scalarval(), ENT_COMPAT, \PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding) . "<br />IN: (";
                                 if ($x->count() > 1) {
-                                    for ($k = 1; $k < $x->arraysize(); $k++) {
-                                        $y = $x->arraymem($k);
+                                    foreach($x as $k => $y) {
+                                        if ($k == 0) continue;
+                                    //for ($k = 1; $k < $x->arraysize(); $k++) {
+                                        //$y = $x->arraymem($k);
                                         echo htmlspecialchars($y->scalarval(), ENT_COMPAT, \PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding);
                                         if ($wstype != 1) {
                                             $type = $y->scalarval();
index b21bf89..0b6aa9a 100644 (file)
@@ -50,8 +50,10 @@ if (!$resp->faultCode()) {
     print "The server gave me these results:<pre>";
     $value = $resp->value();
     foreach ($value as $struct) {
-        $name = $struct->structmem("name");
-        $age = $struct->structmem("age");
+        //$name = $struct->structmem("name");
+        $name = $struct["name"];
+        //$age = $struct->structmem("age");
+        $age = $struct["age"];
         print htmlspecialchars($name->scalarval()) . ", " . htmlspecialchars($age->scalarval()) . "\n";
     }
 
index f25540e..5c35950 100644 (file)
@@ -60,12 +60,14 @@ if ($resp->faultCode()) {
             $val = $rs[1]->value();
             if ($val->kindOf() == "array") {
                 foreach ($val as $x) {
-                    $ret = $x->arraymem(0);
+                    //$ret = $x->arraymem(0);
+                    $ret = $x[0];
                     print "<code>" . $ret->scalarval() . " "
                         . $methodName->scalarval() . "(";
                     if ($x->count() > 1) {
                         for ($k = 1; $k < $x->count(); $k++) {
-                            $y = $x->arraymem($k);
+                            //$y = $x->arraymem($k);
+                            $y = $x[$k];
                             print $y->scalarval();
                             if ($k < $x->count() - 1) {
                                 print ", ";
index 6c36717..d060d97 100644 (file)
@@ -542,8 +542,10 @@ function v1_moderateSizeArrayCheck($req)
 {
     $ar = $req->getParam(0);
     $sz = $ar->count();
-    $first = $ar->arraymem(0);
-    $last = $ar->arraymem($sz - 1);
+    //$first = $ar->arraymem(0);
+    $first = $ar[0];
+    //$last = $ar->arraymem($sz - 1);
+    $last = $ar[$sz - 1];
 
     return new PhpXmlRpc\Response(new Value($first->scalarval() .
         $last->scalarval(), "string"));
index ea9d4e1..7b04ecd 100644 (file)
@@ -52,10 +52,14 @@ In case you had extended the classes of the library and added methods to the sub
 implementation clashes with the new one if you implemented:
 
 
-| Class     | Method      | Notes                                   |
-| --------- | ----------- | --------------------------------------- |
-| xmlrpcval | count       | implements interface: Countable         |
-| xmlrpcval | getIterator | implements interface: IteratorAggregate |
+| Class     | Method       | Notes                                   |
+| --------- | ------------ | --------------------------------------- |
+| xmlrpcval | count        | implements interface: Countable         |
+| xmlrpcval | getIterator  | implements interface: IteratorAggregate |
+| xmlrpcval | offsetExists | implements interface: ArrayAccess       |
+| xmlrpcval | offsetGet    | implements interface: ArrayAccess       |
+| xmlrpcval | offsetSet    | implements interface: ArrayAccess       |
+| xmlrpcval | offsetUnset  | implements interface: ArrayAccess       |
 
 
 Global variables cleanup
@@ -111,7 +115,7 @@ Character sets and encoding
 
 The default character set used by the library to deliver data to your app is now UTF8.
 It is also the character set that the library expects data from your app to be in (including method names).
-The value can be changed (to either US-ASCII or ISO-8859-1) by setting teh desired value to
+The value can be changed (to either US-ASCII or ISO-8859-1) by setting the desired value to
     PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding
 
 Usage of closures for wrapping
index 3ff4b7c..e46e1d5 100644 (file)
@@ -422,7 +422,8 @@ class Client
                 $this->proxyport,
                 $this->proxy_user,
                 $this->proxy_pass,
-                $this->proxy_authtype
+                $this->proxy_authtype,
+                $method
             );
         }
 
@@ -442,14 +443,16 @@ class Client
      * @param string $proxyUsername
      * @param string $proxyPassword
      * @param int $proxyAuthType
+     * @param string $method
      * @return Response
      */
     protected function sendPayloadHTTP10($req, $server, $port, $timeout = 0,
                                        $username = '', $password = '', $authType = 1, $proxyHost = '',
-                                       $proxyPort = 0, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1)
+                                       $proxyPort = 0, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1,
+                                       $method='http')
     {
         if ($port == 0) {
-            $port = 80;
+            $port = ( $method === "https" ) ? 443 : 80;
         }
 
         // Only create the payload if it was not created previously
@@ -498,6 +501,7 @@ class Client
             }
             $connectServer = $proxyHost;
             $connectPort = $proxyPort;
+            $transport = "tcp";
             $uri = 'http://' . $server . ':' . $port . $this->path;
             if ($proxyUsername != '') {
                 if ($proxyAuthType != 1) {
@@ -508,6 +512,8 @@ class Client
         } else {
             $connectServer = $server;
             $connectPort = $port;
+            /// @todo if supporting https, we should support all its current options as well: peer name verification etc...
+            $transport = ( $method === "https" ) ? "tls" : "tcp";
             $uri = $this->path;
         }
 
@@ -557,12 +563,12 @@ class Client
         }
 
         if ($timeout > 0) {
-            $fp = @fsockopen($connectServer, $connectPort, $this->errno, $this->errstr, $timeout);
+            $fp = @stream_socket_client("$transport://$connectServer:$connectPort", $this->errno, $this->errstr, $timeout);
         } else {
-            $fp = @fsockopen($connectServer, $connectPort, $this->errno, $this->errstr);
+            $fp = @stream_socket_client("$transport://$connectServer:$connectPort", $this->errno, $this->errstr);
         }
         if ($fp) {
-            if ($timeout > 0 && function_exists('stream_set_timeout')) {
+            if ($timeout > 0) {
                 stream_set_timeout($fp, $timeout);
             }
         } else {
@@ -1043,26 +1049,30 @@ class Client
             }
 
             $response = array();
-            for ($i = 0; $i < $numRets; $i++) {
-                $val = $rets->arraymem($i);
+            //for ($i = 0; $i < $numRets; $i++) {
+            foreach($rets as $val) {
+                //$val = $rets->arraymem($i);
                 switch ($val->kindOf()) {
                     case 'array':
                         if ($val->count() != 1) {
                             return false;       // Bad value
                         }
                         // Normal return value
-                        $response[$i] = new Response($val->arraymem(0));
+                        //$response[] = new Response($val->arraymem(0));
+                        $response[] = new Response($val[0]);
                         break;
                     case 'struct':
-                        $code = $val->structmem('faultCode');
+                        //$code = $val->structmem('faultCode');
+                        $code = $val['faultCode'];
                         if ($code->kindOf() != 'scalar' || $code->scalartyp() != 'int') {
                             return false;
                         }
-                        $str = $val->structmem('faultString');
+                        //$str = $val->structmem('faultString');
+                        $str = $val['faultString'];
                         if ($str->kindOf() != 'scalar' || $str->scalartyp() != 'string') {
                             return false;
                         }
-                        $response[$i] = new Response(0, $code->scalarval(), $str->scalarval());
+                        $response[] = new Response(0, $code->scalarval(), $str->scalarval());
                         break;
                     default:
                         return false;
index 86e518e..1605418 100644 (file)
@@ -71,10 +71,12 @@ class Encoder
 
                 return $xmlrpcVal->scalarval();
             case 'array':
-                $size = $xmlrpcVal->count();
+                //$size = $xmlrpcVal->count();
                 $arr = array();
-                for ($i = 0; $i < $size; $i++) {
-                    $arr[] = $this->decode($xmlrpcVal->arraymem($i), $options);
+                //for ($i = 0; $i < $size; $i++) {
+                foreach($xmlrpcVal as $value) {
+                    //$arr[] = $this->decode($xmlrpcVal->arraymem($i), $options);
+                    $arr[] = $this->decode($value, $options);
                 }
 
                 return $arr;
@@ -290,8 +292,10 @@ class Encoder
             case 'methodresponse':
                 $v = &$xmlRpcParser->_xh['value'];
                 if ($xmlRpcParser->_xh['isf'] == 1) {
-                    $vc = $v->structmem('faultCode');
-                    $vs = $v->structmem('faultString');
+                    //$vc = $v->structmem('faultCode');
+                    //$vs = $v->structmem('faultString');
+                    $vc = $v['faultCode'];
+                    $vs = $v['faultString'];
                     $r = new Response(0, $vc->scalarval(), $vs->scalarval());
                 } else {
                     $r = new Response($v);
index 2c47940..5220968 100644 (file)
@@ -330,8 +330,10 @@ class Request
             if ($xmlRpcParser->_xh['isf']) {
                 /// @todo we should test here if server sent an int and a string, and/or coerce them into such...
                 if ($returnType == 'xmlrpcvals') {
-                    $errNo_v = $v->structmem('faultCode');
-                    $errStr_v = $v->structmem('faultString');
+                    //$errNo_v = $v->structmem('faultCode');
+                    //$errStr_v = $v->structmem('faultString');
+                    $errNo_v = $v['faultCode'];
+                    $errStr_v = $v['faultString'];
                     $errNo = $errNo_v->scalarval();
                     $errStr = $errStr_v->scalarval();
                 } else {
index d530fcd..20ec5a0 100644 (file)
@@ -914,7 +914,8 @@ class Server
         if ($call->kindOf() != 'struct') {
             return static::_xmlrpcs_multicall_error('notstruct');
         }
-        $methName = @$call->structmem('methodName');
+        //$methName = $call->structmem('methodName');
+        $methName = @$call['methodName'];
         if (!$methName) {
             return static::_xmlrpcs_multicall_error('nomethod');
         }
@@ -925,20 +926,22 @@ class Server
             return static::_xmlrpcs_multicall_error('recursion');
         }
 
-        $params = @$call->structmem('params');
+        //$params = @$call->structmem('params');
+        $params = @$call['params'];
         if (!$params) {
             return static::_xmlrpcs_multicall_error('noparams');
         }
         if ($params->kindOf() != 'array') {
             return static::_xmlrpcs_multicall_error('notarray');
         }
-        $numParams = $params->count();
+        //$numParams = $params->count();
 
         $req = new Request($methName->scalarval());
-        for ($i = 0; $i < $numParams; $i++) {
-            if (!$req->addParam($params->arraymem($i))) {
-                $i++;
-
+        //for ($i = 0; $i < $numParams; $i++) {
+        foreach($params as $i => $param) {
+            //if (!$req->addParam($params->arraymem($i))) {
+            if (!$req->addParam($param)) {
+                $i++; // for error message, we count params from 1
                 return static::_xmlrpcs_multicall_error(new Response(0,
                     PhpXmlRpc::$xmlrpcerr['incorrect_params'],
                     PhpXmlRpc::$xmlrpcstr['incorrect_params'] . ": probable xml error in param " . $i));
@@ -999,10 +1002,11 @@ class Server
         // let accept a plain list of php parameters, beside a single xmlrpc msg object
         if (is_object($req)) {
             $calls = $req->getParam(0);
-            $numCalls = $calls->count();
-            for ($i = 0; $i < $numCalls; $i++) {
-                $call = $calls->arraymem($i);
-                $result[$i] = static::_xmlrpcs_multicall_do_call($server, $call);
+            //$numCalls = $calls->count();
+            //for ($i = 0; $i < $numCalls; $i++) {
+            foreach($calls as $call) {
+                //$call = $calls->arraymem($i);
+                $result[] = static::_xmlrpcs_multicall_do_call($server, $call);
             }
         } else {
             $numCalls = count($req);
index 84f04d1..f0d331a 100644 (file)
@@ -4,7 +4,7 @@ namespace PhpXmlRpc;
 
 use PhpXmlRpc\Helper\Charset;
 
-class Value implements \Countable, \IteratorAggregate
+class Value implements \Countable, \IteratorAggregate, \ArrayAccess
 {
     public static $xmlrpcI4 = "i4";
     public static $xmlrpcInt = "int";
@@ -323,6 +323,8 @@ class Value implements \Countable, \IteratorAggregate
      * @param string $key the name of the struct member to be looked up
      *
      * @return boolean
+     *
+     * @deprecated use array access, e.g. isset($val[$key])
      */
     public function structmemexists($key)
     {
@@ -336,6 +338,8 @@ class Value implements \Countable, \IteratorAggregate
      * @param string $key the name of the struct member to be looked up
      *
      * @return Value
+     *
+     * @deprecated use array access, e.g. $val[$key]
      */
     public function structmem($key)
     {
@@ -399,6 +403,8 @@ class Value implements \Countable, \IteratorAggregate
      * @param integer $key the index of the value to be retrieved (zero based)
      *
      * @return Value
+     *
+     * @deprecated use array access, e.g. $val[$key]
      */
     public function arraymem($key)
     {
@@ -441,7 +447,7 @@ class Value implements \Countable, \IteratorAggregate
     {
         switch ($this->mytype) {
             case 3:
-                count($this->me['struct']);
+                return count($this->me['struct']);
             case 2:
                 return count($this->me['array']);
             case 1:
@@ -469,4 +475,92 @@ class Value implements \Countable, \IteratorAggregate
         }
         return new \ArrayIterator();
     }
+
+
+    public function offsetSet($offset, $value) {
+
+        switch ($this->mytype) {
+            case 3:
+                if (!($value instanceof \PhpXmlRpc\Value)) {
+                    throw new \Exception('It is only possible to add Value objects to an XML-RPC Struct');
+                }
+                if (is_null($offset)) {
+                    // disallow struct members with empty names
+                    throw new \Exception('It is not possible to add anonymous members to an XML-RPC Struct');
+                } else {
+                    $this->me['struct'][$offset] = $value;
+                }
+                return;
+            case 2:
+                if (!($value instanceof \PhpXmlRpc\Value)) {
+                    throw new \Exception('It is only possible to add Value objects to an XML-RPC Array');
+                }
+                if (is_null($offset)) {
+                    $this->me['array'][] = $value;
+                } else {
+                    // nb: we are not checking that $offset is above the existing array range...
+                    $this->me['array'][$offset] = $value;
+                }
+                return;
+            case 1:
+// todo: handle i4 vs int
+                reset($this->me);
+                list($type,) = each($this->me);
+                if ($type != $offset) {
+                    throw new \Exception('');
+                }
+                $this->me[$type] = $value;
+                return;
+            default:
+                // 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
+                throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be set using array index");
+        }
+    }
+
+    public function offsetExists($offset) {
+        switch ($this->mytype) {
+            case 3:
+                return isset($this->me['struct'][$offset]);
+            case 2:
+                return isset($this->me['array'][$offset]);
+            case 1:
+// todo: handle i4 vs int
+                return $offset == $this->scalartyp();
+            default:
+                return false;
+        }
+    }
+
+    public function offsetUnset($offset) {
+        switch ($this->mytype) {
+            case 3:
+                unset($this->me['struct'][$offset]);
+                return;
+            case 2:
+                unset($this->me['array'][$offset]);
+                return;
+            case 1:
+                // can not remove value from a scalar
+                throw new \Exception("XML-RPC Value is of type 'scalar' and its value can not be unset using array index");
+            default:
+                throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be unset using array index");
+        }
+    }
+
+    public function offsetGet($offset) {
+        switch ($this->mytype) {
+            case 3:
+                return isset($this->me['struct'][$offset]) ? $this->me['struct'][$offset] : null;
+            case 2:
+                return isset($this->me['array'][$offset]) ? $this->me['array'][$offset] : null;
+            case 1:
+// on bad type: null or exception?
+                reset($this->me);
+                list($type, $value) = each($this->me);
+                return $type == $offset ? $value : null;
+            default:
+// return null or exception?
+                throw new \Exception("XML-RPC Value is of type 'undef' and can not be accessed using array index");
+        }
+    }
 }
\ No newline at end of file