|
|
@@ -0,0 +1,776 @@
|
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
|
+<!-- EN-Revision: 24249 -->
|
|
|
+<!-- Reviewed: no -->
|
|
|
+<sect1 id="zend.amf.server">
|
|
|
+ <title>Zend_Amf_Server</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <classname>Zend_Amf_Server</classname> fornece um servidor no estilo <acronym>RPC</acronym>
|
|
|
+ para manipular requisições feitas com Adobe Flash Player usando o protocolo
|
|
|
+ <acronym>AMF</acronym>. Como todas as classes de servidor do Zend Framework, ela segue a
|
|
|
+ <acronym>API</acronym> SoapServer, fornecendo uma interface fácil de lembrar para criar
|
|
|
+ servidores.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <example id="zend.amf.server.basic">
|
|
|
+ <title>Servidor AMF Básico</title>
|
|
|
+ <para>
|
|
|
+ Vamos assumir que você tenha criado uma classe <classname>Foo</classname> com uma
|
|
|
+ variedade de métodos públicos. Você pode criar um servidor <acronym>AMF</acronym> usando
|
|
|
+ o código a seguir:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$server = new Zend_Amf_Server();
|
|
|
+$server->setClass('Foo');
|
|
|
+$response = $server->handle();
|
|
|
+echo $response;
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Alternativamente, você pode optar por atribuir uma simples função como chamada de
|
|
|
+ retorno:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$server = new Zend_Amf_Server();
|
|
|
+$server->addFunction('myUberCoolFunction');
|
|
|
+$response = $server->handle();
|
|
|
+echo $response;
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Você também pode misturar e combinar várias classes e funções. Quando fizer isso,
|
|
|
+ sugerimos que utilize namespaces para que nenhuma colisão de nomes de métodos ocorra;
|
|
|
+ isso pode ser feito simplesmente passando uma string como segundo argumento, tanto para
|
|
|
+ <methodname>addFunction()</methodname> ou <methodname>setClass()</methodname>.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$server = new Zend_Amf_Server();
|
|
|
+$server->addFunction('myUberCoolFunction', 'my')
|
|
|
+ ->setClass('Foo', 'foo')
|
|
|
+ ->setClass('Bar', 'bar');
|
|
|
+$response = $server->handle();
|
|
|
+echo $response;
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ O <classname>Zend_Amf_Server</classname> também permite que serviços sejam
|
|
|
+ dinamicamente carregados baseado em um caminho de diretório fornecido. Você pode
|
|
|
+ adicionar tantos quantos diretórios desejar ao servidor. A ordem em que você adiciona os
|
|
|
+ diretórios ao servidor será a ordem em que a pesquisa <acronym>LIFO</acronym> será
|
|
|
+ realizada nos diretórios para encontrar a classe. Adição de diretórios é feita com
|
|
|
+ o método <methodname>addDirectory()</methodname>.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$server->addDirectory(dirname(__FILE__) .'/../services/');
|
|
|
+$server->addDirectory(dirname(__FILE__) .'/../package/');
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Ao chamar serviços remotos, os nomes de seus fontes podem conter sublinhados ("_") e
|
|
|
+ pontos (".") como delimitadores de diretórios. Quando um sublinhado é usado, as
|
|
|
+ conveções <acronym>PEAR</acronym> e Zend Framework para nomenclaturas serão respeitadas.
|
|
|
+ Isso significa que se você chamar um serviço <filename>com_Foo_Bar</filename> o servidor
|
|
|
+ procurará pelo arquivo <filename>Bar.php</filename> em cada caminho incluído em
|
|
|
+ <filename>com/Foo/Bar.php</filename>. Se a notação de ponto é usada para seu serviço
|
|
|
+ remoto como em <filename>com.Foo.Bar</filename>, <filename>com/Foo/Bar.php</filename>
|
|
|
+ será adicionado ao final de cada caminho incluído para autocarregar
|
|
|
+ <filename>Bar.php</filename>
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Todas as requisições <acronym>AMF</acronym> enviadas ao script serão manipuladas
|
|
|
+ posteriormente pelo servidor, e uma resposta <acronym>AMF</acronym> será retornada.
|
|
|
+ </para>
|
|
|
+ </example>
|
|
|
+
|
|
|
+ <note>
|
|
|
+ <title>
|
|
|
+ Todas as Funções e Métodos atribuídos precisam de Blocos de Documentação (Docblocks)
|
|
|
+ </title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Como todos os componentes de servidor no Zend Framework, você precisa documentar seus
|
|
|
+ métodos de classe usando docblocks <acronym>PHP</acronym>. Você precisa fornecer,
|
|
|
+ no mínimo, anotações para cada argumento obrigatório assim como o valor de retorno.
|
|
|
+ Exemplo:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// Função para atribuição:
|
|
|
+
|
|
|
+/**
|
|
|
+ * @param string $name
|
|
|
+ * @param string $greeting
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+function helloWorld($name, $greeting = 'Hello')
|
|
|
+{
|
|
|
+ return $greeting . ', ' . $name;
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// Classe atribuída
|
|
|
+
|
|
|
+class World
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * @param string $name
|
|
|
+ * @param string $greeting
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public function hello($name, $greeting = 'Hello')
|
|
|
+ {
|
|
|
+ return $greeting . ', ' . $name;
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Outras anotações podem ser usadas, mas serão ignoradas.
|
|
|
+ </para>
|
|
|
+ </note>
|
|
|
+
|
|
|
+ <sect2 id="zend.amf.server.flex">
|
|
|
+ <title>Conectando ao servidor com Flex</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Conectar a seu <classname>Zend_Amf_Server</classname> a partir de seu projeto Flex é
|
|
|
+ bem simples; você simplesmente precisa apontar sua <acronym>URI</acronym> de ponto de
|
|
|
+ extremidade para seu script <classname>Zend_Amf_Server</classname>
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Digamos, por exemplo, que você tenha criado seu servidor e o colocado no arquivo
|
|
|
+ <filename>server.php</filename> na raiz de sua aplicação, e seu <acronym>URI</acronym> é
|
|
|
+ <filename>http://example.com/server.php</filename>. Neste caso, você modificaria seu
|
|
|
+ arquivo <filename>services-config.xml</filename> para definir o atributo uri de ponto de
|
|
|
+ extremidade, channel, para este valor.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Se você nunca criou um arquivo <filename>services-config.xml</filename> você poderá
|
|
|
+ fazê-lo abrindo seu projeto na janela 'Navigator' do Flex Builder. Clique com o botão
|
|
|
+ direito sobre nome do projeto e selecione 'properties'. Na janela de diálogo de
|
|
|
+ propriedades do projeto vá para o menu 'Flex Build Path', aba 'Library path' e tenha
|
|
|
+ certeza de que o arquivo '<filename>rpc.swc</filename>' foi adicionado ao caminhos de
|
|
|
+ seus projetos e pressione 'Ok' para fechar a janela.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ You will also need to tell the compiler to use the
|
|
|
+ <filename>service-config.xml</filename> to find the RemoteObject endpoint. To do this
|
|
|
+ open your project properties panel again by right clicking on the project folder from
|
|
|
+ your Navigator and selecting properties. From the properties popup select 'Flex
|
|
|
+ Compiler' and add the string: <command>-services "services-config.xml"</command>. Press
|
|
|
+ Apply then OK to return to update the option. What you have just done is told the Flex
|
|
|
+ compiler to look to the <filename>services-config.xml</filename> file for runtime
|
|
|
+ variables that will be used by the RemotingObject class.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ We now need to tell Flex which services configuration file to use for connecting to
|
|
|
+ our remote methods. For this reason create a new
|
|
|
+ '<filename>services-config.xml</filename>' file into your Flex project src folder. To
|
|
|
+ do this right click on the project folder and select 'new' 'File' which will popup a
|
|
|
+ new window. Select the project folder and then name the file
|
|
|
+ '<filename>services-config.xml</filename>' and press finish.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Flex has created the new <filename>services-config.xml</filename> and has it open. Use
|
|
|
+ the following example text for your <filename>services-config.xml</filename> file. Make
|
|
|
+ sure that you update your endpoint to match that of your testing server. Make sure you
|
|
|
+ save the file.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="xml"><![CDATA[
|
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
|
+<services-config>
|
|
|
+ <services>
|
|
|
+ <service id="zend-service"
|
|
|
+ class="flex.messaging.services.RemotingService"
|
|
|
+ messageTypes="flex.messaging.messages.RemotingMessage">
|
|
|
+ <destination id="zend">
|
|
|
+ <channels>
|
|
|
+ <channel ref="zend-endpoint"/>
|
|
|
+ </channels>
|
|
|
+ <properties>
|
|
|
+ <source>*</source>
|
|
|
+ </properties>
|
|
|
+ </destination>
|
|
|
+ </service>
|
|
|
+ </services>
|
|
|
+ <channels>
|
|
|
+ <channel-definition id="zend-endpoint"
|
|
|
+ class="mx.messaging.channels.AMFChannel">
|
|
|
+ <endpoint uri="http://example.com/server.php"
|
|
|
+ class="flex.messaging.endpoints.AMFEndpoint"/>
|
|
|
+ </channel-definition>
|
|
|
+ </channels>
|
|
|
+</services-config>
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ There are two key points in the example. First, but last in the
|
|
|
+ listing, we create an <acronym>AMF</acronym> channel, and specify the endpoint as the
|
|
|
+ <acronym>URL</acronym> to our <classname>Zend_Amf_Server</classname>:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="xml"><![CDATA[
|
|
|
+<channel-definition id="zend-endpoint"
|
|
|
+ <endpoint uri="http://example.com/server.php"
|
|
|
+ class="flex.messaging.endpoints.AMFEndpoint"/>
|
|
|
+</channel-definition>
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Notice that we've given this channel an identifier, "zend-endpoint".
|
|
|
+ The example create a service destination that refers to this channel,
|
|
|
+ assigning it an ID as well -- in this case "zend".
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Within our Flex <acronym>MXML</acronym> files, we need to bind a RemoteObject to the
|
|
|
+ service. In <acronym>MXML</acronym>, this might be done as follows:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="xml"><![CDATA[
|
|
|
+<mx:RemoteObject id="myservice"
|
|
|
+ fault="faultHandler(event)"
|
|
|
+ showBusyCursor="true"
|
|
|
+ destination="zend">
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Here, we've defined a new remote object identified by "myservice"
|
|
|
+ bound to the service destination "zend" we defined in the
|
|
|
+ <filename>services-config.xml</filename> file. We then call methods on it
|
|
|
+ in our ActionScript by simply calling "myservice.<method>".
|
|
|
+ As an example:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="ActionScript"><![CDATA[
|
|
|
+myservice.hello("Wade");
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ When namespacing, you would use
|
|
|
+ "myservice.<namespace>.<method>":
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="ActionScript"><![CDATA[
|
|
|
+myservice.world.hello("Wade");
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ For more information on Flex RemoteObject invocation, <ulink
|
|
|
+ url="http://livedocs.adobe.com/flex/3/html/help.html?content=data_access_4.html">
|
|
|
+ visit the Adobe Flex 3 Help site</ulink>.
|
|
|
+ </para>
|
|
|
+ </sect2>
|
|
|
+
|
|
|
+ <sect2 id="zend.amf.server.errors">
|
|
|
+ <title>Error Handling</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ By default, all exceptions thrown in your attached classes or
|
|
|
+ functions will be caught and returned as <acronym>AMF</acronym> ErrorMessages. However,
|
|
|
+ the content of these ErrorMessage objects will vary based on whether
|
|
|
+ or not the server is in "production" mode (the default state).
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ When in production mode, only the exception code will be returned.
|
|
|
+ If you disable production mode -- something that should be done for
|
|
|
+ testing only -- most exception details will be returned: the
|
|
|
+ exception message, line, and backtrace will all be attached.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ To disable production mode, do the following:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$server->setProduction(false);
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ To re-enable it, pass a <constant>TRUE</constant> boolean value instead:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$server->setProduction(true);
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <note>
|
|
|
+ <title>Disable production mode sparingly!</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ We recommend disabling production mode only when in development.
|
|
|
+ Exception messages and backtraces can contain sensitive system
|
|
|
+ information that you may not wish for outside parties to access.
|
|
|
+ Even though <acronym>AMF</acronym> is a binary format, the specification is now
|
|
|
+ open, meaning anybody can potentially deserialize the payload.
|
|
|
+ </para>
|
|
|
+ </note>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ One area to be especially careful with is <acronym>PHP</acronym> errors themselves.
|
|
|
+ When the <property>display_errors</property> <acronym>INI</acronym> directive is
|
|
|
+ enabled, any <acronym>PHP</acronym> errors for the current error reporting level are
|
|
|
+ rendered directly in the output -- potentially disrupting the <acronym>AMF</acronym>
|
|
|
+ response payload. We suggest turning off the <property>display_errors</property>
|
|
|
+ directive in production to prevent such problems
|
|
|
+ </para>
|
|
|
+ </sect2>
|
|
|
+
|
|
|
+ <sect2 id="zend.amf.server.response">
|
|
|
+ <title>AMF Responses</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Occasionally you may desire to manipulate the response object
|
|
|
+ slightly, typically to return extra message headers. The
|
|
|
+ <methodname>handle()</methodname> method of the server returns the response
|
|
|
+ object, allowing you to do so.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <example id="zend.amf.server.response.messageHeaderExample">
|
|
|
+ <title>Adding Message Headers to the AMF Response</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ In this example, we add a 'foo' MessageHeader with the value
|
|
|
+ 'bar' to the response prior to returning it.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$response = $server->handle();
|
|
|
+$response->addAmfHeader(new Zend_Amf_Value_MessageHeader('foo', true, 'bar'))
|
|
|
+echo $response;
|
|
|
+]]></programlisting>
|
|
|
+ </example>
|
|
|
+ </sect2>
|
|
|
+
|
|
|
+ <sect2 id="zend.amf.server.typedobjects">
|
|
|
+ <title>Typed Objects</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Similar to <acronym>SOAP</acronym>, <acronym>AMF</acronym> allows passing objects
|
|
|
+ between the client and server. This allows a great amount of flexibility and
|
|
|
+ coherence between the two environments.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <classname>Zend_Amf</classname> provides three methods for mapping
|
|
|
+ ActionScript and <acronym>PHP</acronym> objects.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <itemizedlist>
|
|
|
+ <listitem>
|
|
|
+ <para>
|
|
|
+ First, you may create explicit bindings at the server level,
|
|
|
+ using the <methodname>setClassMap()</methodname> method. The first
|
|
|
+ argument is the ActionScript class name, the second the <acronym>PHP</acronym>
|
|
|
+ class name it maps to:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// Map the ActionScript class 'ContactVO' to the PHP class 'Contact':
|
|
|
+$server->setClassMap('ContactVO', 'Contact');
|
|
|
+]]></programlisting>
|
|
|
+ </listitem>
|
|
|
+
|
|
|
+ <listitem>
|
|
|
+ <para>
|
|
|
+ Second, you can set the public property <varname>$_explicitType</varname>
|
|
|
+ in your <acronym>PHP</acronym> class, with the
|
|
|
+ value representing the ActionScript class to map to:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+class Contact
|
|
|
+{
|
|
|
+ public $_explicitType = 'ContactVO';
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+ </listitem>
|
|
|
+
|
|
|
+ <listitem>
|
|
|
+ <para>
|
|
|
+ Third, in a similar vein, you may define the public method
|
|
|
+ <methodname>getASClassName()</methodname> in your <acronym>PHP</acronym> class;
|
|
|
+ this method should return the appropriate ActionScript class:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+class Contact
|
|
|
+{
|
|
|
+ public function getASClassName()
|
|
|
+ {
|
|
|
+ return 'ContactVO';
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+ </listitem>
|
|
|
+ </itemizedlist>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Although we have created the ContactVO on the server we now need to make its
|
|
|
+ corresponding class in <acronym>AS3</acronym> for the server object to be mapped to.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Right click on the src folder of the Flex project and select New -> ActionScript
|
|
|
+ File. Name the file ContactVO and press finish to see the new file. Copy the
|
|
|
+ following code into the file to finish creating the class.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+package
|
|
|
+{
|
|
|
+ [Bindable]
|
|
|
+ [RemoteClass(alias="ContactVO")]
|
|
|
+ public class ContactVO
|
|
|
+ {
|
|
|
+ public var id:int;
|
|
|
+ public var firstname:String;
|
|
|
+ public var lastname:String;
|
|
|
+ public var email:String;
|
|
|
+ public var mobile:String;
|
|
|
+ public function ProductVO():void {
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The class is syntactically equivalent to the <acronym>PHP</acronym> of the same name.
|
|
|
+ The variable names are exactly the same and need to be in the same case
|
|
|
+ to work properly. There are two unique <acronym>AS3</acronym> meta tags in this class.
|
|
|
+ The first is bindable which makes fire a change event when it is updated.
|
|
|
+ The second tag is the RemoteClass tag which defines that this class can
|
|
|
+ have a remote object mapped with the alias name in this case
|
|
|
+ <emphasis>ContactVO</emphasis>. It is mandatory that this tag the value that was set
|
|
|
+ is the <acronym>PHP</acronym> class are strictly equivalent.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+[Bindable]
|
|
|
+private var myContact:ContactVO;
|
|
|
+
|
|
|
+private function getContactHandler(event:ResultEvent):void {
|
|
|
+ myContact = ContactVO(event.result);
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The following result event from the service call is cast instantly onto the Flex
|
|
|
+ ContactVO. Anything that is bound to myContact will be updated with the returned
|
|
|
+ ContactVO data.
|
|
|
+ </para>
|
|
|
+ </sect2>
|
|
|
+
|
|
|
+ <sect2 id="zend.amf.server.resources">
|
|
|
+ <title>Resources</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <classname>Zend_Amf</classname> provides tools for mapping resource types
|
|
|
+ returned by service classes into data consumable by ActionScript.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ In order to handle specific resource type, the user needs to create a plugin class named
|
|
|
+ after the resource name, with words capitalized and spaces removed (so, resource
|
|
|
+ type "mysql result" becomes MysqlResult), with some prefix, e.g.
|
|
|
+ <classname>My_MysqlResult</classname>. This class should implement one method,
|
|
|
+ <methodname>parse()</methodname>, receiving one argument - the resource - and returning
|
|
|
+ the value that should be sent to ActionScript. The class should be located in the file
|
|
|
+ named after the last component of the name, e.g. <filename>MysqlResult.php</filename>.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The directory containing the resource handling plugins should be registered with
|
|
|
+ <classname>Zend_Amf</classname> type loader:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+Zend_Amf_Parse_TypeLoader::addResourceDirectory(
|
|
|
+ "My",
|
|
|
+ "application/library/resources/My"
|
|
|
+);
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ For detailed discussion of loading plugins, please see
|
|
|
+ the <link linkend="zend.loader.pluginloader">plugin loader</link> section.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Default directory for <classname>Zend_Amf</classname> resources is registered
|
|
|
+ automatically and currently contains handlers for "mysql result" and "stream"
|
|
|
+ resources.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+// Example class implementing handling resources of type mysql result
|
|
|
+class Zend_Amf_Parse_Resource_MysqlResult
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * Parse resource into array
|
|
|
+ *
|
|
|
+ * @param resource $resource
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function parse($resource) {
|
|
|
+ $result = array();
|
|
|
+ while($row = mysql_fetch_assoc($resource)) {
|
|
|
+ $result[] = $row;
|
|
|
+ }
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Trying to return unknown resource type (i.e., one for which no handler plugin exists)
|
|
|
+ will result in an exception.
|
|
|
+ </para>
|
|
|
+ </sect2>
|
|
|
+
|
|
|
+ <sect2 id="zend.amf.server.flash">
|
|
|
+ <title>Connecting to the Server from Flash</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Connecting to your <classname>Zend_Amf_Server</classname> from your Flash project is
|
|
|
+ slightly different than from Flex. However once the connection Flash functions with
|
|
|
+ <classname>Zend_Amf_Server</classname> the same way is flex. The following example can
|
|
|
+ also be used from a Flex <acronym>AS3</acronym> file. We will reuse the same
|
|
|
+ <classname>Zend_Amf_Server</classname> configuration along with the World class for our
|
|
|
+ connection.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Open Flash CS and create and new Flash File (ActionScript 3). Name the document
|
|
|
+ <filename>ZendExample.fla</filename> and save the document into a folder that you will
|
|
|
+ use for this example. Create a new <acronym>AS3</acronym> file in the same directory
|
|
|
+ and call the file <filename>Main.as</filename>. Have both files open in your editor. We
|
|
|
+ are now going to connect the two files via the document class. Select ZendExample and
|
|
|
+ click on the stage. From the stage properties panel change the Document class to Main.
|
|
|
+ This links the <filename>Main.as</filename> ActionScript file with the user interface
|
|
|
+ in <filename>ZendExample.fla</filename>. When you run the Flash file ZendExample the
|
|
|
+ <filename>Main.as</filename> class will now be run. Next we will add ActionScript to
|
|
|
+ make the <acronym>AMF</acronym> call.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ We now are going to make a Main class so that we can send the data to the server and
|
|
|
+ display the result. Copy the following code into your <filename>Main.as</filename> file
|
|
|
+ and then we will walk through the code to describe what each element's role is.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+package {
|
|
|
+ import flash.display.MovieClip;
|
|
|
+ import flash.events.*;
|
|
|
+ import flash.net.NetConnection;
|
|
|
+ import flash.net.Responder;
|
|
|
+
|
|
|
+ public class Main extends MovieClip {
|
|
|
+ private var gateway:String = "http://example.com/server.php";
|
|
|
+ private var connection:NetConnection;
|
|
|
+ private var responder:Responder;
|
|
|
+
|
|
|
+ public function Main() {
|
|
|
+ responder = new Responder(onResult, onFault);
|
|
|
+ connection = new NetConnection;
|
|
|
+ connection.connect(gateway);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function onComplete( e:Event ):void{
|
|
|
+ var params = "Sent to Server";
|
|
|
+ connection.call("World.hello", responder, params);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function onResult(result:Object):void {
|
|
|
+ // Display the returned data
|
|
|
+ trace(String(result));
|
|
|
+ }
|
|
|
+ private function onFault(fault:Object):void {
|
|
|
+ trace(String(fault.description));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ We first need to import two ActionScript libraries that perform the bulk of the work.
|
|
|
+ The first is NetConnection which acts like a by directional pipe between the client and
|
|
|
+ the server. The second is a Responder object which handles the return values from the
|
|
|
+ server related to the success or failure of the call.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+import flash.net.NetConnection;
|
|
|
+import flash.net.Responder;
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ In the class we need three variables to represent the NetConnection, Responder, and
|
|
|
+ the gateway <acronym>URL</acronym> to our <classname>Zend_Amf_Server</classname>
|
|
|
+ installation.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+private var gateway:String = "http://example.com/server.php";
|
|
|
+private var connection:NetConnection;
|
|
|
+private var responder:Responder;
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ In the Main constructor we create a responder and a new connection to the
|
|
|
+ <classname>Zend_Amf_Server</classname> endpoint. The responder defines two different
|
|
|
+ methods for handling the response from the server. For simplicity I have called these
|
|
|
+ onResult and onFault.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+responder = new Responder(onResult, onFault);
|
|
|
+connection = new NetConnection;
|
|
|
+connection.connect(gateway);
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ In the onComplete function which is run as soon as the construct has completed we send
|
|
|
+ the data to the server. We need to add one more line that makes a call to the
|
|
|
+ <classname>Zend_Amf_Server</classname> World->hello function.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+connection.call("World.hello", responder, params);
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ When we created the responder variable we defined an onResult and onFault function to
|
|
|
+ handle the response from the server. We added this function for the successful result
|
|
|
+ from the server. A successful event handler is run every time the connection is handled
|
|
|
+ properly to the server.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+private function onResult(result:Object):void {
|
|
|
+ // Display the returned data
|
|
|
+ trace(String(result));
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The onFault function, is called if there was an invalid response from the server. This
|
|
|
+ happens when there is an error on the server, the <acronym>URL</acronym> to the server
|
|
|
+ is invalid, the remote service or method does not exist, and any other connection
|
|
|
+ related issues.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="as"><![CDATA[
|
|
|
+private function onFault(fault:Object):void {
|
|
|
+ trace(String(fault.description));
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Adding in the ActionScript to make the remoting connection is now complete. Running the
|
|
|
+ ZendExample file now makes a connection to <classname>Zend_Amf</classname>. In review
|
|
|
+ you have added the required variables to open a connection to the remote server, defined
|
|
|
+ what methods should be used when your application receives a response from the server,
|
|
|
+ and finally displayed the returned data to output via <methodname>trace()</methodname>.
|
|
|
+ </para>
|
|
|
+ </sect2>
|
|
|
+
|
|
|
+ <sect2 id="zend.amf.server.auth">
|
|
|
+ <title>Authentication</title>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ <classname>Zend_Amf_Server</classname> allows you to specify authentication and
|
|
|
+ authorization hooks to control access to the services. It is using the infrastructure
|
|
|
+ provided by <link linkend="zend.auth"><classname>Zend_Auth</classname></link> and
|
|
|
+ <link linkend="zend.acl"><classname>Zend_Acl</classname></link> components.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ In order to define authentication, the user provides authentication adapter extening
|
|
|
+ <classname>Zend_Amf_Auth_Abstract</classname> abstract class. The adapter should
|
|
|
+ implement the <methodname>authenticate()</methodname> method just like regular
|
|
|
+ <link linkend="zend.auth.introduction.adapters">authentication adapter</link>.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The adapter should use properties <emphasis>_username</emphasis> and
|
|
|
+ <emphasis>_password</emphasis> from the parent
|
|
|
+ <classname>Zend_Amf_Auth_Abstract</classname> class in order to authenticate. These
|
|
|
+ values are set by the server using <methodname>setCredentials()</methodname> method
|
|
|
+ before call to <methodname>authenticate()</methodname> if the credentials are received
|
|
|
+ in the <acronym>AMF</acronym> request headers.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The identity returned by the adapter should be an object containing property
|
|
|
+ <property>role</property> for the <acronym>ACL</acronym> access control to work.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ If the authentication result is not successful, the request is not proceseed further
|
|
|
+ and failure message is returned with the reasons for failure taken from the result.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ The adapter is connected to the server using <methodname>setAuth()</methodname> method:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$server->setAuth(new My_Amf_Auth());
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ Access control is performed by using <classname>Zend_Acl</classname> object set by
|
|
|
+ <methodname>setAcl()</methodname> method:
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+$acl = new Zend_Acl();
|
|
|
+createPermissions($acl); // create permission structure
|
|
|
+$server->setAcl($acl);
|
|
|
+]]></programlisting>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ If the <acronym>ACL</acronym> object is set, and the class being called defines
|
|
|
+ <methodname>initAcl()</methodname> method, this method will be called with the
|
|
|
+ <acronym>ACL</acronym> object as an argument. The class then can create additional
|
|
|
+ <acronym>ACL</acronym> rules and return <constant>TRUE</constant>, or return
|
|
|
+ <constant>FALSE</constant> if no access control is required for this class.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <para>
|
|
|
+ After <acronym>ACL</acronym> have been set up, the server will check if access is
|
|
|
+ allowed with role set by the authentication, resource being the class name (or
|
|
|
+ <constant>NULL</constant> for
|
|
|
+ function calls) and privilege being the function name. If no authentication was
|
|
|
+ provided, then if the <emphasis>anonymous</emphasis> role was defined, it will be used,
|
|
|
+ otherwise the access will be denied.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <programlisting language="php"><![CDATA[
|
|
|
+if($this->_acl->isAllowed($role, $class, $function)) {
|
|
|
+ return true;
|
|
|
+} else {
|
|
|
+ require_once 'Zend/Amf/Server/Exception.php';
|
|
|
+ throw new Zend_Amf_Server_Exception("Access not allowed");
|
|
|
+}
|
|
|
+]]></programlisting>
|
|
|
+ </sect2>
|
|
|
+</sect1>
|
|
|
+<!--
|
|
|
+vim:se ts=4 sw=4 et:
|
|
|
+-->
|