improve client demos: demo more lib features; add use clauses
authorgggeek <giunta.gaetano@gmail.com>
Sun, 8 Jan 2023 12:23:45 +0000 (12:23 +0000)
committergggeek <giunta.gaetano@gmail.com>
Sun, 8 Jan 2023 12:23:45 +0000 (12:23 +0000)
demo/client/agesort.php
demo/client/getstatename.php
demo/client/introspect.php
demo/client/mail.php [deleted file]
demo/client/proxy.php
demo/client/which.php
demo/server/methodProviders/interop.php

index e6b863e..7ab2b95 100644 (file)
@@ -6,60 +6,59 @@ output('<html lang="en">
 <body>
 <h1>Agesort demo</h1>
 <h2>Send an array of "name" => "age" pairs to the server that will send it back sorted.</h2>
-<h3>The source code demonstrates basic lib usage, including manual creation of xml-rpc arrays and structs</h3>
-<p>Have a look at <a href="getstatename.php">getstatename.php</a> for automatic encoding and decoding, and at
-    <a href="../vardemo.php">vardemo.php</a> for more examples of manual encoding and decoding</p>
+<h3>The code demonstrates usage of automatic encoding/decoding of php variables into xmlrpc values such as arrays and structs</h3>
+<p>Have a look at <a href="../vardemo.php">vardemo.php</a> for more examples of manual encoding and decoding</p>
 <p>You can see the source to this page here: <a href="agesort.php?showSource=1">agesort.php</a></p>
 ');
 
-$inAr = array("Dave" => 24, "Edd" => 45, "Joe" => 37, "Fred" => 27);
+use PhpXmlRpc\Client;
+use PhpXmlRpc\Encoder;
+use PhpXmlRpc\Request;
 
-output("This is the input data:<br/><pre>");
-foreach ($inAr as $key => $val) {
-    output($key . ", " . $val . "\n");
-}
-output("</pre>");
+$inAr = array(
+    array('name' => 'Dave', 'age' => 24),
+    array('name' => 'Edd',  'age' => 45),
+    array('name' => 'Joe',  'age' => 37),
+    array('name' => 'Fred', 'age' => 27),
+);
 
-// Create parameters from the input array: an xmlrpc array of xmlrpc structs
-$p = array();
-foreach ($inAr as $key => $val) {
-    $p[] = new PhpXmlRpc\Value(
-        array(
-            "name" => new PhpXmlRpc\Value($key),
-            "age" => new PhpXmlRpc\Value($val, "int")
-        ),
-        "struct"
-    );
+output('This is the input data:<br/><pre>');
+foreach ($inAr as $val) {
+    output($val['name'] . ", " . $val['age'] . "\n");
 }
-$v = new PhpXmlRpc\Value($p, "array");
+output('</pre>');
+
+// Create xml-rpc parameters from the input array: an array of structs
+$encoder = new Encoder();
+$v = $encoder->encode($inAr);
 output("Encoded into xmlrpc format it looks like this: <pre>\n" . htmlentities($v->serialize()) . "</pre>\n");
 
-// create client and message objects
-$req = new PhpXmlRpc\Request('examples.sortByAge', array($v));
-$client = new PhpXmlRpc\Client(XMLRPCSERVER);
+// create client and request objects
+$req = new Request('examples.sortByAge', array($v));
+$client = new Client(XMLRPCSERVER);
 
 // set maximum debug level, to have the complete communication printed to screen
 $client->setDebug(2);
 
 // send request
-output("Now sending request (detailed debug info follows)");
+output('Now sending the request... (very detailed debug info follows. Scroll to the bottom of the page for results!)<hr/>');
 $resp = $client->send($req);
+output('<hr/>');
 
 // check response for errors, and take appropriate action
 if (!$resp->faultCode()) {
-    output("The server gave me these results:<pre>");
+    output('The server gave me these results:<pre>');
     $value = $resp->value();
-    foreach ($value as $struct) {
-        $name = $struct["name"];
-        $age = $struct["age"];
-        output(htmlspecialchars($name->scalarval()) . ", " . htmlspecialchars($age->scalarval()) . "\n");
+    foreach ($encoder->decode($value) as $struct) {
+        // note: here we are trusting the server's response to have the expected format
+        output(htmlspecialchars($struct['name']) . ", " . htmlspecialchars($struct['age']) . "\n");
     }
 
-    output("<hr/>For nerds: I got this value back<br/><pre>" .
+    output('</pre><hr/>For nerds: I got this value back<br/><pre>' .
         htmlentities($resp->serialize()) . "</pre><hr/>\n");
 } else {
-    output("An error occurred:<pre>");
-    output("Code: " . htmlspecialchars($resp->faultCode()) .
+    output('An error occurred:<pre>');
+    output('Code: ' . htmlspecialchars($resp->faultCode()) .
         "\nReason: '" . htmlspecialchars($resp->faultString()) . "'</pre><hr/>");
 }
 
index 2f7df37..ee838b5 100644 (file)
@@ -6,35 +6,43 @@ output('<html lang="en">
 <body>
 <h1>Getstatename demo</h1>
 <h2>Send a U.S. state number to the server and get back the state name</h2>
-<h3>The code demonstrates usage of automatic encoding/decoding of php variables into xmlrpc values</h3>
+<h3>The source code demonstrates basic lib usage, including manual creation and decoding of of xml-rpc values</h3>
 <p>You can see the source to this page here: <a href="getstatename.php?showSource=1">getstatename.php</a></p>
 ');
 
-if (isset($_POST["stateno"]) && $_POST["stateno"] != "") {
-    $stateNo = (integer)$_POST["stateno"];
-    $encoder = new PhpXmlRpc\Encoder();
-    $req = new PhpXmlRpc\Request('examples.getStateName',
-        array($encoder->encode($stateNo))
+use PhpXmlRpc\Client;
+use PhpXmlRpc\Request;
+use PhpXmlRpc\Value;
+
+$stateNo = "";
+
+if (isset($_POST['stateno']) && $_POST['stateno'] != "") {
+    $stateNo = (integer)$_POST['stateno'];
+    $method = 'examples.getStateName';
+    $arguments = array(
+        new Value($stateNo, Value::$xmlrpcInt),
     );
-    output("Sending the following request:<pre>\n\n" . htmlentities($req->serialize()) . "\n\n</pre>Debug info of server data follows...\n\n");
-    $client = new PhpXmlRpc\Client(XMLRPCSERVER);
+    $req = new Request($method, $arguments);
+    output("Sending the following request:<pre>\n\n" . htmlentities($req->serialize()) .
+        "\n\n</pre>Debug info of server data follows...\n\n");
+    $client = new Client(XMLRPCSERVER);
     $client->setDebug(1);
-    $r = $client->send($req);
-    if (!$r->faultCode()) {
-        $v = $r->value();
-        output("<br/>State number <b>" . $stateNo . "</b> is <b>"
-            . htmlspecialchars($encoder->decode($v)) . "</b><br/><br/>");
+    $resp = $client->send($req);
+    if (!$resp->faultCode()) {
+        $val = $resp->value();
+        // NB: we are _assuming_ that the server did return a scalar xml-rpc value here.
+        // If the server is not trusted, we might check that via `$val->kindOf() == 'scalar'`
+        output('<br/>State number <b>' . $stateNo . '</b> is <b>'
+            . htmlspecialchars($val->scalarval()) . '</b><br/><br/>');
     } else {
-        output("An error occurred: ");
-        output("Code: " . htmlspecialchars($r->faultCode())
-            . " Reason: '" . htmlspecialchars($r->faultString()) . "'</pre><br/>");
+        output('An error occurred: ');
+        output('<pre>Code: ' . htmlspecialchars($resp->faultCode())
+            . " Reason: '" . htmlspecialchars($resp->faultString()) . "'</pre>");
     }
-} else {
-    $stateNo = "";
 }
 
 output("<form action=\"getstatename.php\" method=\"POST\">
-<input name=\"stateno\" value=\"" . $stateNo . "\">
+<input name=\"stateno\" value=\"$stateNo\">
 <input type=\"submit\" value=\"go\" name=\"submit\">
 </form>
 <p>Enter a state number to query its name</p>");
index 565e1c4..30a0a62 100644 (file)
@@ -5,22 +5,28 @@ output('<html lang="en">
 <head><title>xmlrpc - Introspect demo</title></head>
 <body>
 <h1>Introspect demo</h1>
-<h2>Query server for available methods and their description</h2>
-<h3>The code demonstrates usage of multicall and introspection methods</h3>
+<h2>Query server for available methods, their description and their signatures</h2>
+<h3>The code demonstrates usage of multicall, introspection methods `system.listMethods` and co., and `$client->return_type`</h3>
 <p>You can see the source to this page here: <a href="introspect.php?showSource=1">introspect.php</a></p>
 ');
 
+use PhpXmlRpc\Client;
+use PhpXmlRpc\Helper\XMLParser;
+use PhpXmlRpc\Request;
+
 function display_error($r)
 {
     output("An error occurred: ");
     output("Code: " . $r->faultCode() . " Reason: '" . $r->faultString() . "'<br/>");
 }
 
-$client = new PhpXmlRpc\Client(XMLRPCSERVER);
+$client = new Client(XMLRPCSERVER);
+// tell the client we want back plain php values
+$client->return_type = XMLParser::RETURN_PHP;
 
 // First off, let's retrieve the list of methods available on the remote server
 output("<h3>methods available at http://" . $client->server . $client->path . "</h3>\n");
-$req = new PhpXmlRpc\Request('system.listMethods');
+$req = new Request('system.listMethods');
 $resp = $client->send($req);
 
 if ($resp->faultCode()) {
@@ -28,46 +34,61 @@ if ($resp->faultCode()) {
 } else {
     $v = $resp->value();
 
+    // check if the server supports 'system.multicall', and configure the client accordingly
+    $avoidMulticall = true;
+    foreach ($v as $methodName) {
+        if ($methodName == 'system.multicall') {
+            $avoidMulticall = false;
+            break;
+        }
+    }
+
+    $client->no_multicall = $avoidMulticall;
+
     // Then, retrieve the signature and help text of each available method
     foreach ($v as $methodName) {
-        output("<h4>" . htmlspecialchars($methodName->scalarval()) . "</h4>\n");
-        // build messages first, add params later
-        $m1 = new PhpXmlRpc\Request('system.methodHelp');
-        $m2 = new PhpXmlRpc\Request('system.methodSignature');
-        $val = new PhpXmlRpc\Value($methodName->scalarval(), "string");
-        $m1->addParam($val);
-        $m2->addParam($val);
-        // Send multiple requests in one http call.
-        // If server does not support multicall, client will automatically fall back to 2 separate calls
-        $ms = array($m1, $m2);
-        $rs = $client->send($ms);
-        if ($rs[0]->faultCode()) {
-            display_error($rs[0]);
+        output("<h4>" . htmlspecialchars($methodName) . "</h4>\n");
+        // build requests first, add params later
+        $r1 = new PhpXmlRpc\Request('system.methodHelp');
+        $r2 = new PhpXmlRpc\Request('system.methodSignature');
+        $val = new PhpXmlRpc\Value($methodName, "string");
+        $r1->addParam($val);
+        $r2->addParam($val);
+        // Send multiple requests in one/many http calls.
+        $reqs = array($r1, $r2);
+        $resps = $client->send($reqs);
+        if ($resps[0]->faultCode()) {
+            display_error($resps[0]);
         } else {
-            $val = $rs[0]->value();
-            $txt = $val->scalarval();
+            output("<h5>Documentation</h5><p>\n");
+            $txt = $resps[0]->value();
             if ($txt != "") {
-                output("<h4>Documentation</h4><p>{$txt}</p>\n");
+                // NB: we explicitly avoid escaping the received data because the spec says that html _can be_ in methodHelp.
+                // That is not a very good practice nevertheless!
+                output("<p>$txt</p>\n");
             } else {
                 output("<p>No documentation available.</p>\n");
             }
         }
-        if ($rs[1]->faultCode()) {
-            display_error($rs[1]);
+        if ($resps[1]->faultCode()) {
+            display_error($resps[1]);
         } else {
-            output("<h4>Signature</h4><p>\n");
-            // note: using PhpXmlRpc\Encoder::decode() here would lead to cleaner code
-            $val = $rs[1]->value();
-            if ($val->kindOf() == "array") {
-                foreach ($val as $x) {
-                    $ret = $x[0];
-                    output("<code>" . htmlspecialchars($ret->scalarval()) . " "
-                        . htmlspecialchars($methodName->scalarval()) . "(");
-                    if ($x->count() > 1) {
-                        for ($k = 1; $k < $x->count(); $k++) {
-                            $y = $x[$k];
-                            output(htmlspecialchars($y->scalarval()));
-                            if ($k < $x->count() - 1) {
+            output("<h5>Signature(s)</h5><p>\n");
+            $sigs = $resps[1]->value();
+            if (is_array($sigs)) {
+                foreach ($sigs as $sn => $sig) {
+                    // can we trust the server to be fully compliant with the spec?
+                    if (!is_array($sig)) {
+                        output("Signature $sn: unknown\n");
+                        continue;
+                    }
+                    $ret = $sig[0];
+                    output("<code>" . htmlspecialchars($ret) . " "
+                        . htmlspecialchars($methodName) . "(");
+                    if (count($sig) > 1) {
+                        for ($k = 1; $k < count($sig); $k++) {
+                            output(htmlspecialchars($sig[$k]));
+                            if ($k < count($sig) - 1) {
                                 output(", ");
                             }
                         }
diff --git a/demo/client/mail.php b/demo/client/mail.php
deleted file mode 100644 (file)
index 06f1db8..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-require_once __DIR__ . "/_prepend.php";
-
-output('<html lang="en">
-<head><title>xmlrpc - Mail demo</title></head>
-<body>
-<h1>Mail demo</h1>
-<p>This form enables you to send mail via an XML-RPC server.
-    When you press <kbd>Send</kbd> this page will reload, showing you the XML-RPC request sent to the host server, the
-    XML-RPC response received and the internal evaluation done by the PHP implementation.</p>
-<p>You can see the source to this page here: <a href="mail.php?showSource=1">mail.php</a><br/>
-    And the source to a functionally identical mail-by-XML-RPC server in the file <a
-        href="../server/server.php?showSource=1">server.php</a> included with the library (look for the "mail_send"
-    method)</p>
-');
-
-if (isset($_POST["mailto"]) && $_POST["mailto"]) {
-    $server = XMLRPCSERVER;
-    $req = new PhpXmlRpc\Request('mail.send', array(
-        new PhpXmlRpc\Value($_POST["mailto"]),
-        new PhpXmlRpc\Value($_POST["mailsub"]),
-        new PhpXmlRpc\Value($_POST["mailmsg"]),
-        new PhpXmlRpc\Value($_POST["mailfrom"]),
-        new PhpXmlRpc\Value($_POST["mailcc"]),
-        new PhpXmlRpc\Value($_POST["mailbcc"]),
-        new PhpXmlRpc\Value("text/plain")
-    ));
-
-    $client = new PhpXmlRpc\Client($server);
-    $client->setDebug(2);
-    $resp = $client->send($req);
-    if (!$resp->faultCode()) {
-        output("Mail sent OK<br/>\n");
-    } else {
-        output("<font color=\"red\">");
-        output("Mail send failed<br/>\n");
-        output("Fault: ");
-        output("Code: " . htmlspecialchars($resp->faultCode()) .
-            " Reason: '" . htmlspecialchars($resp->faultString()) . "'<br/>");
-        output("</font><br/>");
-    }
-}
-output('
-<form method="POST">
-    From <input size="60" name="mailfrom" value=""/><br/>
-    <hr/>
-    To <input size="60" name="mailto" value=""/><br/>
-    Cc <input size="60" name="mailcc" value=""/><br/>
-    Bcc <input size="60" name="mailbcc" value=""/><br/>
-    <hr/>
-    Subject <input size="60" name="mailsub" value="A message from xmlrpc"/>
-    <hr/>
-    Body <textarea rows="7" cols="60" name="mailmsg">Your message here</textarea><br/>
-    <input type="Submit" value="Send"/>
-</form>
-</body>
-</html>
-');
index 2108e60..20bd8a0 100644 (file)
@@ -10,45 +10,55 @@ output('<html lang="en">
 <p>You can see the source to this page here: <a href="proxy.php?showSource=1">proxy.php</a></p>
 ');
 
-class PhpXmlRpcProxy
+class XmlRpcProxy
 {
     protected $client;
     protected $prefix;
+    protected $encoder;
     protected $encodingOptions = array();
 
+    /**
+     * We rely on injecting a fully-formed Client, so that all the necessary http/debugging options can be set into it
+     * without the need for this class to reimplement support for that configuration.
+     */
     public function __construct(PhpXmlRpc\Client $client, $prefix = 'examples.', $encodingOptions = array())
     {
         $this->client = $client;
         $this->prefix = $prefix;
         $this->encodingOptions = $encodingOptions;
+        $this->encoder = new PhpXmlRpc\Encoder();
     }
 
     /**
-     * Translates any method call to an xml-rpc call.
+     * Translates any php method call to an xml-rpc call.
+     * Note that the server might expose methods which can not be called directly this way, because their name includes
+     * characters which are not allowed in a php method. That's why we implement as well method `call`
      *
      * @author Toth Istvan
      *
      * @param string $name remote function name. Will be prefixed
-     * @param array $arguments
+     * @param array $arguments any php value will do. For xml-rpc base64 values, wrap them in a Value object
      * @return mixed
      *
      * @throws Exception
      */
     public function __call($name, $arguments)
     {
-        $encoder = new PhpXmlRpc\Encoder();
-        $valueArray = array();
+        $arguments = array();
         foreach ($arguments as $parameter) {
-            $valueArray[] = $encoder->encode($parameter, $this->encodingOptions);
+            $arguments[] = $this->encoder->encode($parameter, $this->encodingOptions);
         }
 
         // just in case this was set to something else
+        $originalReturnType = $this->client->return_type;
         $this->client->return_type = 'phpvals';
 
-        $resp = $this->client->send(new PhpXmlRpc\Request($this->prefix.$name, $valueArray));
+        $resp = $this->client->send(new PhpXmlRpc\Request($this->prefix.$name, $arguments));
+
+        $this->client->return_type = $originalReturnType;
 
         if ($resp->faultCode()) {
-            throw new Exception($resp->faultString(), $resp->faultCode());
+            throw new \Exception($resp->faultString(), $resp->faultCode());
         } else {
             return $resp->value();
         }
@@ -58,7 +68,7 @@ class PhpXmlRpcProxy
      * In case the remote method name has characters which are not valid as php method names, use this.
      *
      * @param string $name remote function name. Will be prefixed
-     * @param array $arguments
+     * @param array $arguments any php value will do. For xml-rpc base64 values, wrap them in a Value object
      * @return mixed
      *
      * @throws Exception
@@ -69,7 +79,7 @@ class PhpXmlRpcProxy
     }
 }
 
-$proxy = new PhpXmlRpcProxy(new PhpXmlRpc\Client(XMLRPCSERVER));
+$proxy = new XmlRpcProxy(new PhpXmlRpc\Client(XMLRPCSERVER));
 
 $stateNo = rand(1, 51);
 // sadly, no IDE will be able to assist with autocompletion for this method, unless you manually add an equivalent phpdoc comment...
index 888c751..1de3207 100644 (file)
@@ -6,16 +6,45 @@ output('<html lang="en">
 <body>
 <h1>Which toolkit demo</h1>
 <h2>Query server for toolkit information</h2>
-<h3>The code demonstrates usage of the PhpXmlRpc\Encoder class</h3>
+<h3>The code demonstrates support for http redirects, the `interopEchoTests.whichToolkit` xml-rpc methods and use of pre-built xml</h3>
 <p>You can see the source to this page here: <a href="which.php?showSource=1">which.php</a></p>
 ');
 
-$client = new PhpXmlRpc\Client(XMLRPCSERVER);
-$req = new PhpXmlRpc\Request('interopEchoTests.whichToolkit', array());
-$resp = $client->send($req);
+use PhpXmlRpc\Client;
+use PhpXmlRpc\Encoder;
+
+$client = new Client(XMLRPCSERVER);
+
+// to support http redirects we have to force usage of cURL even for http 1.0 requests
+$client->setUseCurl(Client::USE_CURL_ALWAYS);
+$client->setCurlOptions(array(CURLOPT_FOLLOWLOCATION => true, CURLOPT_POSTREDIR => 3));
+
+// use a pre-built request payload
+$payload = '<?xml version="1.0"?>
+<methodCall>
+    <methodName>interopEchoTests.whichToolkit</methodName>
+    <params/>
+</methodCall>';
+// and ask the client to give us back xml
+$client->return_type = 'xml';
+
+output("XML custom request:<br/><pre>" . htmlspecialchars($payload) . "</pre>\n");
+
+$resp = $client->send($payload);
+
 if (!$resp->faultCode()) {
-    $encoder = new PhpXmlRpc\Encoder();
-    $value = $encoder->decode($resp->value());
+
+    $xml = $resp->value();
+    output("XML response:<br/><pre>" . htmlspecialchars($xml) . "</pre>\n");
+
+    $encoder = new Encoder();
+    // from xml to xml-rpc Response
+    $response = $encoder->decodeXml($xml);
+    // from Response to Value
+    $value = $response->value();
+    // from value to php
+    $value = $encoder->decode($value);
+
     output("Toolkit info:<br/>\n");
     output("<pre>");
     output("name: " . htmlspecialchars($value["toolkitName"]) . "\n");
index 691a7ec..b911cb6 100644 (file)
@@ -114,7 +114,7 @@ function i_whichToolkit($req)
 {
     global $SERVER_SOFTWARE;
     $ret = array(
-        "toolkitDocsUrl" => "http://phpxmlrpc.sourceforge.net/",
+        "toolkitDocsUrl" => "https://gggeek.github.io/phpxmlrpc/",
         "toolkitName" => PhpXmlRpc\PhpXmlRpc::$xmlrpcName,
         "toolkitVersion" => PhpXmlRpc\PhpXmlRpc::$xmlrpcVersion,
         "toolkitOperatingSystem" => isset($SERVER_SOFTWARE) ? $SERVER_SOFTWARE : $_SERVER['SERVER_SOFTWARE'],