Zend_Amf-Server.xml 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <sect1 id="zend.amf.server">
  4. <title>Zend_Amf_Server</title>
  5. <para>
  6. <classname>Zend_Amf_Server</classname> provides an RPC-style server for handling
  7. requests made from the Adobe Flash Player using the AMF protocol. Like
  8. all Zend Framework server classes, it follows the SoapServer API,
  9. providing an easy to remember interface for creating servers.
  10. </para>
  11. <example id="zend.amf.server.basic">
  12. <title>Basic AMF Server</title>
  13. <para>
  14. Let's assume that you have created a class <emphasis>Foo</emphasis> with a
  15. variety of public methods. You may create an AMF server using the
  16. following code:
  17. </para>
  18. <programlisting language="php"><![CDATA[
  19. $server = new Zend_Amf_Server();
  20. $server->setClass('Foo');
  21. $response = $server->handle();
  22. echo $response;
  23. ]]></programlisting>
  24. <para>
  25. Alternately, you may choose to attach a simple function as a
  26. callback instead:
  27. </para>
  28. <programlisting language="php"><![CDATA[
  29. $server = new Zend_Amf_Server();
  30. $server->addFunction('myUberCoolFunction');
  31. $response = $server->handle();
  32. echo $response;
  33. ]]></programlisting>
  34. <para>
  35. You could also mix and match multiple classes and functions. When
  36. doing so, we suggest namespacing each to ensure that no method name
  37. collisions occur; this can be done by simply passing a second string
  38. argument to either <methodname>addFunction()</methodname> or
  39. <methodname>setClass()</methodname>:
  40. </para>
  41. <programlisting language="php"><![CDATA[
  42. $server = new Zend_Amf_Server();
  43. $server->addFunction('myUberCoolFunction', 'my')
  44. ->setClass('Foo', 'foo')
  45. ->setClass('Bar', 'bar');
  46. $response = $server->handle();
  47. echo $response;
  48. ]]></programlisting>
  49. <para>
  50. The <classname>Zend_Amf_Server</classname> also allows services to be dynamically
  51. loaded based on a supplied directory path. You may add as many directories as you wish
  52. to the server. The order that you add the directories to the server will be the
  53. order that the <emphasis>LIFO</emphasis> search will be performed on the directories to
  54. match the class. Adding directories is completed with the
  55. <methodname>addDirectory()</methodname> method.
  56. </para>
  57. <programlisting language="php"><![CDATA[
  58. $server->addDirectory(dirname(__FILE__) .'/../services/');
  59. $server->addDirectory(dirname(__FILE__) .'/../package/');
  60. ]]></programlisting>
  61. <para>
  62. When calling remote services your source name can have underscore ("_") and dot (".")
  63. directory delimiters. When an underscore is used <emphasis>PEAR</emphasis> and Zend
  64. Framework class naming conventions will be respected. This means that if you call the
  65. service <classname>com_Foo_Bar</classname> the server will look for the file
  66. <filename>Bar.php</filename> in the each of the included paths at
  67. <filename>com/Foo/Bar.php</filename>. If the dot notation is used for your remote
  68. service such as <filename>com.Foo.Bar</filename> each included path will have
  69. <filename>com/Foo/Bar.php</filename> append to the end to autoload
  70. <filename>Bar.php</filename>
  71. </para>
  72. <para>
  73. All AMF requests sent to the script will then be handled by the
  74. server, and an AMF response will be returned.
  75. </para>
  76. </example>
  77. <note>
  78. <title>All Attached Methods and Functions Need Docblocks</title>
  79. <para>
  80. Like all other server components in Zend Framework, you must
  81. document your class methods using PHP docblocks. At the minimum, you
  82. need to provide annotations for each required argument as well as
  83. the return value. As examples:
  84. </para>
  85. <programlisting language="php"><![CDATA[
  86. // Function to attach:
  87. /**
  88. * @param string $name
  89. * @param string $greeting
  90. * @return string
  91. */
  92. function helloWorld($name, $greeting = 'Hello')
  93. {
  94. return $greeting . ', ' . $name;
  95. }
  96. ]]></programlisting>
  97. <programlisting language="php"><![CDATA[
  98. // Attached class
  99. class World
  100. {
  101. /**
  102. * @param string $name
  103. * @param string $greeting
  104. * @return string
  105. */
  106. public function hello($name, $greeting = 'Hello')
  107. {
  108. return $greeting . ', ' . $name;
  109. }
  110. }
  111. ]]></programlisting>
  112. <para>
  113. Other annotations may be used, but will be ignored.
  114. </para>
  115. </note>
  116. <sect2 id="zend.amf.server.flex">
  117. <title>Connecting to the Server from Flex</title>
  118. <para>
  119. Connecting to your <classname>Zend_Amf_Server</classname> from your Flex
  120. project is quite simple; you simply need to point your endpoint URI
  121. to your <classname>Zend_Amf_Server</classname> script.
  122. </para>
  123. <para>
  124. Say, for instance, you have created your server and placed it in the
  125. <filename>server.php</filename> file in your application root, and thus the
  126. URI is <filename>http://example.com/server.php</filename>. In this case, you
  127. would modify your <filename>services-config.xml</filename> file to set the channel
  128. endpoint uri attribute to this value.
  129. </para>
  130. <para>
  131. If you have never created a <filename>service-config.xml</filename> file you can do so
  132. by opening your project in your Navigator window. Right click on the project name and
  133. select 'properties'. In the Project properties dialog go into 'Flex Build Path' menu,
  134. 'Library path' tab and be sure the '<filename>rpc.swc</filename>' file is added to your
  135. projects path and Press Ok to close the window.
  136. </para>
  137. <para>
  138. You will also need to tell the compiler to use the
  139. <filename>service-config.xml</filename> to find the RemoteObject endpoint. To do this
  140. open your project properties panel again by right clicking on the project folder from
  141. your Navigator and selecting properties. From the properties popup select 'Flex
  142. Compiler' and add the string: -services "services-config.xml". Press Apply then OK to
  143. return to update the option. What you have just done is told the Flex compiler to look
  144. to the <filename>services-config.xml</filename> file for runtime variables that will be
  145. used by the RemotingObject class.
  146. </para>
  147. <para>
  148. We now need to tell Flex which services configuration file to use for connecting to
  149. our remote methods. For this reason create a new 'services-config.xml' file into your
  150. Flex project src folder. To do this right click on the project folder and select
  151. 'new' 'File' which will popup a new window. Select the project folder and then name
  152. the file 'services-config.xml' and press finish.
  153. </para>
  154. <para>
  155. Flex has created the new <filename>services-config.xml</filename> and has it open. Use
  156. the following example text for your <filename>services-config.xml</filename> file. Make
  157. sure that you update your endpoint to match that of your testing server. Make sure you
  158. save the file.
  159. </para>
  160. <programlisting language="xml"><![CDATA[
  161. <?xml version="1.0" encoding="UTF-8"?>
  162. <services-config>
  163. <services>
  164. <service id="zend-service"
  165. class="flex.messaging.services.RemotingService"
  166. messageTypes="flex.messaging.messages.RemotingMessage">
  167. <destination id="zend">
  168. <channels>
  169. <channel ref="zend-endpoint"/>
  170. </channels>
  171. <properties>
  172. <source>*</source>
  173. </properties>
  174. </destination>
  175. </service>
  176. </services>
  177. <channels>
  178. <channel-definition id="zend-endpoint"
  179. class="mx.messaging.channels.AMFChannel">
  180. <endpoint uri="http://example.com/server.php"
  181. class="flex.messaging.endpoints.AMFEndpoint"/>
  182. </channel-definition>
  183. </channels>
  184. </services-config>
  185. ]]></programlisting>
  186. <para>
  187. There are two key points in the example. First, but last in the
  188. listing, we create an AMF channel, and specify the endpoint as the
  189. URL to our <classname>Zend_Amf_Server</classname>:
  190. </para>
  191. <programlisting language="xml"><![CDATA[
  192. <channel-definition id="zend-endpoint"
  193. <endpoint uri="http://example.com/server.php"
  194. class="flex.messaging.endpoints.AMFEndpoint"/>
  195. </channel-definition>
  196. ]]></programlisting>
  197. <para>
  198. Notice that we've given this channel an identifier, "zend-endpoint".
  199. The example create a service destination that refers to this channel,
  200. assigning it an ID as well -- in this case "zend".
  201. </para>
  202. <para>
  203. Within our Flex <emphasis>MXML</emphasis> files, we need to bind a RemoteObject to the
  204. service. In <emphasis>MXML</emphasis>, this might be done as follows:
  205. </para>
  206. <programlisting language="xml"><![CDATA[
  207. <mx:RemoteObject id="myservice"
  208. fault="faultHandler(event)"
  209. showBusyCursor="true"
  210. destination="zend">
  211. ]]></programlisting>
  212. <para>
  213. Here, we've defined a new remote object identified by "myservice"
  214. bound to the service destination "zend" we defined in the
  215. <filename>services-config.xml</filename> file. We then call methods on it
  216. in our ActionScript by simply calling "myservice.&lt;method&gt;".
  217. As an example:
  218. </para>
  219. <programlisting language="ActionScript"><![CDATA[
  220. myservice.hello("Wade");
  221. ]]></programlisting>
  222. <para>
  223. When namespacing, you would use
  224. "myservice.&lt;namespace&gt;.&lt;method&gt;":
  225. </para>
  226. <programlisting language="ActionScript"><![CDATA[
  227. myservice.world.hello("Wade");
  228. ]]></programlisting>
  229. <para>
  230. For more information on Flex RemoteObject invocation, <ulink
  231. url="http://livedocs.adobe.com/flex/3/html/help.html?content=data_access_4.html">
  232. visit the Adobe Flex 3 Help site</ulink>.
  233. </para>
  234. </sect2>
  235. <sect2 id="zend.amf.server.errors">
  236. <title>Error Handling</title>
  237. <para>
  238. By default, all exceptions thrown in your attached classes or
  239. functions will be caught and returned as AMF ErrorMessages. However,
  240. the content of these ErrorMessage objects will vary based on whether
  241. or not the server is in "production" mode (the default state).
  242. </para>
  243. <para>
  244. When in production mode, only the exception code will be returned.
  245. If you disable production mode -- something that should be done for
  246. testing only -- most exception details will be returned: the
  247. exception message, line, and backtrace will all be attached.
  248. </para>
  249. <para>
  250. To disable production mode, do the following:
  251. </para>
  252. <programlisting language="php"><![CDATA[
  253. $server->setProduction(false);
  254. ]]></programlisting>
  255. <para>
  256. To re-enable it, pass a true boolean value instead:
  257. </para>
  258. <programlisting language="php"><![CDATA[
  259. $server->setProduction(true);
  260. ]]></programlisting>
  261. <note>
  262. <title>Disable production mode sparingly!</title>
  263. <para>
  264. We recommend disabling production mode only when in development.
  265. Exception messages and backtraces can contain sensitive system
  266. information that you may not wish for outside parties to access.
  267. Even though AMF is a binary format, the specification is now
  268. open, meaning anybody can potentially deserialize the payload.
  269. </para>
  270. </note>
  271. <para>
  272. One area to be especially careful with is PHP errors themselves.
  273. When the <emphasis>display_errors</emphasis> INI directive is enabled, any
  274. PHP errors for the current error reporting level are rendered
  275. directly in the output -- potentially disrupting the AMF response
  276. payload. We suggest turning off the <emphasis>display_errors</emphasis>
  277. directive in production to prevent such problems
  278. </para>
  279. </sect2>
  280. <sect2 id="zend.amf.server.response">
  281. <title>AMF Responses</title>
  282. <para>
  283. Occasionally you may desire to manipulate the response object
  284. slightly, typically to return extra message headers. The
  285. <methodname>handle()</methodname> method of the server returns the response
  286. object, allowing you to do so.
  287. </para>
  288. <example id="zend.amf.server.response.messageHeaderExample">
  289. <title>Adding Message Headers to the AMF Response</title>
  290. <para>
  291. In this example, we add a 'foo' MessageHeader with the value
  292. 'bar' to the response prior to returning it.
  293. </para>
  294. <programlisting language="php"><![CDATA[
  295. $response = $server->handle();
  296. $response->addAmfHeader(new Zend_Amf_Value_MessageHeader('foo', true, 'bar'))
  297. echo $response;
  298. ]]></programlisting>
  299. </example>
  300. </sect2>
  301. <sect2 id="zend.amf.server.typedobjects">
  302. <title>Typed Objects</title>
  303. <para>
  304. Similar to <emphasis>SOAP</emphasis>, AMF allows passing objects between the client and
  305. server. This allows a great amount of flexibility and coherence
  306. between the two environments.
  307. </para>
  308. <para>
  309. <classname>Zend_Amf</classname> provides three methods for mapping
  310. ActionScript and PHP objects.
  311. </para>
  312. <itemizedlist>
  313. <listitem>
  314. <para>
  315. First, you may create explicit bindings at the server level,
  316. using the <methodname>setClassMap()</methodname> method. The first
  317. argument is the ActionScript class name, the second the PHP
  318. class name it maps to:
  319. </para>
  320. <programlisting language="php"><![CDATA[
  321. // Map the ActionScript class 'ContactVO' to the PHP class 'Contact':
  322. $server->setClassMap('ContactVO', 'Contact');
  323. ]]></programlisting>
  324. </listitem>
  325. <listitem>
  326. <para>
  327. Second, you can set the public property
  328. <varname>$_explicitType</varname> in your PHP class, with the
  329. value representing the ActionScript class to map to:
  330. </para>
  331. <programlisting language="php"><![CDATA[
  332. class Contact
  333. {
  334. public $_explicitType = 'ContactVO';
  335. }
  336. ]]></programlisting>
  337. </listitem>
  338. <listitem>
  339. <para>
  340. Third, in a similar vein, you may define the public method
  341. <methodname>getASClassName()</methodname> in your PHP class; this method
  342. should return the appropriate ActionScript class:
  343. </para>
  344. <programlisting language="php"><![CDATA[
  345. class Contact
  346. {
  347. public function getASClassName()
  348. {
  349. return 'ContactVO';
  350. }
  351. }
  352. ]]></programlisting>
  353. </listitem>
  354. </itemizedlist>
  355. <para>
  356. Although we have created the ContactVO on the server we now need to make
  357. its corresponding class in AS3 for the server object to be mapped to.
  358. </para>
  359. <para>
  360. Right click on the src folder of the Flex project and select New -> ActionScript
  361. File. Name the file ContactVO and press finish to see the new file. Copy the
  362. following code into the file to finish creating the class.
  363. </para>
  364. <programlisting language="as"><![CDATA[
  365. package
  366. {
  367. [Bindable]
  368. [RemoteClass(alias="ContactVO")]
  369. public class ContactVO
  370. {
  371. public var id:int;
  372. public var firstname:String;
  373. public var lastname:String;
  374. public var email:String;
  375. public var mobile:String;
  376. public function ProductVO():void {
  377. }
  378. }
  379. }
  380. ]]></programlisting>
  381. <para>
  382. The class is syntactically equivalent to the PHP of the same name.
  383. The variable names are exactly the same and need to be in the same case
  384. to work properly. There are two unique AS3 meta tags in this class.
  385. The first is bindable which makes fire a change event when it is updated.
  386. The second tag is the RemoteClass tag which defines that this class can
  387. have a remote object mapped with the alias name in this case
  388. <emphasis>ContactVO</emphasis>. It is mandatory that this tag the value that was set
  389. is the PHP class are strictly equivalent.
  390. </para>
  391. <programlisting language="as"><![CDATA[
  392. [Bindable]
  393. private var myContact:ContactVO;
  394. private function getContactHandler(event:ResultEvent):void {
  395. myContact = ContactVO(event.result);
  396. }
  397. ]]></programlisting>
  398. <para>
  399. The following result event from the service call is cast instantly onto the Flex
  400. ContactVO. Anything that is bound to myContact will be updated with the returned
  401. ContactVO data.
  402. </para>
  403. </sect2>
  404. <sect2 id="zend.amf.server.flash">
  405. <title>Connecting to the Server from Flash</title>
  406. <para>
  407. Connecting to your <classname>Zend_Amf_Server</classname> from your Flash project is
  408. slightly different than from Flex. However once the connection Flash functions with
  409. <classname>Zend_Amf_Server</classname> the same way is flex. The following example can
  410. also be used from a Flex AS3 file. We will reuse the same
  411. <classname>Zend_Amf_Server</classname> configuration along with the World class for our
  412. connection.
  413. </para>
  414. <para>
  415. Open Flash CS and create and new Flash File (ActionScript 3). Name the document
  416. <filename>ZendExample.fla</filename> and save the document into a folder that you will
  417. use for this example. Create a new AS3 file in the same directory and call the file
  418. <filename>Main.as</filename>. Have both files open in your editor. We are now going to
  419. connect the two files via the document class. Select ZendExample and click on the
  420. stage. From the stage properties panel change the Document class to Main. This links
  421. the <filename>Main.as</filename> ActionScript file with the user interface in
  422. <filename>ZendExample.fla</filename>. When you run the Flash file ZendExample the
  423. <filename>Main.as</filename> class will now be run. Next we will add ActionScript to
  424. make the AMF call.
  425. </para>
  426. <para>
  427. We now are going to make a Main class so that we can send the data to the server and
  428. display the result. Copy the following code into your <filename>Main.as</filename> file
  429. and then we will walk through the code to describe what each element's role is.
  430. </para>
  431. <programlisting language="as"><![CDATA[
  432. package {
  433. import flash.display.MovieClip;
  434. import flash.events.*;
  435. import flash.net.NetConnection;
  436. import flash.net.Responder;
  437. public class Main extends MovieClip {
  438. private var gateway:String = "http://example.com/server.php";
  439. private var connection:NetConnection;
  440. private var responder:Responder;
  441. public function Main() {
  442. responder = new Responder(onResult, onFault);
  443. connection = new NetConnection;
  444. connection.connect(gateway);
  445. }
  446. public function onComplete( e:Event ):void{
  447. var params = "Sent to Server";
  448. connection.call("World.hello", responder, params);
  449. }
  450. private function onResult(result:Object):void {
  451. // Display the returned data
  452. trace(String(result));
  453. }
  454. private function onFault(fault:Object):void {
  455. trace(String(fault.description));
  456. }
  457. }
  458. }
  459. ]]></programlisting>
  460. <para>
  461. We first need to import two ActionScript libraries that perform the bulk of the work.
  462. The first is NetConnection which acts like a by directional pipe between the client and
  463. the server. The second is a Responder object which handles the return values from the
  464. server related to the success or failure of the call.
  465. </para>
  466. <programlisting language="as"><![CDATA[
  467. import flash.net.NetConnection;
  468. import flash.net.Responder;
  469. ]]></programlisting>
  470. <para>
  471. In the class we need three variables to represent the NetConnection, Responder, and
  472. the gateway URL to our <classname>Zend_Amf_Server</classname> installation.
  473. </para>
  474. <programlisting language="as"><![CDATA[
  475. private var gateway:String = "http://example.com/server.php";
  476. private var connection:NetConnection;
  477. private var responder:Responder;
  478. ]]></programlisting>
  479. <para>
  480. In the Main constructor we create a responder and a new connection to the
  481. <classname>Zend_Amf_Server</classname> endpoint. The responder defines two different
  482. methods for handling the response from the server. For simplicity I have called these
  483. onResult and onFault.
  484. </para>
  485. <programlisting language="as"><![CDATA[
  486. responder = new Responder(onResult, onFault);
  487. connection = new NetConnection;
  488. connection.connect(gateway);
  489. ]]></programlisting>
  490. <para>
  491. In the onComplete function which is run as soon as the construct has completed we send
  492. the data to the server. We need to add one more line that makes a call to the
  493. <classname>Zend_Amf_Server</classname> World->hello function.
  494. </para>
  495. <programlisting language="as"><![CDATA[
  496. connection.call("World.hello", responder, params);
  497. ]]></programlisting>
  498. <para>
  499. When we created the responder variable we defined an onResult and onFault function to
  500. handle the response from the server. We added this function for the successful result
  501. from the server. A successful event handler is run every time the connection is handled
  502. properly to the server.
  503. </para>
  504. <programlisting language="as"><![CDATA[
  505. private function onResult(result:Object):void {
  506. // Display the returned data
  507. trace(String(result));
  508. }
  509. ]]></programlisting>
  510. <para>
  511. The onFault function, is called if there was an invalid response from the server. This
  512. happens when there is an error on the server, the URL to the server is invalid, the
  513. remote service or method does not exist, and any other connection related issues.
  514. </para>
  515. <programlisting language="as"><![CDATA[
  516. private function onFault(fault:Object):void {
  517. trace(String(fault.description));
  518. }
  519. ]]></programlisting>
  520. <para>
  521. Adding in the ActionScript to make the remoting connection is now complete. Running the
  522. ZendExample file now makes a connection to Zend Amf. In review you have added the
  523. required variables to open a connection to the remote server, defined what methods
  524. should be used when your application receives a response from the server, and finally
  525. displayed the returned data to output via <methodname>trace()</methodname>.
  526. </para>
  527. </sect2>
  528. </sect1>
  529. <!--
  530. vim:se ts=4 sw=4 et:
  531. -->