Zend_XmlRpc_Server pretende ser un servidor
XML-RPC completo, siguiendo las
especificaciones descritas en www.xmlrpc.com. Además, implementa el
método system.multicall(), permitiendo el empaquetado de peticiones (boxcarring).
Un ejemplo del caso de uso más básico:
$server = new Zend_XmlRpc_Server();
$server->setClass('My_Service_Class');
echo $server->handle();
Zend_XmlRpc_Server está compuesto por una variedad de componentes,
que van desde el propio servidor hasta los objetos de petición, respuesta y fallo.
Para inicializar Zend_XmlRpc_Server, el desarrollador debe adjuntar
una o más clases o funciones al servidor, mediante los métodos
setClass() y addFunction().
Una vez hecho esto, puede pasar un objeto Zend_XmlRpc_Request
a Zend_XmlRpc_Server::handle(), o de lo contrario
se instanciará un objeto Zend_XmlRpc_Request_Http si no
se proporciona ninguno -- obteniendo así la petición desde
php://input.
Zend_XmlRpc_Server::handle() entonces intenta
despachar hacia el manejador apropiado según el método
solicitado. Luego devuelve ya sea un objeto basado en
Zend_XmlRpc_Response o un objeto
Zend_XmlRpc_Server_Fault. Estos objetos tienen ambos
métodos __toString() que crean respuestas
XML-RPC XML válidas, permitiendo que sean
impresas directamente.
Para un máximo rendimiento se recomienda utilizar un archivo de arranque
sencillo para el componente del servidor. Utilizar
Zend_XmlRpc_Server dentro de un
Zend_Controller
se desaconseja firmemente para evitar la sobrecarga.
Los servicios cambian con el tiempo y aunque los servicios web son generalmente menos propensos a cambios que las APIs nativas de código, se recomienda versionar su servicio. Hágalo para sentar las bases para proporcionar compatibilidad a los clientes que utilizan versiones anteriores de su servicio y gestionar el ciclo de vida de su servicio, incluyendo los plazos de obsolescencia. Para hacerlo, simplemente incluya un número de versión en su URI. También se recomienda incluir el nombre del protocolo remoto en la URI para permitir una fácil integración de las próximas tecnologías de acceso remoto. http://myservice.ws/1.0/XMLRPC/.
La mayoría de las veces no es sensato exponer objetos de negocio directamente. Los objetos de negocio suelen ser pequeños y estar sujetos a cambios frecuentes, porque el cambio es barato en esta capa de su aplicación. Una vez desplegado y adoptado, los servicios web son difíciles de cambiar. Otra preocupación es la E/S y la latencia: las mejores llamadas a servicios web son aquellas que no se producen. Por lo tanto, las llamadas a servicios deben tener un grano más grueso de lo que suele tener la lógica de negocio habitual. A menudo tiene sentido una capa adicional delante de sus objetos de negocio. Esta capa a veces se denomina Remote Facade. Dicha capa de servicio añade una interfaz de grano grueso sobre su lógica de negocio y agrupa operaciones detalladas en otras más pequeñas.
Zend_XmlRpc_Server permite al desarrollador adjuntar funciones y
llamadas a métodos de clase como métodos XML-RPC despachables. Mediante
Zend_Server_Reflection, realiza introspección sobre todos los
métodos adjuntados, utilizando los docblocks de las funciones y métodos para determinar el
texto de ayuda del método y las firmas del método.
Los tipos XML-RPC no necesariamente se corresponden uno a uno con los tipos de PHP. Sin embargo, el código hará lo posible por adivinar el tipo apropiado en función de los valores indicados en las líneas @param y @return. Algunos tipos XML-RPC no tienen un equivalente inmediato en PHP, sin embargo, y deben ser indicados mediante el tipo XML-RPC en el PHPDoc. Estos incluyen:
dateTime.iso8601, una cadena con el formato 'YYYYMMDDTHH:mm:ss'
base64, datos codificados en base64
struct, cualquier array asociativo
A continuación se muestra un ejemplo de cómo indicar estos tipos:
/**
* This is a sample function
*
* @param base64 $val1 Base64-encoded data
* @param dateTime.iso8601 $val2 An ISO date
* @param struct $val3 An associative array
* @return struct
*/
function myFunc($val1, $val2, $val3)
{
}
PhpDocumentor no realiza ninguna validación de los tipos especificados para los parámetros o los valores de retorno, por lo que esto no tendrá ningún impacto en su documentación de la API. Sin embargo, proporcionar dichas indicaciones es necesario cuando el servidor valida los parámetros proporcionados a la llamada al método.
Es perfectamente válido especificar múltiples tipos tanto para los parámetros como para los valores de retorno; la especificación XML-RPC incluso sugiere que system.methodSignature debería devolver un array con todas las firmas de método posibles (es decir, todas las combinaciones posibles de valores de parámetros y de retorno). Puede hacerlo tal como lo haría normalmente con PhpDocumentor, utilizando el operador '|':
/**
* This is a sample function
*
* @param string|base64 $val1 String or base64-encoded data
* @param string|dateTime.iso8601 $val2 String or an ISO date
* @param array|struct $val3 Normal indexed array or an associative array
* @return boolean|struct
*/
function myFunc($val1, $val2, $val3)
{
}
![]() |
Nota |
|---|---|
Permitir múltiples firmas puede generar confusión para los desarrolladores que utilizan los servicios; para simplificar las cosas, un método de servicio XML-RPC debería tener una única firma. |
XML-RPC tiene un concepto de espacios de nombres; básicamente, permite agrupar métodos XML-RPC mediante espacios de nombres delimitados por puntos. Esto ayuda a evitar colisiones de nombres entre métodos servidos por distintas clases. Como ejemplo, se espera que el servidor XML-RPC sirva varios métodos en el espacio de nombres 'system':
system.listMethods
system.methodHelp
system.methodSignature
Internamente, estos se corresponden con los métodos del mismo nombre en
Zend_XmlRpc_Server.
Si desea añadir espacios de nombres a los métodos que sirve, simplemente proporcione un espacio de nombres al método correspondiente al adjuntar una función o clase:
// All public methods in My_Service_Class will be accessible as
// myservice.METHODNAME
$server->setClass('My_Service_Class', 'myservice');
// Function 'somefunc' will be accessible as funcs.somefunc
$server->addFunction('somefunc', 'funcs');
La mayoría de las veces, simplemente utilizará el tipo de petición predeterminado incluido con
Zend_XmlRpc_Server,
Zend_XmlRpc_Request_Http. Sin embargo, puede haber ocasiones en las que
necesite que XML-RPC esté disponible a través de la CLI, una
GUI, u otro entorno, o quiera registrar las peticiones entrantes. Para
hacerlo, puede crear un objeto de petición personalizado que extienda
Zend_XmlRpc_Request. Lo más importante que debe recordar es
asegurarse de que los métodos getMethod() y
getParams() estén implementados de forma que el
servidor XML-RPC pueda recuperar esa información para despachar la
petición.
De forma similar a los objetos de petición, Zend_XmlRpc_Server puede devolver
objetos de respuesta personalizados; por defecto, se devuelve un objeto
Zend_XmlRpc_Response_Http, que envía una cabecera HTTP
Content-Type apropiada para su uso con XML-RPC. Posibles usos de un objeto
personalizado serían registrar las respuestas, o enviar respuestas a
STDOUT.
Para utilizar una clase de respuesta personalizada, use
Zend_XmlRpc_Server::setResponseClass() antes de llamar a
handle().
Zend_XmlRpc_Server captura las excepciones generadas por un método
despachado, y genera una respuesta de fallo (fault) XML-RPC cuando se
captura dicha excepción. Sin embargo, por defecto, los mensajes y códigos de
excepción no se utilizan en una respuesta de fallo. Esta es una decisión
intencionada para proteger su código; muchas excepciones exponen más
información sobre el código o el entorno de la que un desarrollador
necesariamente pretendería (un ejemplo típico son las excepciones de la capa
de abstracción o de acceso a base de datos).
Sin embargo, las clases de excepción pueden incluirse en una lista blanca para
usarse como respuestas de fallo. Para ello, simplemente utilice
Zend_XmlRpc_Server_Fault::attachFaultException() para pasar una
clase de excepción a incluir en la lista blanca:
Zend_XmlRpc_Server_Fault::attachFaultException('My_Project_Exception');
Si utiliza una clase de excepción de la que heredan las demás excepciones de su
proyecto, puede entonces incluir en la lista blanca toda una familia de excepciones a
la vez. Las excepciones de tipo Zend_XmlRpc_Server_Exception siempre están
en la lista blanca, para permitir informar de errores internos específicos (métodos
indefinidos, etc.).
Cualquier excepción que no esté específicamente en la lista blanca generará una respuesta de fallo con un código '404' y un mensaje 'Unknown error'.
Adjuntar muchas clases a una instancia del servidor XML-RPC puede utilizar
muchos recursos; cada clase debe hacer introspección utilizando la API de
Reflection (mediante Zend_Server_Reflection), lo que a su
vez genera una lista de todas las posibles firmas de método para proporcionar a la clase del servidor.
Para reducir algo este impacto en el rendimiento, se puede utilizar
Zend_XmlRpc_Server_Cache para almacenar en caché la definición del servidor
entre peticiones. Cuando se combina con __autoload(), esto
puede incrementar considerablemente el rendimiento.
A continuación se muestra un ejemplo de uso:
function __autoload($class)
{
Zend_Loader::loadClass($class);
}
$cacheFile = dirname(__FILE__) . '/xmlrpc.cache';
$server = new Zend_XmlRpc_Server();
if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
require_once 'My/Services/Glue.php';
require_once 'My/Services/Paste.php';
require_once 'My/Services/Tape.php';
$server->setClass('My_Services_Glue', 'glue'); // glue. namespace
$server->setClass('My_Services_Paste', 'paste'); // paste. namespace
$server->setClass('My_Services_Tape', 'tape'); // tape. namespace
Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
}
echo $server->handle();
El ejemplo anterior intenta recuperar una definición del servidor desde xmlrpc.cache en el mismo directorio que el script. Si no lo consigue, carga las clases de servicio que necesita, las adjunta a la instancia del servidor, y luego intenta crear un nuevo archivo de caché con la definición del servidor.
A continuación se muestran varios ejemplos de uso, que muestran todo el espectro de opciones disponibles para los desarrolladores. Los ejemplos de uso se irán construyendo cada uno sobre el ejemplo anterior proporcionado.
Example 80.8. Basic Usage
El ejemplo a continuación adjunta una función como un método XML-RPC despachable y gestiona las llamadas entrantes.
/**
* Return the MD5 sum of a value
*
* @param string $value Value to md5sum
* @return string MD5 sum of value
*/
function md5Value($value)
{
return md5($value);
}
$server = new Zend_XmlRpc_Server();
$server->addFunction('md5Value');
echo $server->handle();
Example 80.9. Attaching a class
El ejemplo a continuación ilustra cómo adjuntar los métodos públicos de una clase como métodos XML-RPC despachables.
require_once 'Services/Comb.php';
$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb');
echo $server->handle();
Example 80.10. Attaching a class with arguments
El siguiente ejemplo ilustra cómo adjuntar los métodos públicos de una clase y pasar argumentos a sus métodos. Esto puede utilizarse para especificar ciertos valores predeterminados al registrar clases de servicio.
class Services_PricingService
{
/**
* Calculate current price of product with $productId
*
* @param ProductRepository $productRepository
* @param PurchaseRepository $purchaseRepository
* @param integer $productId
*/
public function calculate(ProductRepository $productRepository,
PurchaseRepository $purchaseRepository,
$productId)
{
...
}
}
$server = new Zend_XmlRpc_Server();
$server->setClass('Services_PricingService',
'pricing',
new ProductRepository(),
new PurchaseRepository());
Los argumentos pasados en setClass() en el momento de
construir el servidor se inyectan en la llamada al método
pricing.calculate() en la invocación remota. En el ejemplo anterior,
sólo se espera del cliente el argumento
$purchaseId.
Example 80.11. Passing arguments only to constructor
Zend_XmlRpc_Server permite restringir el paso de argumentos
únicamente a los constructores. Esto puede utilizarse para la inyección de
dependencias en el constructor. Para limitar la inyección a los constructores, llame a
sendArgumentsToAllMethods y pase
FALSE como argumento. Esto deshabilita el comportamiento predeterminado de
inyectar todos los argumentos en el método remoto. En el ejemplo siguiente, la instancia
de ProductRepository y
PurchaseRepository sólo se inyecta en el constructor de
Services_PricingService2.
class Services_PricingService2
{
/**
* @param ProductRepository $productRepository
* @param PurchaseRepository $purchaseRepository
*/
public function __construct(ProductRepository $productRepository,
PurchaseRepository $purchaseRepository)
{
...
}
/**
* Calculate current price of product with $productId
*
* @param integer $productId
* @return double
*/
public function calculate($productId)
{
...
}
}
$server = new Zend_XmlRpc_Server();
$server->sendArgumentsToAllMethods(false);
$server->setClass('Services_PricingService2',
'pricing',
new ProductRepository(),
new PurchaseRepository());
Example 80.12. Attaching a class instance
setClass() permite registrar en el servidor un objeto
previamente instanciado. Simplemente pase una instancia en lugar del nombre de la clase.
Obviamente, pasar argumentos al constructor no es posible con objetos
ya instanciados.
Example 80.13. Attaching several classes using namespaces
El ejemplo a continuación ilustra cómo adjuntar varias clases, cada una con su propio espacio de nombres.
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb', 'comb'); // methods called as comb.*
$server->setClass('Services_Brush', 'brush'); // methods called as brush.*
$server->setClass('Services_Pick', 'pick'); // methods called as pick.*
echo $server->handle();
Example 80.14. Specifying exceptions to use as valid fault responses
El ejemplo a continuación permite que cualquier clase derivada de
Services_Exception informe de su código y mensaje en la respuesta de fallo.
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
// Allow Services_Exceptions to report as fault responses
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb', 'comb'); // methods called as comb.*
$server->setClass('Services_Brush', 'brush'); // methods called as brush.*
$server->setClass('Services_Pick', 'pick'); // methods called as pick.*
echo $server->handle();
Example 80.15. Utilizing custom request and response objects
Algunos casos de uso requieren utilizar un objeto de petición personalizado. Por ejemplo, XML/RPC no está vinculado a HTTP como protocolo de transferencia. Es posible utilizar otros protocolos de transferencia como SSH o telnet para enviar los datos de petición y respuesta a través de la red. Otro caso de uso es la autenticación y autorización. En el caso de un protocolo de transferencia diferente, es necesario cambiar la implementación para leer los datos de la petición.
El ejemplo a continuación instancia un objeto de petición personalizado y lo pasa al servidor para que lo gestione.
require_once 'Services/Request.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
// Allow Services_Exceptions to report as fault responses
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb', 'comb'); // methods called as comb.*
$server->setClass('Services_Brush', 'brush'); // methods called as brush.*
$server->setClass('Services_Pick', 'pick'); // methods called as pick.*
// Create a request object
$request = new Services_Request();
echo $server->handle($request);
Example 80.16. Specifying a custom response class
El ejemplo a continuación ilustra cómo especificar una clase de respuesta personalizada para la respuesta devuelta.
require_once 'Services/Request.php';
require_once 'Services/Response.php';
require_once 'Services/Exception.php';
require_once 'Services/Comb.php';
require_once 'Services/Brush.php';
require_once 'Services/Pick.php';
// Allow Services_Exceptions to report as fault responses
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
$server = new Zend_XmlRpc_Server();
$server->setClass('Services_Comb', 'comb'); // methods called as comb.*
$server->setClass('Services_Brush', 'brush'); // methods called as brush.*
$server->setClass('Services_Pick', 'pick'); // methods called as pick.*
// Create a request object
$request = new Services_Request();
// Utilize a custom response
$server->setResponseClass('Services_Response');
echo $server->handle($request);
Example 80.17. Cache server definitions between requests
El ejemplo a continuación ilustra cómo almacenar en caché las definiciones del servidor entre peticiones.
// Specify a cache file
$cacheFile = dirname(__FILE__) . '/xmlrpc.cache';
// Allow Services_Exceptions to report as fault responses
Zend_XmlRpc_Server_Fault::attachFaultException('Services_Exception');
$server = new Zend_XmlRpc_Server();
// Attempt to retrieve server definition from cache
if (!Zend_XmlRpc_Server_Cache::get($cacheFile, $server)) {
$server->setClass('Services_Comb', 'comb'); // methods called as comb.*
$server->setClass('Services_Brush', 'brush'); // methods called as brush.*
$server->setClass('Services_Pick', 'pick'); // methods called as pick.*
// Save cache
Zend_XmlRpc_Server_Cache::save($cacheFile, $server);
}
// Create a request object
$request = new Services_Request();
// Utilize a custom response
$server->setResponseClass('Services_Response');
echo $server->handle($request);
![]() |
Nota |
|---|---|
El archivo de caché del servidor debe ubicarse fuera de la raíz del documento. |
Example 80.18. Optimizing XML generation
Zend_XmlRpc_Server utiliza
DOMDocument de la extensión de PHP
ext/dom para generar su salida
XML. Aunque ext/dom está
disponible en muchos hosts, no es precisamente la más rápida.
Los benchmarks han demostrado que XmlWriter
de ext/xmlwriter rinde mejor.
Si ext/xmlwriter está disponible en su host, puede
seleccionar el generador basado en XmlWriter
para aprovechar las diferencias de rendimiento.
require_once 'Zend/XmlRpc/Server.php'; require_once 'Zend/XmlRpc/Generator/XmlWriter.php'; Zend_XmlRpc_Value::setGenerator(new Zend_XmlRpc_Generator_XmlWriter()); $server = new Zend_XmlRpc_Server(); ...
![]() |
Realice benchmarks de su aplicación |
|---|---|
El rendimiento está determinado por muchos parámetros y los benchmarks sólo aplican al caso de prueba concreto. Las diferencias provienen de la versión de PHP, las extensiones instaladas, el servidor web y el sistema operativo, por nombrar sólo algunos. Asegúrese de realizar sus propios benchmarks de su aplicación y decidir qué generador utilizar en función de sus propios números. |
![]() |
Realice benchmarks de su cliente |
|---|---|
Esta optimización también tiene sentido en el lado del cliente. Simplemente
seleccione el generador XML alternativo antes de
realizar cualquier trabajo con |
![[Note]](images/note.png)