&reftitle.examples;
Los ejemplos de las secciones subsiguientes ilustran los
siguientes aspectos de SCA para PHP:
Cómo se usan las anotaciones de PHP para definir clases de PHP como componentes
SCA, y cómo se usan las anotaciones para definir los
servicios.
Cómo un componente SCA puede ser expuesto como un servicio web.
Cómo un componente SCA puede consumir un servicio web, ya sea
proporcionado por otro componente SCA o por algún otro servicio que
no sepa nada de SCA.
Cómo un componente SCA puede llamar a otro componete SCA localmente
(dentro del mismo proceso en la misma plica de llamadas).
Cómo un script cliente que no es un componente SCA puede usar la
llamada a getService para optener un delegado para un componente SCA.
Cómo las estructuras de datos, tales como Direcciones o Pedidos de Compra,
están represetadas como Objetos de Datos de Servicio (SDOs), y son manejadas.
Cómo se implementan los componentes SCA, y en particular cómo y
cuando se genera WSDL para un servicio.
Cómo los parmétros son siempre pasados por valor (y no por
referencia) entre componentes, incluso cuando las llamadas son locales.
Esto aseguna que la semántica de una llamada no cambie según
la ubicación de un componente.
Cómo están soportados los parámetros posicionales para un servicio, incluso
cuando el WSDL es un documento literal envuelto, y
naturalmente admite únicamente parámetros con nombre.
Cómo se manejan las excepciones de negocio y en tiempo de ejecución.
La estructura de un Componente de Servicio
Un componente de servicio es implementado por una clase. Para identificarlo
como un componente de servicio, contiente una anotación @service. El tiempo de
ejecución d SCA usará el nombre del fichero del script para determinar el
nombre del componente, por convención. La clase y el fichero del script deben,
por lo tanto, compartir el mismo nombre.
Los componentes de SCA para PHP siempre exponen un servicio, y no exite otra forma
de invocar a un componente sino que sea llamado como un resultado de una
petición de servidio web, o llamado directamente desde otro componente o
desde un script. Por este motivo, un componente SCA de PHP válido siempre
contienen una anotación @service y al menos un método público.
Cada componente SCA requiere que el script SCA.php esté
incluido. Además de contener la definición de la clase SCA,
el script contiene código de PHP que será ejecutado siempre que el
script sea invocado, y que será responsable de hacer que el
componente se comporte como sea neceario.
Es muy importante que, si el fichero contiene otras
inclusiones, estas vengan antes de la inclusión de SCA.php. Si hubieran
inclusiones después de la del SCA.php, no serán
procesadas cuando el tiempo de ejecución de SCA ejecute la clase.
El ejemplo de abajo ilustra esta estructura en general.
La estructura de un componente SCA para PHP
]]>
Obtener un delegado para otro Componente de Servicio
Un componente SCA puede llamar al servicio proporcionado por otro componente
SCA. El servicio que proporciona un componente está hecho de todos sus
métodos públicos. SCA para PHP actualemnte proporciona dos maneras de que un
componente llame a otro: localmente (esto es, dentro del mismo tiempo de ejecución
de PHP, y sobre la misma pila de llamadas) o remotamente si el componente
llamador expone una vinculación de servicio web.
Para que un componente llame a otro, el llamador
necesita un delgado para el componente llamado. Este delegado es
proporcionado normalmente por una variable de instancia en el componente llamador,
aunque los delegados también se pueden obtener con la llamada a
SCA::getService(), como se verá más tarde. Cuando se construye un componente,
los delegados son construidos para cualquier variable de instancia que recurra a
otro componente, ya que estos delegados están "inyectados" en las
variables de instancia. Los delegados se usan siempre, ya sea si el
componente es local o remoto, para proporcinar un comportamiento de llamada
idéntico entre llamadas remotas y locales (por ejemplo, las llamadas locales
se hacen siempre para pasar datos por valor). Los delegados saben cómo
localizar el componente requerido y pasar las llamadas realizadas sobre ellos.
Las variables de instancia, que tienen por objeto mantener delgeados para
servicio, están indicados por dos anotaciones estilo PHPDocumentor,
@reference y @binding. Ambas están ubicadas
en la sección de documentación de una variable de instancia de clase,
como se muestra en el código de abajo.
La anotación @reference antes de una variable de instancia
indica que dicha variable se va a inicializar con un
delegado a un componente.
La anotación @binding posee dos formas: @binding.php y
@binding.soap, e indica qu el delegado es para un componente
local o para un servicio web, respectivamente. Para ambas, @binding.php
y @binding.soap, la anotación proporciona una URI objetivo.
Por el momento, con el método basado en anotaciones de
especificación de dependencias, la única forma de alterar el objetivo planeado
de una referencia es alterar la anotación dentro del componente.
En el ejemplo de ConvertedStockQuote, la
variable de instancia $exchange_rate estará
inicializada con un delegado para el componente local ExchangeRate
siempre que se construya una instancia de ConvertedStockQuote.
Obtener un delegado para unca clase local de PHP
]]>
Para @binding.php, la URI identifica la ubicación del
script que contiene la implementación del componente. El
componente será invocado localmente. El servicio proporcionado es el conjunto de
métodos públicos del componente. La URI debe ser un nombre de ruta simple,
ya sea absoluta o relativa. El componente se cargará con la
directiva include de PHP, después de comprobar si ya está cargado
con
class_exists. Si la URI es una ruta
relativa, se resuelve relativa al componente que contiene la
anotación. Observe que esto es diferente del comportamiento normal
de PHP, donde los scripts deberían buscar por el include_path
de PHP. Esto tiene por objeto proporcionar independencia
de ubicaciones para referencias de componentes cruzadas.
Si este servicio ExchangeRate fuera remoto y fuese llamad como un
servicio web, solamente cambia la línea @binding. En lugar del proporcionar la
ubicación de una clase de PHP, proporciona la ubicación del WSDL que describe
el servicio web. En el componente del ejemplo, esto está ilustrado por
la segunda referencia:
Obtener un delgado para un servicio web
]]>
El componente StockQuote será invocado mediante una petición de servicio
web. En este caso la URI para el WSDL puede ser un nombre de ruta sencillo, o
podría contener una envoltura de PHP y comenzar con, por ejemplo,
file:// o
http://. En el caso que sea un nombre de ruta
sencillo, puede ser absoluto o relativo, y si es relativo será
resulto relativo al componente que contiene la anotación.
Observe que este comportamiento es como el de @binding.php, pero
diferente del comportamiento noraml de PHP donde se buscaría el fichero
relativo al directorio de trabajo actual de PHP, el cual
sería normalmente al ubicación del primer script a ser llamado. Este
comportamiento tiene como objeto otorgar consistencia a través de las diferentes
vinculaciones y proporcionar algo de independencia de ubicación para
referencias entre componentes.
Invocar a otro Componente de Servicio
El ejemplo ConvertedStockQuote también invoca a los delegados para
los dos componentes a los que hace referencia.
Llamar a servicios
stock_quote->getQuote($ticker);
$rate = $this->exchange_rate->getRate($currency);
?>
]]>
La llamada al servicio StockQuote es una llamada a un servicio local;
la llamada al servicio ExchangeRate es una llamada a un servicio remoto.
Observe que las llamadas se realizan de la misma manera, sin tener en cuenta
si la llamada es a un servicio local o a uno remoto.
Los delegados que han sido inyectados se aseguran de que la manera de llamar
a los componentes y el comportamiento sea el mismo, ya se trate de un servicio
local o de uno remoto, por lo que los componentes no dependen
de si una llamada es a un servicio local o a uno remoto. Por
ejemplo, el delegado para un servicio local copia los argumentos
y únicamente pasa esas copias, para asegurarse de que las llamadas sean por
paso por valor, como lo harían para una llamada remota. También, el delegado
para un servicio remoto toma los argumentos desde una lista de parámetros
posicionales y se asegura de que están empaquetados apropiadamente en una petición
SOAP y vueltos a convertir a una lista de parámetros posicionales al final de
todo.
En el ejemplo de arriba,
$ticker y
$currency son claramente tipos escalares de PHP.
Los componentes puede pasar los tipos escalares de PHP string, integer, float y
boolean, pero las estructuras de datos sobre llamadas a servicios son siempre pasadas como
Objetos de Datos de Servicio (SDOs). En un sección posterior se describe cómo un
componente puede crear un SDO y pasarlo a una llamada a un servicio local o web, o
cómo un componente puede crear un SDO para devolverlo. La documentación de proyecto
SDO de PHP describe cómo trabajar con las APIs de SDO (véanse
las páginas de SDO).
Localizar e invocar a servicios desde un script que no es un
Componente SCA
Los componentes SCA obtienen delegados para otros componentes o
servicios como variables de instancia anotadas con @reference, pero
esto no es posible para un script que no sea también un componente.
Un script cliente que no es un componente debe usar el
método estático SCA::getService para obtener un
delegado para un servicio, ya sea local o remoto. El
método getService toma una URI como
argumento. Normalmente, esta es la ubicación de un script de PHP local
que contiene un componente, o un fichero WSDL, y se usa exactamente de la
misma manera que los objetivos de las anotaciones @binding descritas en la
sección anterior: esto es, las URIs relativas se resuelven en la
ubicación del script cliente y no en el include_path de PHP
o en el directorio de trabajo actual.
Por ejemplo, un script que necesite obetener delegados para los
servicios ExchangeRate y StockQuote, aunque no un compoentne,
usarían el método
getService como sigue:
Obtener un delegado usando getService
]]>
Los métodos en servicios se pueden invocar entonces en el delegado devuelto,
tal como se puede en un componente.
Realizar llamadas en el componente
getQuote($ticker);
$rate = $exchange_rate->getRate($currency);
?>
]]>
Exponer un Componente de Servicio como un servicio web
SCA para PHP puede generar WSDL desde anotaciones dentro de un
componente de servicio, por lo que puede ser fácilmente implementado y expuesto como un
servicio web. Para porporcionar a SCA la información que necesita para
generar el WSDL, es necesario añadir la anotación
@binding.soap bajo la anotación @service, y especificar los
parámetros y devolver valores de los métodos usando las anotaciones @param y
@return. Estas anotaciones serán leídas cuando el WSDL sea
generado, y el orden y tipo de los parámetros determinará el
contenido de la
sección <schema> del WSDL.
SCA para PHP siempre genera WSDL document/literal envueltos
para componentes que exponen un servicio web. Observe que esto no hace
que los componentes dejen de consumir servicios web que no son componentes
SCA y que estén documentados con WSDL escrito en un
estilo diferente.
Los tipos escalares que puede usarse en la anotación @param son
los cuatro tipos escalares comunes de PHP: boolean, integer, float y
string. Simplemente se hacen corresponder con los tipos del esquema XML del mismo
nombre en el WSDL. El ejemplo de abajo, que es una implementación
trivial del servicio StockQuote que el
componente ConvertedStockQuote llama, ilustra los tipos string y
float.
El servicio StockQuote
]]>
Se generará WSDL muy parecido a lo siguiente para este
servicio (aunque con una ubicación para el servicio
distinta de 'localhost', probablemente):
WSDL generado
]]>
Implementar un componente SCA
No son necesarios pasos especiales para implementar un componente SCA
para PHP. Es suficiente colocar el script de PHP del componente en su
lugar adecuado bajo la raíz de documentos del servidor web, al igual que cualquier otro
script de PHP. Es la línea ejecutable
SCA::initComponent
dentro de cada componente la que será ejecutada siempre que el script sea
invocado, y la que será responsable de hacer que el comoenente
responda de la forma adecuada a las llamadas a los servicios web, llamadas locales, o
peticiones de WSDL.
Obtener el WSDL para un componente SCA que ofrece un Servicio como
servicio web
Los componentes SCA que exponen una interfaz de servicio web (esto es, tienen
una anotación @binding.soap) devolverán su definición WSDL en
respuesta a una petición HTTP con un parámetro 'get' de "wsdl". La forma
usual de obtener esto es con "?wsdl" al final de una URL. El ejemplo de
abajo utiliza
file_get_contents para obtener WSDL desde un
servicio y escribirlo a un fichero temporal antes de obtener un delegado
para el servicio de la manera usual. Por supuesto, se podría también
obtener el WSDL en un navegador, o mediante otros medios, y guardar el fichero
uno mismo.
Generated WSDL
]]>
NOTA: si el wsdl requiere xsds importado, será necesario que este sea
obtenido por separado.
Comprender cómo se genera el WSDL
SCA para PHP genera WSDL para componentes que contienen una
anotación @binding.soap después de la anotación @service. Para
generar WSDL, el tiempo de ejecución de SCA reflexiona sobre el componente y
examina las anotaciones @param y @return para cada método
público, así como también cualquier anotación @types dentro del componente. La
información desde las anotaciones @param y @return se usa para
construir la sección <types> del WSDL. Cualquier anotación
@types que especifique un fichero de esquema distinto resultará en un
elemento <import> para tal esquema dentro del WSDL.
El atributo 'location' del elemento <service>
Al final del WSDL se encuentra el elmento <service>,
que usa el atributo de ubicación 'location' para identificar la URL del
servicio. Por ejemplo, esto podría ser como sigue:
El atrubuto 'location'
]]>
Observe que esta ubicación es relaitva a la raíz de documentos del
servidor web, y no puede ser resuelta de antemano. Solamente puede
hacerse una vez que el componente esté en su propio lugar bajo un servidor
web en ejecución, cuando el nombre de host y el puerto puedan ser conocidos y colocados en
el WSDL. Se usan los detalles de la URL que solicita el WSDL, por lo que, por
ejemplo, si el WSDL es generado en respuesta a una petición a
http://www.example.com:1111/ConvertedStockQuote/ConvertedStockQuote.php?wsdl,
se insertará la ubicación
http://www.example.com:1111/ConvertedStockQuote/ConvertedStockQuote.php
en el atributo 'location' del WSDL.
WSDL document/literal envuelto y parámetros
posicionales
SCA para PHP genera WSDL al estilo document/literal
envuelto. Este estilo encierra los parámetros y devuelve los tipo de un
método en "envolturas" que son nombradas después des método
correspondiente. El elemento <types> al incio del WSDL define
cada envoltura. Si se considera el
método getQuote del
ejemplo ConvertedStockQuote:
Método con dos arguentos
stock_quote->getQuote($ticker);
$rate = $this->exchange_rate->getRate($currency);
return $rate * $quote;
}
?>
]]>
El WSDL generado para definir este método nombrará a los métodos
y a los parámetros, y proporcionará un tipo de esquema XML para los
parámetros. La sección de tipos del WSDL se parecería a esto:
La sección de tipos ilustrando parámetros con nombre
]]>
El tiempo de ejecución de SCA procesa de manera especial la conversión de
las listas de parámetros posicionales de la interfaz a XML que contiene
parámetros con nombre en la solicitud soap, y luego vueltas a convertir
a listas de parámetros posicionales. Para ver por qué importa esto,
considere cómo un script de PHP que usó una interfaz diferente para hace una
llamada SOAP necesitaría construir la lista de parámetros. Un script de PHP
que use un SoapClient de PHP, por ejemplo, necesitaría pasar al
SoapClient un único parámetro proporcionando los valores para "ticker" y
"currency", quizás como un array asociativo. Para insistir que los componentes
SCA construyen listas de parámetros para hacer llamadas a servicios web de
esta manera, sería hacer que las llamadas locales y remotas fueran diferentes, por lo que
sería necesario un enfoque diferente.
Cuando SCA genera WSDL para un componente SCA, incluye un
comentario en el WSDL que marca dicho WSDL como la interfaz para
un componente SCA. En este caso, cuando un componente SCA invoca
a otro a través de un servicio web, el tiempo de ejecución de SCA al finalizar la llamada
toma la lista de parámetros posicionales desde la llamada y asigna los
valores uno a uno a los elementos con nombre del mensaje soap. Por ejemplo,
una llamada al método
getQuote definido arriba que
pasa los valores 'IBM' y 'USD' podría ser así:
getQuote('IBM','USD');
?>
]]>
lo que resultará en un mensaje soap que coteiene los
siguiente:
IBMUSD
]]>
Al final de proporcionar el servicio, el tiempo de ejecución de SCA toma los
parámetros una a uno desde el mensaje soap y forma una lista de parámetros
posicionales a partir de ellos, reformando la lista de argumentos
('IBM','USD').
Al finalizar ambos, el tiempo de ejecución de SCA cuenta con que el
orden en que aparecen los parámetros en el mensaje soap sea el mismo que el de la
lista de parámetros de los métodos objetivo. Esto se determina básicamente
por el orden de las anotaciones @param: determina el orden
en el que aparacen los parámetros en el WSDL y por lo tanto el orden en
que aparacen el el mensaje soap. Así, es esencial
que el orden de las anotaciones @param coincida con el de los
parámetros de la lista de parámetros del método.
Trabajar con estructuras de datos
Los componente SCA pueden pasar y devolver los cuatro tipos escalares de PHP
boolean, integer, float y string, pero para pasar o devolver estructuras de
datos, los componentes SCA usan Objetos de Datos de Servicio (SDOs). Los SDOs
están descritos con más detalle en las
páginas de SDO de este manual.
Los lectores familiarizados con SDOs sabrán que son adecuados para
representar el tipo de datos estructurados y semiestructurados que
frecuentemente se modelan en XML, y que serializan de forma muy
natural para pasarlos entre componentes remotos, o en servicios
web. Los SDOs son actualmente la única manera soportada de pasar y
devolver estructuras de datos. No es posible pasar o devolver objetos
de PHP, o arrays de PHP.
El tiempo de ejecución de SCA siempre se asegura de que los datos sean pasados por valor, incluso
para llamadas locales. Para hacer esto, el tiempo de ejecución de SCA copia cualquier SDO de la
lista de parámetros antes de pasarlo, al igual que hace con los tipos
escalares.
Cómo se definen estructuras de datos para componentes SCA
Actualemtne, el único mecanismo para especificar la ubicación de
una definición de una estructura de datos, es especificar los tipos en un fichero
de esquema XML. Sin embargo, en el futuro podría ser posible definir
tipos de otra manera, tal como basadas en clases o interfaces de PHP, o
basadas en definiciones expresadas como arrays asociativos.
Para ilustrar el uso de SDOs, se introduce un nuevo componente.
El servico PortfolioMangement de abajo devuelve un SDO
que representa una cartera de acciones para un cliente dado.
Un componente que usa estructuras de datos
createDataObject('holding');
$holding->ticker = 'AAPL';
$holding->number = 100.5;
$holding = $portfolio->createDataObject('holding');
$holding->ticker = 'INTL';
$holding->number = 100.5;
$holding = $portfolio->createDataObject('holding');
$holding->ticker = 'IBM';
$holding->number = 100.5;
return $portfolio;
}
}
?>
]]>
La anotación @types:
]]>
indica que los tipos del espacio de nombres
http://www.example.org/Portfolio se encontrarán en el fichero
de esquema ubicado en la URI URI PortfolioTypes.xsd. El WSDL generado
reproducirá esta información con una sentencia importante, como
sigue:
]]>
por lo que la URI, absoluta o relativa, debe ser una que pueda ser
resuelta al incluirla en el atributo 'schemaLocation'.
Crear SDOs
Los lectores familiarizados con SDOs sabrán que siempre son
creados según una descripción de la estructura permitida
(algunas veces llamada como el "esquema" o "modelo") y que,
en vez de crearlos usando directamente 'new', es necesaria alguna
forma de fábrica de datos. A menudo, se puede usar un objeto de datos existente como la
fábrica de datos, pero otras veces, y especialmente para obtener el
primer objeto de daots, otra cosa debe actuar como fábrica de datos.
En SCA, tanto la clase en tiempo de ejecución de SCA o los delegados para
servicios, ya sean locales o remotos, pueden actuar como las fábricas de datos
para SDOs. La elección de cúal usar, y cuando, está descrita en las dos
secciones siguientes.
Cambiamos a un nuevo ejemplo para ilustrar la creación
de SDOs, para pasarlo a, y ser devuelto por un servicio.
Crear un SDO para pasárselo a un servicio
Un llamador de un servicio que requiera que una estructura de datos
le sea pasada, utiliza un delegado para el servicio como la fábrica de datos para
los correspondientes SDOs. Por ejemplo, suponga que un componente hace
uso de un delegada para un servicio proporcionado por un componente AddressBook
local.
]]>
El componente AddressBook que se desea llamar está definido
como sigue:
]]>
El componente AddressBook proporciona un método de servicio llamado
lookupAddress que usa los tipos del
espacio de nombres http://addressbook namespace. el método lookupAddress toma una
estructura de datos personType y devuelve un addressType. Ambos tipos
están definido en el fichero de esquema addressbook.xsd.
Una vez que el componente que desea usar el componente
AddressBook haya sido construido, de modo que la
instancia variable $address_book contiene
un delegado para el servicio, el componente llamador puede usar el delegado en
$address_book para crear el SDO 'person', tal como
se muestra abajo:
createDataObject('http://addressbook','personType');
$william_shakespeare ->name = "William Shakespeare";
$address = $address_book->lookupAddress($william_shakespeare);
?>
]]>
Observe que el uso del delegado como el medio de crear el SDO no está
limitado a componentes SCA. Si un servicio está siendo llamado desde un
script de PHP general, y el delegado fue obtenido con
getService, entonces se usa el mismo
enfoque.
createDataObject('http://addressbook','personType');
?>
]]>
Crear un SDO SDO para devolverlo desde un componente
Un componente que necesita crear un objeto de datos para devolverlo a un
llamador no tendrá un delgado que usar como objeto de datos. En este caso
usa el
método estático createDataObject de
SCA.php. Por lo tanto, si el componente
AddressBook descrito arriba necesita crear un objeto del tipo
addressType dentro del espacio de nombres
http://addressbook, podría hacerlo como sigue:
]]>
Manejo de errores
Esta sección describe cómo se manejan los errores. Existen dos
tipos de errores:
Las excepciones en tiempo de ejecución de SCA son aquellas que señalan
problemas en el manejo de la ejecución de componentes, y en la
interacción con servicios remotos. Podrían ocurrir debido a
problemas de red o de configuración.
Las excpeciones de negocio son aquellas definidas por el
programador. Amplían la clase Exception de PHP, y son lanzadas
y capturadas deliberadamente como parte de la lógica de negocio.
Manejo de excepciones en tiempo de ejecución
Hay dos tipos de excepciones en tiempo de ejecución de SCA:
SCA_RuntimeException - señala un problema encontrado por o
quizás ocurrido dentro del tiempo de ejecución de SCA. Puede ser lanzada por
varias razones, muchas de las cuales pueden ocurrir sin tener en cuenta si
si se ha realizado una conexión a un servicio local o remoto:
un error en una de las anotaciones de un componente, un fichero
WSDL o php faltante, etc. En el caso de servicios web, una
SCA_RuntimeException también puede ser lanzada si se recibe un
SoapFault desde un servicio web remoto y el código de error del
SoapFault indica que sea improbable que tenga éxito un reintento.
SCA_ServiceUnavailableException - esta es una subclase de
SCA_RuntimeException y señala un problema al conectarse a, o
usar un servicio remoto, aunque podría tener éxito si se reintenta.
En el caso de servicios web, esta excepción es lanzada si se
recibe un SoapFault con un código de error que indique que un
reintento podría tener éxito.
Manejo de excepciones de negocio
Las excepciones de negocio pueden ser definidas y lanzadas por un componente
de la manera usual, sin tener en cuenta si el componente ha sido
invocado local o remotamente. El tiempo de ejecución de SCA no captura
las excepciones de negocio que han sido lanzadas por un componente invocado
localmente, por lo que serán devueltas al llamador de la manera usuarl. Por otro
lado, si un componente ha sido invocado mediante un servicio web, el tiempo
de ejecución de SCA, al final de proporcionar el servicio, captura las excepciones
de negocio, y se asegurará de que sean pasadas otra vez al final
de la llamada y sena relanzadas. Asumiento que al final de la llamada hay una definición
de la excepción (esto es, es capaz de incluir un fichero que contenga la
clase de PHP que contenga la excepción) la excepción relanzada
contendrá los mismos detalles que la original, por lo que los métodos
getLine y
getFile, por ejemplo, contendrán
la ubicación de donde fue lanzada la excepción dentro de la lógica
de negocio. La excepción será pasada en el campo 'detail' de un error
soap con el código de error "Client".