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