1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="zend.xmlrpc.server">
4 <title>Zend_XmlRpc_Server</title>
6 <sect2 id="zend.xmlrpc.server.introduction">
7 <title>Introduction</title>
10 <classname>Zend_XmlRpc_Server</classname> is intended as a fully-featured
11 <acronym>XML-RPC</acronym> server, following <ulink url="http://www.xmlrpc.com/spec">the
12 specifications outlined at www.xmlrpc.com</ulink>. Additionally, it implements the
13 <command>system.multicall()</command> method, allowing boxcarring of requests.
17 <sect2 id="zend.xmlrpc.server.usage">
18 <title>Basic Usage</title>
21 An example of the most basic use case:
24 <programlisting language="php"><![CDATA[
25 $server = new Zend_XmlRpc_Server();
26 $server->setClass('My_Service_Class');
27 echo $server->handle();
31 <sect2 id="zend.xmlrpc.server.structure">
32 <title>Server Structure</title>
35 <classname>Zend_XmlRpc_Server</classname> is composed of a variety of components,
36 ranging from the server itself to request, response, and fault objects.
40 To bootstrap <classname>Zend_XmlRpc_Server</classname>, the developer must attach one or
41 more classes or functions to the server, via the
42 <methodname>setClass()</methodname> and <methodname>addFunction()</methodname> methods.
46 Once done, you may either pass a <classname>Zend_XmlRpc_Request</classname>
47 object to <methodname>Zend_XmlRpc_Server::handle()</methodname>, or it will
48 instantiate a <classname>Zend_XmlRpc_Request_Http</classname> object if none
49 is provided -- thus grabbing the request from
50 <filename>php://input</filename>.
54 <methodname>Zend_XmlRpc_Server::handle()</methodname> then attempts to
55 dispatch to the appropriate handler based on the method
56 requested. It then returns either a
57 <classname>Zend_XmlRpc_Response</classname>-based object or a
58 <classname>Zend_XmlRpc_Server_Fault</classname>object. These objects both have
59 <methodname>__toString()</methodname> methods that create valid
60 <acronym>XML-RPC</acronym> <acronym>XML</acronym> responses, allowing them to be
65 <sect2 id="zend.xmlrpc.server.anatomy">
66 <title>Anatomy of a webservice</title>
68 <sect3 id="zend.xmlrpc.server.anatomy.general">
69 <title>General considerations</title>
72 For maximum performance it is recommended to use a simple
73 bootstrap file for the server component. Using
74 <classname>Zend_XmlRpc_Server</classname> inside a
75 <link linkend="zend.controller"><classname>Zend_Controller</classname></link>
76 is strongly discouraged to avoid the overhead.
80 Services change over time and while webservices are generally
81 less change intense as code-native <acronym>APIs</acronym>, it
82 is recommended to version your service. Do so to lay grounds to
83 provide compatibility for clients using older versions of your
84 service and manage your service lifecycle including deprecation
85 timeframes.To do so just include a version number into your
86 <acronym>URI</acronym>. It is also recommended to include the
87 remote protocol name in the <acronym>URI</acronym> to allow easy
88 integration of upcoming remoting technologies.
89 http://myservice.ws/<emphasis>1.0/XMLRPC/</emphasis>.
93 <sect3 id="zend.xmlrpc.server.anatomy.expose">
94 <title>What to expose?</title>
97 Most of the time it is not sensible to expose business objects
98 directly. Business objects are usually small and under heavy
99 change, because change is cheap in this layer of your
100 application. Once deployed and adopted, web services are hard to
101 change. Another concern is <acronym>I/O</acronym> and latency:
102 the best webservice calls are those not happening. Therefore
103 service calls need to be more coarse-grained than usual business
104 logic is. Often an additional layer in front of your business
105 objects makes sense. This layer is sometimes referred to as <ulink
106 url="http://martinfowler.com/eaaCatalog/remoteFacade.html">Remote
108 Such a service layer adds a coarse grained interface on top of
109 your business logic and groups verbose operations into smaller
115 <sect2 id="zend.xmlrpc.server.conventions">
116 <title>Conventions</title>
119 <classname>Zend_XmlRpc_Server</classname> allows the developer to attach functions and
120 class method calls as dispatchable <acronym>XML-RPC</acronym> methods. Via
121 <classname>Zend_Server_Reflection</classname>, it does introspection on all attached
122 methods, using the function and method docblocks to determine the
123 method help text and method signatures.
127 <acronym>XML-RPC</acronym> types do not necessarily map one-to-one to
128 <acronym>PHP</acronym> types. However, the code will do its best to guess the
129 appropriate type based on the values listed in @param and @return lines. Some
130 <acronym>XML-RPC</acronym> types have no immediate <acronym>PHP</acronym> equivalent,
131 however, and should be hinted using the <acronym>XML-RPC</acronym> type in the PHPDoc.
138 <emphasis><property>dateTime.iso8601</property></emphasis>, a string formatted
139 as '<command>YYYYMMDDTHH:mm:ss</command>'
143 <listitem><para><emphasis>base64</emphasis>, base64 encoded data</para></listitem>
145 <listitem><para><emphasis>struct</emphasis>, any associative array</para></listitem>
149 An example of how to hint follows:
152 <programlisting language="php"><![CDATA[
154 * This is a sample function
156 * @param base64 $val1 Base64-encoded data
157 * @param dateTime.iso8601 $val2 An ISO date
158 * @param struct $val3 An associative array
161 function myFunc($val1, $val2, $val3)
167 PhpDocumentor does no validation of the types specified for params
168 or return values, so this will have no impact on your <acronym>API</acronym>
169 documentation. Providing the hinting is necessary, however, when the
170 server is validating the parameters provided to the method call.
174 It is perfectly valid to specify multiple types for both params and
175 return values; the <acronym>XML-RPC</acronym> specification even suggests that
176 system.methodSignature should return an array of all possible method
177 signatures (i.e., all possible combinations of param and return
178 values). You may do so just as you normally would with
179 PhpDocumentor, using the '|' operator:
182 <programlisting language="php"><![CDATA[
184 * This is a sample function
186 * @param string|base64 $val1 String or base64-encoded data
187 * @param string|dateTime.iso8601 $val2 String or an ISO date
188 * @param array|struct $val3 Normal indexed array or an associative array
189 * @return boolean|struct
191 function myFunc($val1, $val2, $val3)
198 Allowing multiple signatures can lead to confusion for developers
199 using the services; to keep things simple, a <acronym>XML-RPC</acronym>
200 service method should only have a single signature.
205 <sect2 id="zend.xmlrpc.server.namespaces">
206 <title>Utilizing Namespaces</title>
209 <acronym>XML-RPC</acronym> has a concept of namespacing; basically, it allows grouping
210 <acronym>XML-RPC</acronym> methods by dot-delimited namespaces. This helps prevent
211 naming collisions between methods served by different classes. As an
212 example, the <acronym>XML-RPC</acronym> server is expected to server several methods in
213 the 'system' namespace:
217 <listitem><para>system.listMethods</para></listitem>
218 <listitem><para>system.methodHelp</para></listitem>
219 <listitem><para>system.methodSignature</para></listitem>
223 Internally, these map to the methods of the same name in
224 <classname>Zend_XmlRpc_Server</classname>.
228 If you want to add namespaces to the methods you serve, simply
229 provide a namespace to the appropriate method when attaching a
233 <programlisting language="php"><![CDATA[
234 // All public methods in My_Service_Class will be accessible as
235 // myservice.METHODNAME
236 $server->setClass('My_Service_Class', 'myservice');
238 // Function 'somefunc' will be accessible as funcs.somefunc
239 $server->addFunction('somefunc', 'funcs');
243 <sect2 id="zend.xmlrpc.server.request">
244 <title>Custom Request Objects</title>
247 Most of the time, you'll simply use the default request type included with
248 <classname>Zend_XmlRpc_Server</classname>,
249 <classname>Zend_XmlRpc_Request_Http</classname>. However, there may be times when you
250 need <acronym>XML-RPC</acronym> to be available via the <acronym>CLI</acronym>, a
251 <acronym>GUI</acronym>, or other environment, or want to log incoming requests. To do
252 so, you may create a custom request object that extends
253 <classname>Zend_XmlRpc_Request</classname>. The most important thing to remember is to
254 ensure that the <methodname>getMethod()</methodname> and
255 <methodname>getParams()</methodname> methods are implemented so that the
256 <acronym>XML-RPC</acronym> server can retrieve that information in order to dispatch the
261 <sect2 id="zend.xmlrpc.server.response">
262 <title>Custom Responses</title>
265 Similar to request objects, <classname>Zend_XmlRpc_Server</classname> can return custom
266 response objects; by default, a <classname>Zend_XmlRpc_Response_Http</classname> object
267 is returned, which sends an appropriate Content-Type <acronym>HTTP</acronym> header for
268 use with <acronym>XML-RPC</acronym>. Possible uses of a custom object would be to log
269 responses, or to send responses back to <constant>STDOUT</constant>.
273 To use a custom response class, use
274 <methodname>Zend_XmlRpc_Server::setResponseClass()</methodname> prior to calling
275 <methodname>handle()</methodname>.
279 <sect2 id="zend.xmlrpc.server.fault">
280 <title>Handling Exceptions via Faults</title>
283 <classname>Zend_XmlRpc_Server</classname> catches Exceptions generated by a dispatched
284 method, and generates an <acronym>XML-RPC</acronym> fault response when such an
285 exception is caught. By default, however, the exception messages and
286 codes are not used in a fault response. This is an intentional
287 decision to protect your code; many exceptions expose more
288 information about the code or environment than a developer would
289 necessarily intend (a prime example includes database abstraction or
290 access layer exceptions).
294 Exception classes can be whitelisted to be used as fault responses,
295 however. To do so, simply utilize
296 <methodname>Zend_XmlRpc_Server_Fault::attachFaultException()</methodname> to pass an
297 exception class to whitelist:
300 <programlisting language="php"><![CDATA[
301 Zend_XmlRpc_Server_Fault::attachFaultException('My_Project_Exception');
305 If you utilize an exception class that your other project exceptions
306 inherit, you can then whitelist a whole family of exceptions at a
307 time. <classname>Zend_XmlRpc_Server_Exception</classname>s are always whitelisted, to
308 allow reporting specific internal errors (undefined methods, etc.).
312 Any exception not specifically whitelisted will generate a fault
313 response with a code of '404' and a message of 'Unknown error'.
317 <sect2 id="zend.xmlrpc.server.caching">
318 <title>Caching Server Definitions Between Requests</title>
321 Attaching many classes to an <acronym>XML-RPC</acronym> server instance can utilize a
322 lot of resources; each class must introspect using the Reflection
323 <acronym>API</acronym> (via <classname>Zend_Server_Reflection</classname>), which in
324 turn generates a list of all possible method signatures to provide to the server class.
328 To reduce this performance hit somewhat, <classname>Zend_XmlRpc_Server_Cache</classname>
329 can be used to cache the server definition between requests. When
330 combined with <methodname>__autoload()</methodname>, this can greatly increase
335 An sample usage follows:
338 <programlisting language="php"><![CDATA[
339 function __autoload($class)
341 Zend_Loader::loadClass($class);
344 $cacheFile = dirname(__FILE__) . '/xmlrpc.cache';
345 $server = new Zend_XmlRpc_Server();
347 if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
348 require_once 'My/Services/Glue.php';
349 require_once 'My/Services/Paste.php';
350 require_once 'My/Services/Tape.php';
352 $server->setClass('My_Services_Glue', 'glue'); // glue. namespace
353 $server->setClass('My_Services_Paste', 'paste'); // paste. namespace
354 $server->setClass('My_Services_Tape', 'tape'); // tape. namespace
356 Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
359 echo $server->handle();
363 The above example attempts to retrieve a server definition from
364 <property>xmlrpc.cache</property> in the same directory as the script. If unsuccessful,
365 it loads the service classes it needs, attaches them to the server
366 instance, and then attempts to create a new cache file with the
371 <sect2 id="zend.xmlrpc.server.use">
372 <title>Usage Examples</title>
375 Below are several usage examples, showing the full spectrum of
376 options available to developers. Usage examples will each build
377 on the previous example provided.
380 <example id="zend.xmlrpc.server.use.attach-function">
381 <title>Basic Usage</title>
384 The example below attaches a function as a dispatchable <acronym>XML-RPC</acronym>
385 method and handles incoming calls.
388 <programlisting language="php"><![CDATA[
390 * Return the MD5 sum of a value
392 * @param string $value Value to md5sum
393 * @return string MD5 sum of value
395 function md5Value($value)
400 $server = new Zend_XmlRpc_Server();
401 $server->addFunction('md5Value');
402 echo $server->handle();
406 <example id="zend.xmlrpc.server.use.attach-class">
407 <title>Attaching a class</title>
410 The example below illustrates attaching a class' public methods
411 as dispatchable <acronym>XML-RPC</acronym> methods.
414 <programlisting language="php"><![CDATA[
415 require_once 'Services/Comb.php';
417 $server = new Zend_XmlRpc_Server();
418 $server->setClass('Services_Comb');
419 echo $server->handle();
423 <example id="zend.xmlrpc.server.use.attach-class-with-arguments">
424 <title>Attaching a class with arguments</title>
427 The following example illustrates how to attach a class' public
428 methods and passing arguments to its methods. This can be used to specify certain
429 defaults when registering service classes.
432 <programlisting language="php"><![CDATA[
433 class Services_PricingService
436 * Calculate current price of product with $productId
438 * @param ProductRepository $productRepository
439 * @param PurchaseRepository $purchaseRepository
440 * @param integer $productId
442 public function calculate(ProductRepository $productRepository,
443 PurchaseRepository $purchaseRepository,
450 $server = new Zend_XmlRpc_Server();
451 $server->setClass('Services_PricingService',
453 new ProductRepository(),
454 new PurchaseRepository());
458 The arguments passed at <methodname>setClass()</methodname> at server construction
459 time are injected into the method call <command>pricing.calculate()</command> on
460 remote invokation. In the example above, only the argument <code>$purchaseId</code>
461 is expected from the client.
465 <example id="zend.xmlrpc.server.use.attach-class-with-arguments-constructor">
466 <title>Passing arguments only to constructor</title>
469 <classname>Zend_XmlRpc_Server</classname> allows to restrict argument passing to
470 constructors only. This can be used for constructor dependency injection.
471 To limit injection to constructors, call
472 <methodname>sendArgumentsToAllMethods</methodname> and pass
473 <constant>FALSE</constant> as an argument. This disables the default behavior of all
474 arguments being injected into the remote method. In the example below the instance
475 of <classname>ProductRepository</classname> and
476 <classname>PurchaseRepository</classname> is only injected into the constructor of
477 <classname>Services_PricingService2</classname>.
480 <programlisting language="php"><![CDATA[
481 class Services_PricingService2
484 * @param ProductRepository $productRepository
485 * @param PurchaseRepository $purchaseRepository
487 public function __construct(ProductRepository $productRepository,
488 PurchaseRepository $purchaseRepository)
494 * Calculate current price of product with $productId
496 * @param integer $productId
499 public function calculate($productId)
505 $server = new Zend_XmlRpc_Server();
506 $server->sendArgumentsToAllMethods(false);
507 $server->setClass('Services_PricingService2',
509 new ProductRepository(),
510 new PurchaseRepository());
514 <example id="zend.xmlrpc.server.use.attach-instance">
515 <title>Attaching a class instance</title>
518 <methodname>setClass()</methodname> allows to register a previously instantiated
519 object at the server. Just pass an instance instead of the class name. Obviously
520 passing arguments to the constructor is not possible with pre-instantiated
525 <example id="zend.xmlrpc.server.use.attach-several-classes-namespaces">
526 <title>Attaching several classes using namespaces</title>
529 The example below illustrates attaching several classes, each
530 with their own namespace.
533 <programlisting language="php"><![CDATA[
534 require_once 'Services/Comb.php';
535 require_once 'Services/Brush.php';
536 require_once 'Services/Pick.php';
538 $server = new Zend_XmlRpc_Server();
539 $server->setClass('Services_Comb', 'comb'); // methods called as comb.*
540 $server->setClass('Services_Brush', 'brush'); // methods called as brush.*
541 $server->setClass('Services_Pick', 'pick'); // methods called as pick.*
542 echo $server->handle();
546 <example id="zend.xmlrpc.server.use.exceptions-faults">
547 <title>Specifying exceptions to use as valid fault responses</title>
550 The example below allows any <classname>Services_Exception</classname>-derived
551 class to report its code and message in the fault response.
554 <programlisting language="php"><![CDATA[
555 require_once 'Services/Exception.php';
556 require_once 'Services/Comb.php';
557 require_once 'Services/Brush.php';
558 require_once 'Services/Pick.php';
560 // Allow Services_Exceptions to report as fault responses
561 Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
563 $server = new Zend_XmlRpc_Server();
564 $server->setClass('Services_Comb', 'comb'); // methods called as comb.*
565 $server->setClass('Services_Brush', 'brush'); // methods called as brush.*
566 $server->setClass('Services_Pick', 'pick'); // methods called as pick.*
567 echo $server->handle();
571 <example id="zend.xmlrpc.server.use.custom-request-object">
572 <title>Utilizing custom request and response objects</title>
575 Some use cases require to utilize a custom request object.
576 For example, <acronym>XML/RPC</acronym> is not bound to
577 <acronym>HTTP</acronym> as a transfer protocol. It is possible to use
578 other transfer protocols like <acronym>SSH</acronym> or telnet to send
579 the request and response data over the wire. Another use case is
580 authentication and authorization. In case of a different transfer
581 protocol, one need to change the implementation to read request data.
585 The example below instantiates a custom request object and
586 passes it to the server to handle.
589 <programlisting language="php"><![CDATA[
590 require_once 'Services/Request.php';
591 require_once 'Services/Exception.php';
592 require_once 'Services/Comb.php';
593 require_once 'Services/Brush.php';
594 require_once 'Services/Pick.php';
596 // Allow Services_Exceptions to report as fault responses
597 Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
599 $server = new Zend_XmlRpc_Server();
600 $server->setClass('Services_Comb', 'comb'); // methods called as comb.*
601 $server->setClass('Services_Brush', 'brush'); // methods called as brush.*
602 $server->setClass('Services_Pick', 'pick'); // methods called as pick.*
604 // Create a request object
605 $request = new Services_Request();
607 echo $server->handle($request);
611 <example id="zend.xmlrpc.server.use.custom-response-object">
612 <title>Specifying a custom response class</title>
615 The example below illustrates specifying a custom response class
616 for the returned response.
619 <programlisting language="php"><![CDATA[
620 require_once 'Services/Request.php';
621 require_once 'Services/Response.php';
622 require_once 'Services/Exception.php';
623 require_once 'Services/Comb.php';
624 require_once 'Services/Brush.php';
625 require_once 'Services/Pick.php';
627 // Allow Services_Exceptions to report as fault responses
628 Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
630 $server = new Zend_XmlRpc_Server();
631 $server->setClass('Services_Comb', 'comb'); // methods called as comb.*
632 $server->setClass('Services_Brush', 'brush'); // methods called as brush.*
633 $server->setClass('Services_Pick', 'pick'); // methods called as pick.*
635 // Create a request object
636 $request = new Services_Request();
638 // Utilize a custom response
639 $server->setResponseClass('Services_Response');
641 echo $server->handle($request);
646 <sect2 id="zend.xmlrpc.server.performance">
647 <title>Performance optimization</title>
649 <example id="zend.xmlrpc.server.performance.caching">
650 <title>Cache server definitions between requests</title>
653 The example below illustrates caching server definitions between requests.
656 <programlisting language="php"><![CDATA[
657 // Specify a cache file
658 $cacheFile = dirname(__FILE__) . '/xmlrpc.cache';
660 // Allow Services_Exceptions to report as fault responses
661 Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
663 $server = new Zend_XmlRpc_Server();
665 // Attempt to retrieve server definition from cache
666 if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
667 $server->setClass('Services_Comb', 'comb'); // methods called as comb.*
668 $server->setClass('Services_Brush', 'brush'); // methods called as brush.*
669 $server->setClass('Services_Pick', 'pick'); // methods called as pick.*
672 Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
675 // Create a request object
676 $request = new Services_Request();
678 // Utilize a custom response
679 $server->setResponseClass('Services_Response');
681 echo $server->handle($request);
687 The server cache file should be located outside the document root.
691 <example id="zend.xmlrpc.server.performance.xmlgen">
692 <title>Optimizing XML generation</title>
695 <classname>Zend_XmlRpc_Server</classname> uses
696 <classname>DOMDocument</classname> of <acronym>PHP</acronym>
697 extension <code>ext/dom</code> to generate it's
698 <acronym>XML</acronym> output. While <code>ext/dom</code> is
699 available on a lot of hosts it is not exactly the fastest.
700 Benchmarks have shown, that <classname>XMLWriter</classname>
701 from <code>ext/xmlwriter</code> performs better.
705 If <code>ext/xmlwriter</code> is available on your host, you can
706 select a the <classname>XMLWriter</classname>-based generator
707 to leaverage the performance differences.
710 <programlisting language="php"><![CDATA[
711 require_once 'Zend/XmlRpc/Server.php';
712 require_once 'Zend/XmlRpc/Generator/XMLWriter.php';
714 Zend_XmlRpc_Value::setGenerator(new Zend_XmlRpc_Generator_XMLWriter());
716 $server = new Zend_XmlRpc_Server();
722 <title>Benchmark your application</title>
725 Performance is determined by a lot of parameters and
726 benchmarks only apply for the specific test case. Differences
727 come from <acronym>PHP</acronym> version, installed extensions, webserver and
728 operating system just to name a few. Please make sure to
729 benchmark your application on your own and decide which
730 generator to use based on <emphasis>your</emphasis> numbers.
735 <title>Benchmark your client</title>
738 This optimization makes sense for the client side too. Just
739 select the alternate <acronym>XML</acronym> generator before
740 doing any work with <classname>Zend_XmlRpc_Client</classname>.