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