2
0

Server.php 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Amf
  17. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /** @see Zend_Server_Interface */
  22. require_once 'Zend/Server/Interface.php';
  23. /** @see Zend_Server_Reflection */
  24. require_once 'Zend/Server/Reflection.php';
  25. /** @see Zend_Amf_Constants */
  26. require_once 'Zend/Amf/Constants.php';
  27. /** @see Zend_Amf_Value_MessageBody */
  28. require_once 'Zend/Amf/Value/MessageBody.php';
  29. /** @see Zend_Amf_Value_MessageHeader */
  30. require_once 'Zend/Amf/Value/MessageHeader.php';
  31. /** @see Zend_Amf_Value_Messaging_CommandMessage */
  32. require_once 'Zend/Amf/Value/Messaging/CommandMessage.php';
  33. /** @see Zend_Loader_PluginLoader */
  34. require_once 'Zend/Loader/PluginLoader.php';
  35. /** @see Zend_Amf_Parse_TypeLoader */
  36. require_once 'Zend/Amf/Parse/TypeLoader.php';
  37. /** @see Zend_Auth */
  38. require_once 'Zend/Auth.php';
  39. /**
  40. * An AMF gateway server implementation to allow the connection of the Adobe Flash Player to
  41. * Zend Framework
  42. *
  43. * @todo Make the reflection methods cache and autoload.
  44. * @package Zend_Amf
  45. * @subpackage Server
  46. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  47. * @license http://framework.zend.com/license/new-bsd New BSD License
  48. */
  49. class Zend_Amf_Server implements Zend_Server_Interface
  50. {
  51. /**
  52. * Array of dispatchables
  53. * @var array
  54. */
  55. protected $_methods = array();
  56. /**
  57. * Array of classes that can be called without being explicitly loaded
  58. *
  59. * Keys are class names.
  60. *
  61. * @var array
  62. */
  63. protected $_classAllowed = array();
  64. /**
  65. * Loader for classes in added directories
  66. * @var Zend_Loader_PluginLoader
  67. */
  68. protected $_loader;
  69. /**
  70. * @var bool Production flag; whether or not to return exception messages
  71. */
  72. protected $_production = true;
  73. /**
  74. * Request processed
  75. * @var null|Zend_Amf_Request
  76. */
  77. protected $_request = null;
  78. /**
  79. * Class to use for responses
  80. * @var null|Zend_Amf_Response
  81. */
  82. protected $_response;
  83. /**
  84. * Dispatch table of name => method pairs
  85. * @var array
  86. */
  87. protected $_table = array();
  88. /**
  89. *
  90. * @var bool session flag; whether or not to add a session to each response.
  91. */
  92. protected $_session = false;
  93. /**
  94. * Namespace allows all AMF calls to not clobber other PHP session variables
  95. * @var Zend_Session_NameSpace default session namespace zend_amf
  96. */
  97. protected $_sesionNamespace = 'zend_amf';
  98. /**
  99. * Set the default session.name if php_
  100. * @var string
  101. */
  102. protected $_sessionName = 'PHPSESSID';
  103. /**
  104. * Authentication handler object
  105. *
  106. * @var Zend_Amf_Auth_Abstract
  107. */
  108. protected $_auth;
  109. /**
  110. * ACL handler object
  111. *
  112. * @var Zend_Acl
  113. */
  114. protected $_acl;
  115. /**
  116. * The server constructor
  117. */
  118. public function __construct()
  119. {
  120. Zend_Amf_Parse_TypeLoader::setResourceLoader(new Zend_Loader_PluginLoader(array("Zend_Amf_Parse_Resource" => "Zend/Amf/Parse/Resource")));
  121. }
  122. /**
  123. * Set authentication adapter
  124. *
  125. * If the authentication adapter implements a "getAcl()" method, populate
  126. * the ACL of this instance with it (if none exists already).
  127. *
  128. * @param Zend_Amf_Auth_Abstract $auth
  129. * @return Zend_Amf_Server
  130. */
  131. public function setAuth(Zend_Amf_Auth_Abstract $auth)
  132. {
  133. $this->_auth = $auth;
  134. if ((null === $this->getAcl()) && method_exists($auth, 'getAcl')) {
  135. $this->setAcl($auth->getAcl());
  136. }
  137. return $this;
  138. }
  139. /**
  140. * Get authentication adapter
  141. *
  142. * @return Zend_Amf_Auth_Abstract
  143. */
  144. public function getAuth()
  145. {
  146. return $this->_auth;
  147. }
  148. /**
  149. * Set ACL adapter
  150. *
  151. * @param Zend_Acl $acl
  152. * @return Zend_Amf_Server
  153. */
  154. public function setAcl(Zend_Acl $acl)
  155. {
  156. $this->_acl = $acl;
  157. return $this;
  158. }
  159. /**
  160. * Get ACL adapter
  161. *
  162. * @return Zend_Acl
  163. */
  164. public function getAcl()
  165. {
  166. return $this->_acl;
  167. }
  168. /**
  169. * Set production flag
  170. *
  171. * @param bool $flag
  172. * @return Zend_Amf_Server
  173. */
  174. public function setProduction($flag)
  175. {
  176. $this->_production = (bool) $flag;
  177. return $this;
  178. }
  179. /**
  180. * Whether or not the server is in production
  181. *
  182. * @return bool
  183. */
  184. public function isProduction()
  185. {
  186. return $this->_production;
  187. }
  188. /**
  189. * @param namespace of all incoming sessions defaults to Zend_Amf
  190. * @return Zend_Amf_Server
  191. */
  192. public function setSession($namespace = 'Zend_Amf')
  193. {
  194. require_once 'Zend/Session.php';
  195. $this->_session = true;
  196. $this->_sesionNamespace = new Zend_Session_Namespace($namespace);
  197. return $this;
  198. }
  199. /**
  200. * Whether of not the server is using sessions
  201. * @return bool
  202. */
  203. public function isSession()
  204. {
  205. return $this->_session;
  206. }
  207. /**
  208. * Check if the ACL allows accessing the function or method
  209. *
  210. * @param string|object $object Object or class being accessed
  211. * @param string $function Function or method being accessed
  212. * @return unknown_type
  213. */
  214. protected function _checkAcl($object, $function)
  215. {
  216. if(!$this->_acl) {
  217. return true;
  218. }
  219. if($object) {
  220. $class = is_object($object)?get_class($object):$object;
  221. if(!$this->_acl->has($class)) {
  222. require_once 'Zend/Acl/Resource.php';
  223. $this->_acl->add(new Zend_Acl_Resource($class));
  224. }
  225. $call = array($object, "initAcl");
  226. if(is_callable($call) && !call_user_func($call, $this->_acl)) {
  227. // if initAcl returns false, no ACL check
  228. return true;
  229. }
  230. } else {
  231. $class = null;
  232. }
  233. $auth = Zend_Auth::getInstance();
  234. if($auth->hasIdentity()) {
  235. $role = $auth->getIdentity()->role;
  236. } else {
  237. if($this->_acl->hasRole(Zend_Amf_Constants::GUEST_ROLE)) {
  238. $role = Zend_Amf_Constants::GUEST_ROLE;
  239. } else {
  240. require_once 'Zend/Amf/Server/Exception.php';
  241. throw new Zend_Amf_Server_Exception("Unauthenticated access not allowed");
  242. }
  243. }
  244. if($this->_acl->isAllowed($role, $class, $function)) {
  245. return true;
  246. } else {
  247. require_once 'Zend/Amf/Server/Exception.php';
  248. throw new Zend_Amf_Server_Exception("Access not allowed");
  249. }
  250. }
  251. /**
  252. * Get PluginLoader for the Server
  253. *
  254. * @return Zend_Loader_PluginLoader
  255. */
  256. protected function getLoader()
  257. {
  258. if(empty($this->_loader)) {
  259. require_once 'Zend/Loader/PluginLoader.php';
  260. $this->_loader = new Zend_Loader_PluginLoader();
  261. }
  262. return $this->_loader;
  263. }
  264. /**
  265. * Loads a remote class or method and executes the function and returns
  266. * the result
  267. *
  268. * @param string $method Is the method to execute
  269. * @param mixed $param values for the method
  270. * @return mixed $response the result of executing the method
  271. * @throws Zend_Amf_Server_Exception
  272. */
  273. protected function _dispatch($method, $params = null, $source = null)
  274. {
  275. if($source) {
  276. if(($mapped = Zend_Amf_Parse_TypeLoader::getMappedClassName($source)) !== false) {
  277. $source = $mapped;
  278. }
  279. }
  280. $qualifiedName = empty($source) ? $method : $source . '.' . $method;
  281. if (!isset($this->_table[$qualifiedName])) {
  282. // if source is null a method that was not defined was called.
  283. if ($source) {
  284. $className = str_replace('.', '_', $source);
  285. if(class_exists($className, false) && !isset($this->_classAllowed[$className])) {
  286. require_once 'Zend/Amf/Server/Exception.php';
  287. throw new Zend_Amf_Server_Exception('Can not call "' . $className . '" - use setClass()');
  288. }
  289. try {
  290. $this->getLoader()->load($className);
  291. } catch (Exception $e) {
  292. require_once 'Zend/Amf/Server/Exception.php';
  293. throw new Zend_Amf_Server_Exception('Class "' . $className . '" does not exist: '.$e->getMessage(), 0, $e);
  294. }
  295. // Add the new loaded class to the server.
  296. require_once 'Zend/Amf/Server/Exception.php';
  297. $this->setClass($className, $source);
  298. }
  299. if (!isset($this->_table[$qualifiedName])) {
  300. // Source is null or doesn't contain specified method
  301. require_once 'Zend/Amf/Server/Exception.php';
  302. throw new Zend_Amf_Server_Exception('Method "' . $method . '" does not exist');
  303. }
  304. }
  305. $info = $this->_table[$qualifiedName];
  306. $argv = $info->getInvokeArguments();
  307. if (0 < count($argv)) {
  308. $params = array_merge($params, $argv);
  309. }
  310. $params = $this->_castParameters($info, $params);
  311. if ($info instanceof Zend_Server_Reflection_Function) {
  312. $func = $info->getName();
  313. $this->_checkAcl(null, $func);
  314. $return = call_user_func_array($func, $params);
  315. } elseif ($info instanceof Zend_Server_Reflection_Method) {
  316. // Get class
  317. $class = $info->getDeclaringClass()->getName();
  318. if ('static' == $info->isStatic()) {
  319. // for some reason, invokeArgs() does not work the same as
  320. // invoke(), and expects the first argument to be an object.
  321. // So, using a callback if the method is static.
  322. $this->_checkAcl($class, $info->getName());
  323. $return = call_user_func_array(array($class, $info->getName()), $params);
  324. } else {
  325. // Object methods
  326. try {
  327. $object = $info->getDeclaringClass()->newInstance();
  328. } catch (Exception $e) {
  329. require_once 'Zend/Amf/Server/Exception.php';
  330. throw new Zend_Amf_Server_Exception('Error instantiating class ' . $class . ' to invoke method ' . $info->getName() . ': '.$e->getMessage(), 621, $e);
  331. }
  332. $this->_checkAcl($object, $info->getName());
  333. $return = $info->invokeArgs($object, $params);
  334. }
  335. } else {
  336. require_once 'Zend/Amf/Server/Exception.php';
  337. throw new Zend_Amf_Server_Exception('Method missing implementation ' . get_class($info));
  338. }
  339. return $return;
  340. }
  341. /**
  342. * Handles each of the 11 different command message types.
  343. *
  344. * A command message is a flex.messaging.messages.CommandMessage
  345. *
  346. * @see Zend_Amf_Value_Messaging_CommandMessage
  347. * @param Zend_Amf_Value_Messaging_CommandMessage $message
  348. * @return Zend_Amf_Value_Messaging_AcknowledgeMessage
  349. */
  350. protected function _loadCommandMessage(Zend_Amf_Value_Messaging_CommandMessage $message)
  351. {
  352. require_once 'Zend/Amf/Value/Messaging/AcknowledgeMessage.php';
  353. switch($message->operation) {
  354. case Zend_Amf_Value_Messaging_CommandMessage::DISCONNECT_OPERATION :
  355. case Zend_Amf_Value_Messaging_CommandMessage::CLIENT_PING_OPERATION :
  356. $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
  357. break;
  358. case Zend_Amf_Value_Messaging_CommandMessage::LOGIN_OPERATION :
  359. $data = explode(':', base64_decode($message->body));
  360. $userid = $data[0];
  361. $password = isset($data[1])?$data[1]:"";
  362. if(empty($userid)) {
  363. require_once 'Zend/Amf/Server/Exception.php';
  364. throw new Zend_Amf_Server_Exception('Login failed: username not supplied');
  365. }
  366. if(!$this->_handleAuth($userid, $password)) {
  367. require_once 'Zend/Amf/Server/Exception.php';
  368. throw new Zend_Amf_Server_Exception('Authentication failed');
  369. }
  370. $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
  371. break;
  372. case Zend_Amf_Value_Messaging_CommandMessage::LOGOUT_OPERATION :
  373. if($this->_auth) {
  374. Zend_Auth::getInstance()->clearIdentity();
  375. }
  376. $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
  377. break;
  378. default :
  379. require_once 'Zend/Amf/Server/Exception.php';
  380. throw new Zend_Amf_Server_Exception('CommandMessage::' . $message->operation . ' not implemented');
  381. break;
  382. }
  383. return $return;
  384. }
  385. /**
  386. * Create appropriate error message
  387. *
  388. * @param int $objectEncoding Current AMF encoding
  389. * @param string $message Message that was being processed when error happened
  390. * @param string $description Error description
  391. * @param mixed $detail Detailed data about the error
  392. * @param int $code Error code
  393. * @param int $line Error line
  394. * @return Zend_Amf_Value_Messaging_ErrorMessage|array
  395. */
  396. protected function _errorMessage($objectEncoding, $message, $description, $detail, $code, $line)
  397. {
  398. $return = null;
  399. switch ($objectEncoding) {
  400. case Zend_Amf_Constants::AMF0_OBJECT_ENCODING :
  401. return array (
  402. 'description' => ($this->isProduction ()) ? '' : $description,
  403. 'detail' => ($this->isProduction ()) ? '' : $detail,
  404. 'line' => ($this->isProduction ()) ? 0 : $line,
  405. 'code' => $code
  406. );
  407. case Zend_Amf_Constants::AMF3_OBJECT_ENCODING :
  408. require_once 'Zend/Amf/Value/Messaging/ErrorMessage.php';
  409. $return = new Zend_Amf_Value_Messaging_ErrorMessage ( $message );
  410. $return->faultString = $this->isProduction () ? '' : $description;
  411. $return->faultCode = $code;
  412. $return->faultDetail = $this->isProduction () ? '' : $detail;
  413. break;
  414. }
  415. return $return;
  416. }
  417. /**
  418. * Handle AMF authentication
  419. *
  420. * @param string $userid
  421. * @param string $password
  422. * @return boolean
  423. */
  424. protected function _handleAuth( $userid, $password)
  425. {
  426. if (!$this->_auth) {
  427. return true;
  428. }
  429. $this->_auth->setCredentials($userid, $password);
  430. $auth = Zend_Auth::getInstance();
  431. $result = $auth->authenticate($this->_auth);
  432. if ($result->isValid()) {
  433. if (!$this->isSession()) {
  434. $this->setSession();
  435. }
  436. return true;
  437. } else {
  438. // authentication failed, good bye
  439. require_once 'Zend/Amf/Server/Exception.php';
  440. throw new Zend_Amf_Server_Exception(
  441. "Authentication failed: " . join("\n",
  442. $result->getMessages()), $result->getCode());
  443. }
  444. }
  445. /**
  446. * Takes the deserialized AMF request and performs any operations.
  447. *
  448. * @todo should implement and SPL observer pattern for custom AMF headers
  449. * @todo DescribeService support
  450. * @param Zend_Amf_Request $request
  451. * @return Zend_Amf_Response
  452. * @throws Zend_Amf_server_Exception|Exception
  453. */
  454. protected function _handle(Zend_Amf_Request $request)
  455. {
  456. // Get the object encoding of the request.
  457. $objectEncoding = $request->getObjectEncoding();
  458. // create a response object to place the output from the services.
  459. $response = $this->getResponse();
  460. // set response encoding
  461. $response->setObjectEncoding($objectEncoding);
  462. // Authenticate, if we have credential headers
  463. $error = false;
  464. $headers = $request->getAmfHeaders();
  465. if (isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER])
  466. && isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->userid)
  467. && isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->password)
  468. ) {
  469. try {
  470. if ($this->_handleAuth(
  471. $headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->userid,
  472. $headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->password
  473. )) {
  474. // use RequestPersistentHeader to clear credentials
  475. $response->addAmfHeader(
  476. new Zend_Amf_Value_MessageHeader(
  477. Zend_Amf_Constants::PERSISTENT_HEADER,
  478. false,
  479. new Zend_Amf_Value_MessageHeader(
  480. Zend_Amf_Constants::CREDENTIALS_HEADER,
  481. false, null
  482. )
  483. )
  484. );
  485. }
  486. } catch (Exception $e) {
  487. // Error during authentication; report it
  488. $error = $this->_errorMessage(
  489. $objectEncoding,
  490. '',
  491. $e->getMessage(),
  492. $e->getTraceAsString(),
  493. $e->getCode(),
  494. $e->getLine()
  495. );
  496. $responseType = Zend_AMF_Constants::STATUS_METHOD;
  497. }
  498. }
  499. // Iterate through each of the service calls in the AMF request
  500. foreach($request->getAmfBodies() as $body)
  501. {
  502. if ($error) {
  503. // Error during authentication; just report it and be done
  504. $responseURI = $body->getResponseURI() . $responseType;
  505. $newBody = new Zend_Amf_Value_MessageBody($responseURI, null, $error);
  506. $response->addAmfBody($newBody);
  507. continue;
  508. }
  509. try {
  510. switch ($objectEncoding) {
  511. case Zend_Amf_Constants::AMF0_OBJECT_ENCODING:
  512. // AMF0 Object Encoding
  513. $targetURI = $body->getTargetURI();
  514. $message = '';
  515. // Split the target string into its values.
  516. $source = substr($targetURI, 0, strrpos($targetURI, '.'));
  517. if ($source) {
  518. // Break off method name from namespace into source
  519. $method = substr(strrchr($targetURI, '.'), 1);
  520. $return = $this->_dispatch($method, $body->getData(), $source);
  521. } else {
  522. // Just have a method name.
  523. $return = $this->_dispatch($targetURI, $body->getData());
  524. }
  525. break;
  526. case Zend_Amf_Constants::AMF3_OBJECT_ENCODING:
  527. default:
  528. // AMF3 read message type
  529. $message = $body->getData();
  530. if ($message instanceof Zend_Amf_Value_Messaging_CommandMessage) {
  531. // async call with command message
  532. $return = $this->_loadCommandMessage($message);
  533. } elseif ($message instanceof Zend_Amf_Value_Messaging_RemotingMessage) {
  534. require_once 'Zend/Amf/Value/Messaging/AcknowledgeMessage.php';
  535. $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
  536. $return->body = $this->_dispatch($message->operation, $message->body, $message->source);
  537. } else {
  538. // Amf3 message sent with netConnection
  539. $targetURI = $body->getTargetURI();
  540. // Split the target string into its values.
  541. $source = substr($targetURI, 0, strrpos($targetURI, '.'));
  542. if ($source) {
  543. // Break off method name from namespace into source
  544. $method = substr(strrchr($targetURI, '.'), 1);
  545. $return = $this->_dispatch($method, $body->getData(), $source);
  546. } else {
  547. // Just have a method name.
  548. $return = $this->_dispatch($targetURI, $body->getData());
  549. }
  550. }
  551. break;
  552. }
  553. $responseType = Zend_AMF_Constants::RESULT_METHOD;
  554. } catch (Exception $e) {
  555. $return = $this->_errorMessage($objectEncoding, $message,
  556. $e->getMessage(), $e->getTraceAsString(),$e->getCode(), $e->getLine());
  557. $responseType = Zend_AMF_Constants::STATUS_METHOD;
  558. }
  559. $responseURI = $body->getResponseURI() . $responseType;
  560. $newBody = new Zend_Amf_Value_MessageBody($responseURI, null, $return);
  561. $response->addAmfBody($newBody);
  562. }
  563. // Add a session header to the body if session is requested.
  564. if($this->isSession()) {
  565. $currentID = session_id();
  566. $joint = "?";
  567. if(isset($_SERVER['QUERY_STRING'])) {
  568. if(!strpos($_SERVER['QUERY_STRING'], $currentID) !== FALSE) {
  569. if(strrpos($_SERVER['QUERY_STRING'], "?") !== FALSE) {
  570. $joint = "&";
  571. }
  572. }
  573. }
  574. // create a new AMF message header with the session id as a variable.
  575. $sessionValue = $joint . $this->_sessionName . "=" . $currentID;
  576. $sessionHeader = new Zend_Amf_Value_MessageHeader(Zend_Amf_Constants::URL_APPEND_HEADER, false, $sessionValue);
  577. $response->addAmfHeader($sessionHeader);
  578. }
  579. // serialize the response and return serialized body.
  580. $response->finalize();
  581. }
  582. /**
  583. * Handle an AMF call from the gateway.
  584. *
  585. * @param null|Zend_Amf_Request $request Optional
  586. * @return Zend_Amf_Response
  587. */
  588. public function handle($request = null)
  589. {
  590. // Check if request was passed otherwise get it from the server
  591. if ($request === null || !$request instanceof Zend_Amf_Request) {
  592. $request = $this->getRequest();
  593. } else {
  594. $this->setRequest($request);
  595. }
  596. if ($this->isSession()) {
  597. // Check if a session is being sent from the amf call
  598. if (isset($_COOKIE[$this->_sessionName])) {
  599. session_id($_COOKIE[$this->_sessionName]);
  600. }
  601. }
  602. // Check for errors that may have happend in deserialization of Request.
  603. try {
  604. // Take converted PHP objects and handle service call.
  605. // Serialize to Zend_Amf_response for output stream
  606. $this->_handle($request);
  607. $response = $this->getResponse();
  608. } catch (Exception $e) {
  609. // Handle any errors in the serialization and service calls.
  610. require_once 'Zend/Amf/Server/Exception.php';
  611. throw new Zend_Amf_Server_Exception('Handle error: ' . $e->getMessage() . ' ' . $e->getLine(), 0, $e);
  612. }
  613. // Return the Amf serialized output string
  614. return $response;
  615. }
  616. /**
  617. * Set request object
  618. *
  619. * @param string|Zend_Amf_Request $request
  620. * @return Zend_Amf_Server
  621. */
  622. public function setRequest($request)
  623. {
  624. if (is_string($request) && class_exists($request)) {
  625. $request = new $request();
  626. if (!$request instanceof Zend_Amf_Request) {
  627. require_once 'Zend/Amf/Server/Exception.php';
  628. throw new Zend_Amf_Server_Exception('Invalid request class');
  629. }
  630. } elseif (!$request instanceof Zend_Amf_Request) {
  631. require_once 'Zend/Amf/Server/Exception.php';
  632. throw new Zend_Amf_Server_Exception('Invalid request object');
  633. }
  634. $this->_request = $request;
  635. return $this;
  636. }
  637. /**
  638. * Return currently registered request object
  639. *
  640. * @return null|Zend_Amf_Request
  641. */
  642. public function getRequest()
  643. {
  644. if (null === $this->_request) {
  645. require_once 'Zend/Amf/Request/Http.php';
  646. $this->setRequest(new Zend_Amf_Request_Http());
  647. }
  648. return $this->_request;
  649. }
  650. /**
  651. * Public access method to private Zend_Amf_Server_Response reference
  652. *
  653. * @param string|Zend_Amf_Server_Response $response
  654. * @return Zend_Amf_Server
  655. */
  656. public function setResponse($response)
  657. {
  658. if (is_string($response) && class_exists($response)) {
  659. $response = new $response();
  660. if (!$response instanceof Zend_Amf_Response) {
  661. require_once 'Zend/Amf/Server/Exception.php';
  662. throw new Zend_Amf_Server_Exception('Invalid response class');
  663. }
  664. } elseif (!$response instanceof Zend_Amf_Response) {
  665. require_once 'Zend/Amf/Server/Exception.php';
  666. throw new Zend_Amf_Server_Exception('Invalid response object');
  667. }
  668. $this->_response = $response;
  669. return $this;
  670. }
  671. /**
  672. * get a reference to the Zend_Amf_response instance
  673. *
  674. * @return Zend_Amf_Server_Response
  675. */
  676. public function getResponse()
  677. {
  678. if (null === ($response = $this->_response)) {
  679. require_once 'Zend/Amf/Response/Http.php';
  680. $this->setResponse(new Zend_Amf_Response_Http());
  681. }
  682. return $this->_response;
  683. }
  684. /**
  685. * Attach a class or object to the server
  686. *
  687. * Class may be either a class name or an instantiated object. Reflection
  688. * is done on the class or object to determine the available public
  689. * methods, and each is attached to the server as and available method. If
  690. * a $namespace has been provided, that namespace is used to prefix
  691. * AMF service call.
  692. *
  693. * @param string|object $class
  694. * @param string $namespace Optional
  695. * @param mixed $arg Optional arguments to pass to a method
  696. * @return Zend_Amf_Server
  697. * @throws Zend_Amf_Server_Exception on invalid input
  698. */
  699. public function setClass($class, $namespace = '', $argv = null)
  700. {
  701. if (is_string($class) && !class_exists($class)){
  702. require_once 'Zend/Amf/Server/Exception.php';
  703. throw new Zend_Amf_Server_Exception('Invalid method or class');
  704. } elseif (!is_string($class) && !is_object($class)) {
  705. require_once 'Zend/Amf/Server/Exception.php';
  706. throw new Zend_Amf_Server_Exception('Invalid method or class; must be a classname or object');
  707. }
  708. $args = null;
  709. if (2 < func_num_args()) {
  710. $args = array_slice(func_get_args(), 2);
  711. }
  712. // Use the class name as the name space by default.
  713. if ($namespace == '') {
  714. $namespace = is_object($class) ? get_class($class) : $class;
  715. }
  716. $this->_classAllowed[is_object($class) ? get_class($class) : $class] = true;
  717. $this->_methods[] = Zend_Server_Reflection::reflectClass($class, $args, $namespace);
  718. $this->_buildDispatchTable();
  719. return $this;
  720. }
  721. /**
  722. * Attach a function to the server
  723. *
  724. * Additional arguments to pass to the function at dispatch may be passed;
  725. * any arguments following the namespace will be aggregated and passed at
  726. * dispatch time.
  727. *
  728. * @param string|array $function Valid callback
  729. * @param string $namespace Optional namespace prefix
  730. * @return Zend_Amf_Server
  731. * @throws Zend_Amf_Server_Exception
  732. */
  733. public function addFunction($function, $namespace = '')
  734. {
  735. if (!is_string($function) && !is_array($function)) {
  736. require_once 'Zend/Amf/Server/Exception.php';
  737. throw new Zend_Amf_Server_Exception('Unable to attach function');
  738. }
  739. $argv = null;
  740. if (2 < func_num_args()) {
  741. $argv = array_slice(func_get_args(), 2);
  742. }
  743. $function = (array) $function;
  744. foreach ($function as $func) {
  745. if (!is_string($func) || !function_exists($func)) {
  746. require_once 'Zend/Amf/Server/Exception.php';
  747. throw new Zend_Amf_Server_Exception('Unable to attach function');
  748. }
  749. $this->_methods[] = Zend_Server_Reflection::reflectFunction($func, $argv, $namespace);
  750. }
  751. $this->_buildDispatchTable();
  752. return $this;
  753. }
  754. /**
  755. * Creates an array of directories in which services can reside.
  756. * TODO: add support for prefixes?
  757. *
  758. * @param string $dir
  759. */
  760. public function addDirectory($dir)
  761. {
  762. $this->getLoader()->addPrefixPath("", $dir);
  763. }
  764. /**
  765. * Returns an array of directories that can hold services.
  766. *
  767. * @return array
  768. */
  769. public function getDirectory()
  770. {
  771. return $this->getLoader()->getPaths("");
  772. }
  773. /**
  774. * (Re)Build the dispatch table
  775. *
  776. * The dispatch table consists of a an array of method name =>
  777. * Zend_Server_Reflection_Function_Abstract pairs
  778. *
  779. * @return void
  780. */
  781. protected function _buildDispatchTable()
  782. {
  783. $table = array();
  784. foreach ($this->_methods as $key => $dispatchable) {
  785. if ($dispatchable instanceof Zend_Server_Reflection_Function_Abstract) {
  786. $ns = $dispatchable->getNamespace();
  787. $name = $dispatchable->getName();
  788. $name = empty($ns) ? $name : $ns . '.' . $name;
  789. if (isset($table[$name])) {
  790. require_once 'Zend/Amf/Server/Exception.php';
  791. throw new Zend_Amf_Server_Exception('Duplicate method registered: ' . $name);
  792. }
  793. $table[$name] = $dispatchable;
  794. continue;
  795. }
  796. if ($dispatchable instanceof Zend_Server_Reflection_Class) {
  797. foreach ($dispatchable->getMethods() as $method) {
  798. $ns = $method->getNamespace();
  799. $name = $method->getName();
  800. $name = empty($ns) ? $name : $ns . '.' . $name;
  801. if (isset($table[$name])) {
  802. require_once 'Zend/Amf/Server/Exception.php';
  803. throw new Zend_Amf_Server_Exception('Duplicate method registered: ' . $name);
  804. }
  805. $table[$name] = $method;
  806. continue;
  807. }
  808. }
  809. }
  810. $this->_table = $table;
  811. }
  812. /**
  813. * Raise a server fault
  814. *
  815. * Unimplemented
  816. *
  817. * @param string|Exception $fault
  818. * @return void
  819. */
  820. public function fault($fault = null, $code = 404)
  821. {
  822. }
  823. /**
  824. * Returns a list of registered methods
  825. *
  826. * Returns an array of dispatchables (Zend_Server_Reflection_Function,
  827. * _Method, and _Class items).
  828. *
  829. * @return array
  830. */
  831. public function getFunctions()
  832. {
  833. return $this->_table;
  834. }
  835. /**
  836. * Set server persistence
  837. *
  838. * Unimplemented
  839. *
  840. * @param mixed $mode
  841. * @return void
  842. */
  843. public function setPersistence($mode)
  844. {
  845. }
  846. /**
  847. * Load server definition
  848. *
  849. * Unimplemented
  850. *
  851. * @param array $definition
  852. * @return void
  853. */
  854. public function loadFunctions($definition)
  855. {
  856. }
  857. /**
  858. * Map ActionScript classes to PHP classes
  859. *
  860. * @param string $asClass
  861. * @param string $phpClass
  862. * @return Zend_Amf_Server
  863. */
  864. public function setClassMap($asClass, $phpClass)
  865. {
  866. require_once 'Zend/Amf/Parse/TypeLoader.php';
  867. Zend_Amf_Parse_TypeLoader::setMapping($asClass, $phpClass);
  868. return $this;
  869. }
  870. /**
  871. * List all available methods
  872. *
  873. * Returns an array of method names.
  874. *
  875. * @return array
  876. */
  877. public function listMethods()
  878. {
  879. return array_keys($this->_table);
  880. }
  881. /**
  882. * Cast parameters
  883. *
  884. * Takes the provided parameters from the request, and attempts to cast them
  885. * to objects, if the prototype defines any as explicit object types
  886. *
  887. * @param Reflection $reflectionMethod
  888. * @param array $params
  889. * @return array
  890. */
  891. protected function _castParameters($reflectionMethod, array $params)
  892. {
  893. $prototypes = $reflectionMethod->getPrototypes();
  894. $nonObjectTypes = array(
  895. 'null',
  896. 'mixed',
  897. 'void',
  898. 'unknown',
  899. 'bool',
  900. 'boolean',
  901. 'number',
  902. 'int',
  903. 'integer',
  904. 'double',
  905. 'float',
  906. 'string',
  907. 'array',
  908. 'object',
  909. 'stdclass',
  910. );
  911. $types = array();
  912. foreach ($prototypes as $prototype) {
  913. foreach ($prototype->getParameters() as $parameter) {
  914. $type = $parameter->getType();
  915. if (in_array(strtolower($type), $nonObjectTypes)) {
  916. continue;
  917. }
  918. $position = $parameter->getPosition();
  919. $types[$position] = $type;
  920. }
  921. }
  922. if (empty($types)) {
  923. return $params;
  924. }
  925. foreach ($params as $position => $value) {
  926. if (!isset($types[$position])) {
  927. // No specific type to cast to? done
  928. continue;
  929. }
  930. $type = $types[$position];
  931. if (!class_exists($type)) {
  932. // Not a class, apparently. done
  933. continue;
  934. }
  935. if ($value instanceof $type) {
  936. // Already of the right type? done
  937. continue;
  938. }
  939. if (!is_array($value) && !is_object($value)) {
  940. // Can't cast scalars to objects easily; done
  941. continue;
  942. }
  943. // Create instance, and loop through value to set
  944. $object = new $type;
  945. foreach ($value as $property => $defined) {
  946. $object->{$property} = $defined;
  947. }
  948. $params[$position] = $object;
  949. }
  950. return $params;
  951. }
  952. }