File indexing completed on 2024-12-22 05:36:28

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Amf
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /** @see Zend_Server_Interface */
0023 // require_once 'Zend/Server/Interface.php';
0024 
0025 /** @see Zend_Server_Reflection */
0026 // require_once 'Zend/Server/Reflection.php';
0027 
0028 /** @see Zend_Amf_Constants */
0029 // require_once 'Zend/Amf/Constants.php';
0030 
0031 /** @see Zend_Amf_Value_MessageBody */
0032 // require_once 'Zend/Amf/Value/MessageBody.php';
0033 
0034 /** @see Zend_Amf_Value_MessageHeader */
0035 // require_once 'Zend/Amf/Value/MessageHeader.php';
0036 
0037 /** @see Zend_Amf_Value_Messaging_CommandMessage */
0038 // require_once 'Zend/Amf/Value/Messaging/CommandMessage.php';
0039 
0040 /** @see Zend_Loader_PluginLoader */
0041 // require_once 'Zend/Loader/PluginLoader.php';
0042 
0043 /** @see Zend_Amf_Parse_TypeLoader */
0044 // require_once 'Zend/Amf/Parse/TypeLoader.php';
0045 
0046 /** @see Zend_Auth */
0047 // require_once 'Zend/Auth.php';
0048 /**
0049  * An AMF gateway server implementation to allow the connection of the Adobe Flash Player to
0050  * Zend Framework
0051  *
0052  * @todo       Make the reflection methods cache and autoload.
0053  * @package    Zend_Amf
0054  * @subpackage Server
0055  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0056  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0057  */
0058 class Zend_Amf_Server implements Zend_Server_Interface
0059 {
0060     /**
0061      * Array of dispatchables
0062      * @var array
0063      */
0064     protected $_methods = array();
0065 
0066     /**
0067      * Array of classes that can be called without being explicitly loaded
0068      *
0069      * Keys are class names.
0070      *
0071      * @var array
0072      */
0073     protected $_classAllowed = array();
0074 
0075     /**
0076      * Loader for classes in added directories
0077      * @var Zend_Loader_PluginLoader
0078      */
0079     protected $_loader;
0080 
0081     /**
0082      * @var bool Production flag; whether or not to return exception messages
0083      */
0084     protected $_production = true;
0085 
0086     /**
0087      * Request processed
0088      * @var null|Zend_Amf_Request
0089      */
0090     protected $_request = null;
0091 
0092     /**
0093      * Class to use for responses
0094      * @var null|Zend_Amf_Response
0095      */
0096     protected $_response;
0097 
0098     /**
0099      * Dispatch table of name => method pairs
0100      * @var array
0101      */
0102     protected $_table = array();
0103 
0104     /**
0105      *
0106      * @var bool session flag; whether or not to add a session to each response.
0107      */
0108     protected $_session = false;
0109 
0110     /**
0111      * Namespace allows all AMF calls to not clobber other PHP session variables
0112      * @var Zend_Session_NameSpace default session namespace zend_amf
0113      */
0114     protected $_sesionNamespace = 'zend_amf';
0115 
0116     /**
0117      * Set the default session.name if php_
0118      * @var string
0119      */
0120     protected $_sessionName = 'PHPSESSID';
0121 
0122     /**
0123      * Authentication handler object
0124      *
0125      * @var Zend_Amf_Auth_Abstract
0126      */
0127     protected $_auth;
0128     /**
0129      * ACL handler object
0130      *
0131      * @var Zend_Acl
0132      */
0133     protected $_acl;
0134     /**
0135      * The server constructor
0136      */
0137     public function __construct()
0138     {
0139         Zend_Amf_Parse_TypeLoader::setResourceLoader(new Zend_Loader_PluginLoader(array("Zend_Amf_Parse_Resource" => "Zend/Amf/Parse/Resource")));
0140     }
0141 
0142     /**
0143      * Set authentication adapter
0144      *
0145      * If the authentication adapter implements a "getAcl()" method, populate 
0146      * the ACL of this instance with it (if none exists already).
0147      *
0148      * @param  Zend_Amf_Auth_Abstract $auth
0149      * @return Zend_Amf_Server
0150      */
0151     public function setAuth(Zend_Amf_Auth_Abstract $auth)
0152     {
0153         $this->_auth = $auth;
0154         if ((null === $this->getAcl()) && method_exists($auth, 'getAcl')) {
0155             $this->setAcl($auth->getAcl());
0156         }
0157         return $this;
0158     }
0159    /**
0160      * Get authentication adapter
0161      *
0162      * @return Zend_Amf_Auth_Abstract
0163      */
0164     public function getAuth()
0165     {
0166         return $this->_auth;
0167     }
0168 
0169     /**
0170      * Set ACL adapter
0171      *
0172      * @param  Zend_Acl $acl
0173      * @return Zend_Amf_Server
0174      */
0175     public function setAcl(Zend_Acl $acl)
0176     {
0177         $this->_acl = $acl;
0178         return $this;
0179     }
0180    /**
0181      * Get ACL adapter
0182      *
0183      * @return Zend_Acl
0184      */
0185     public function getAcl()
0186     {
0187         return $this->_acl;
0188     }
0189 
0190     /**
0191      * Set production flag
0192      *
0193      * @param  bool $flag
0194      * @return Zend_Amf_Server
0195      */
0196     public function setProduction($flag)
0197     {
0198         $this->_production = (bool) $flag;
0199         return $this;
0200     }
0201 
0202     /**
0203      * Whether or not the server is in production
0204      *
0205      * @return bool
0206      */
0207     public function isProduction()
0208     {
0209         return $this->_production;
0210     }
0211 
0212     /**
0213      * @param namespace of all incoming sessions defaults to Zend_Amf
0214      * @return Zend_Amf_Server
0215      */
0216     public function setSession($namespace = 'Zend_Amf')
0217     {
0218         // require_once 'Zend/Session.php';
0219         $this->_session = true;
0220         $this->_sesionNamespace = new Zend_Session_Namespace($namespace);
0221         return $this;
0222     }
0223 
0224     /**
0225      * Whether of not the server is using sessions
0226      * @return bool
0227      */
0228     public function isSession()
0229     {
0230         return $this->_session;
0231     }
0232 
0233     /**
0234      * Check if the ACL allows accessing the function or method
0235      *
0236      * @param string|object $object Object or class being accessed
0237      * @param string $function Function or method being accessed
0238      * @return unknown_type
0239      */
0240     protected function _checkAcl($object, $function)
0241     {
0242         if(!$this->_acl) {
0243             return true;
0244         }
0245         if($object) {
0246             $class = is_object($object)?get_class($object):$object;
0247             if(!$this->_acl->has($class)) {
0248                 // require_once 'Zend/Acl/Resource.php';
0249                 $this->_acl->add(new Zend_Acl_Resource($class));
0250             }
0251             $call = array($object, "initAcl");
0252             if(is_callable($call) && !call_user_func($call, $this->_acl)) {
0253                 // if initAcl returns false, no ACL check
0254                 return true;
0255             }
0256         } else {
0257             $class = null;
0258         }
0259 
0260         $auth = Zend_Auth::getInstance();
0261         if($auth->hasIdentity()) {
0262             $role = $auth->getIdentity()->role;
0263         } else {
0264             if($this->_acl->hasRole(Zend_Amf_Constants::GUEST_ROLE)) {
0265                 $role = Zend_Amf_Constants::GUEST_ROLE;
0266             } else {
0267                 // require_once 'Zend/Amf/Server/Exception.php';
0268                 throw new Zend_Amf_Server_Exception("Unauthenticated access not allowed");
0269             }
0270         }
0271         if($this->_acl->isAllowed($role, $class, $function)) {
0272             return true;
0273         } else {
0274             // require_once 'Zend/Amf/Server/Exception.php';
0275             throw new Zend_Amf_Server_Exception("Access not allowed");
0276         }
0277     }
0278 
0279     /**
0280      * Get PluginLoader for the Server
0281      *
0282      * @return Zend_Loader_PluginLoader
0283      */
0284     protected function getLoader()
0285     {
0286         if(empty($this->_loader)) {
0287             // require_once 'Zend/Loader/PluginLoader.php';
0288             $this->_loader = new Zend_Loader_PluginLoader();
0289         }
0290         return $this->_loader;
0291     }
0292 
0293     /**
0294      * Loads a remote class or method and executes the function and returns
0295      * the result
0296      *
0297      * @param  string $method Is the method to execute
0298      * @param  mixed $param values for the method
0299      * @return mixed $response the result of executing the method
0300      * @throws Zend_Amf_Server_Exception
0301      */
0302     protected function _dispatch($method, $params = null, $source = null)
0303     {
0304         if($source) {
0305             if(($mapped = Zend_Amf_Parse_TypeLoader::getMappedClassName($source)) !== false) {
0306                 $source = $mapped;
0307             }
0308         }
0309         $qualifiedName = empty($source) ? $method : $source . '.' . $method;
0310 
0311         if (!isset($this->_table[$qualifiedName])) {
0312             // if source is null a method that was not defined was called.
0313             if ($source) {
0314                 $className = str_replace('.', '_', $source);
0315                 if(class_exists($className, false) && !isset($this->_classAllowed[$className])) {
0316                     // require_once 'Zend/Amf/Server/Exception.php';
0317                     throw new Zend_Amf_Server_Exception('Can not call "' . $className . '" - use setClass()');
0318                 }
0319                 try {
0320                     $this->getLoader()->load($className);
0321                 } catch (Exception $e) {
0322                     // require_once 'Zend/Amf/Server/Exception.php';
0323                     throw new Zend_Amf_Server_Exception('Class "' . $className . '" does not exist: '.$e->getMessage(), 0, $e);
0324                 }
0325                 // Add the new loaded class to the server.
0326                     // require_once 'Zend/Amf/Server/Exception.php';
0327                 $this->setClass($className, $source);
0328             }
0329 
0330             if (!isset($this->_table[$qualifiedName])) {
0331                 // Source is null or doesn't contain specified method
0332                 // require_once 'Zend/Amf/Server/Exception.php';
0333                 throw new Zend_Amf_Server_Exception('Method "' . $method . '" does not exist');
0334             }
0335         }
0336 
0337         $info = $this->_table[$qualifiedName];
0338         $argv = $info->getInvokeArguments();
0339 
0340         if (0 < count($argv)) {
0341             $params = array_merge($params, $argv);
0342         }
0343 
0344         $params = $this->_castParameters($info, $params);
0345 
0346         if ($info instanceof Zend_Server_Reflection_Function) {
0347             $func = $info->getName();
0348             $this->_checkAcl(null, $func);
0349             $return = call_user_func_array($func, $params);
0350         } elseif ($info instanceof Zend_Server_Reflection_Method) {
0351             // Get class
0352             $class = $info->getDeclaringClass()->getName();
0353             if ('static' == $info->isStatic()) {
0354                 // for some reason, invokeArgs() does not work the same as
0355                 // invoke(), and expects the first argument to be an object.
0356                 // So, using a callback if the method is static.
0357                 $this->_checkAcl($class, $info->getName());
0358                 $return = call_user_func_array(array($class, $info->getName()), $params);
0359             } else {
0360                 // Object methods
0361                 try {
0362                     $object = $info->getDeclaringClass()->newInstance();
0363                 } catch (Exception $e) {
0364                     // require_once 'Zend/Amf/Server/Exception.php';
0365                     throw new Zend_Amf_Server_Exception('Error instantiating class ' . $class . ' to invoke method ' . $info->getName() . ': '.$e->getMessage(), 621, $e);
0366                 }
0367                 $this->_checkAcl($object, $info->getName());
0368                 $return = $info->invokeArgs($object, $params);
0369             }
0370         } else {
0371             // require_once 'Zend/Amf/Server/Exception.php';
0372             throw new Zend_Amf_Server_Exception('Method missing implementation ' . get_class($info));
0373         }
0374 
0375         return $return;
0376     }
0377 
0378     /**
0379      * Handles each of the 11 different command message types.
0380      *
0381      * A command message is a flex.messaging.messages.CommandMessage
0382      *
0383      * @see    Zend_Amf_Value_Messaging_CommandMessage
0384      * @param  Zend_Amf_Value_Messaging_CommandMessage $message
0385      * @return Zend_Amf_Value_Messaging_AcknowledgeMessage
0386      */
0387     protected function _loadCommandMessage(Zend_Amf_Value_Messaging_CommandMessage $message)
0388     {
0389         // require_once 'Zend/Amf/Value/Messaging/AcknowledgeMessage.php';
0390         switch($message->operation) {
0391             case Zend_Amf_Value_Messaging_CommandMessage::DISCONNECT_OPERATION :
0392             case Zend_Amf_Value_Messaging_CommandMessage::CLIENT_PING_OPERATION :
0393                 $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
0394                 break;
0395             case Zend_Amf_Value_Messaging_CommandMessage::LOGIN_OPERATION :
0396                 $data = explode(':', base64_decode($message->body));
0397                 $userid = $data[0];
0398                 $password = isset($data[1])?$data[1]:"";
0399                 if(empty($userid)) {
0400                     // require_once 'Zend/Amf/Server/Exception.php';
0401                     throw new Zend_Amf_Server_Exception('Login failed: username not supplied');
0402                 }
0403                 if(!$this->_handleAuth($userid, $password)) {
0404                     // require_once 'Zend/Amf/Server/Exception.php';
0405                     throw new Zend_Amf_Server_Exception('Authentication failed');
0406                 }
0407                 $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
0408                 break;
0409            case Zend_Amf_Value_Messaging_CommandMessage::LOGOUT_OPERATION :
0410                 if($this->_auth) {
0411                     Zend_Auth::getInstance()->clearIdentity();
0412                 }
0413                 $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
0414                 break;
0415             default :
0416                 // require_once 'Zend/Amf/Server/Exception.php';
0417                 throw new Zend_Amf_Server_Exception('CommandMessage::' . $message->operation . ' not implemented');
0418                 break;
0419         }
0420         return $return;
0421     }
0422 
0423     /**
0424      * Create appropriate error message
0425      *
0426      * @param int $objectEncoding Current AMF encoding
0427      * @param string $message Message that was being processed when error happened
0428      * @param string $description Error description
0429      * @param mixed $detail Detailed data about the error
0430      * @param int $code Error code
0431      * @param int $line Error line
0432      * @return Zend_Amf_Value_Messaging_ErrorMessage|array
0433      */
0434     protected function _errorMessage($objectEncoding, $message, $description, $detail, $code, $line)
0435     {
0436         $return = null;
0437         switch ($objectEncoding) {
0438             case Zend_Amf_Constants::AMF0_OBJECT_ENCODING :
0439                 return array (
0440                         'description' => ($this->isProduction ()) ? '' : $description,
0441                         'detail' => ($this->isProduction ()) ? '' : $detail,
0442                         'line' => ($this->isProduction ()) ? 0 : $line,
0443                         'code' => $code
0444                 );
0445             case Zend_Amf_Constants::AMF3_OBJECT_ENCODING :
0446                 // require_once 'Zend/Amf/Value/Messaging/ErrorMessage.php';
0447                 $return = new Zend_Amf_Value_Messaging_ErrorMessage ( $message );
0448                 $return->faultString = $this->isProduction () ? '' : $description;
0449                 $return->faultCode = $code;
0450                 $return->faultDetail = $this->isProduction () ? '' : $detail;
0451                 break;
0452         }
0453         return $return;
0454     }
0455 
0456     /**
0457      * Handle AMF authentication
0458      *
0459      * @param string $userid
0460      * @param string $password
0461      * @return boolean
0462      */
0463     protected function _handleAuth( $userid,  $password)
0464     {
0465         if (!$this->_auth) {
0466             return true;
0467         }
0468         $this->_auth->setCredentials($userid, $password);
0469         $auth = Zend_Auth::getInstance();
0470         $result = $auth->authenticate($this->_auth);
0471         if ($result->isValid()) {
0472             if (!$this->isSession()) {
0473                 $this->setSession();
0474             }
0475             return true;
0476         } else {
0477             // authentication failed, good bye
0478             // require_once 'Zend/Amf/Server/Exception.php';
0479             throw new Zend_Amf_Server_Exception(
0480                 "Authentication failed: " . join("\n",
0481                     $result->getMessages()), $result->getCode());
0482         }
0483 
0484     }
0485 
0486     /**
0487      * Takes the deserialized AMF request and performs any operations.
0488      *
0489      * @todo   should implement and SPL observer pattern for custom AMF headers
0490      * @todo   DescribeService support
0491      * @param  Zend_Amf_Request $request
0492      * @return Zend_Amf_Response
0493      * @throws Zend_Amf_server_Exception|Exception
0494      */
0495     protected function _handle(Zend_Amf_Request $request)
0496     {
0497         // Get the object encoding of the request.
0498         $objectEncoding = $request->getObjectEncoding();
0499 
0500         // create a response object to place the output from the services.
0501         $response = $this->getResponse();
0502 
0503         // set response encoding
0504         $response->setObjectEncoding($objectEncoding);
0505 
0506         // Authenticate, if we have credential headers
0507         $error   = false;
0508         $headers = $request->getAmfHeaders();
0509         if (isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]) 
0510             && isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->userid)
0511             && isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->password)
0512         ) {
0513             try {
0514                 if ($this->_handleAuth(
0515                         $headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->userid,
0516                         $headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->password
0517                 )) {
0518                     // use RequestPersistentHeader to clear credentials
0519                     $response->addAmfHeader(
0520                         new Zend_Amf_Value_MessageHeader(
0521                             Zend_Amf_Constants::PERSISTENT_HEADER,
0522                             false,
0523                             new Zend_Amf_Value_MessageHeader(
0524                                 Zend_Amf_Constants::CREDENTIALS_HEADER,
0525                                 false, null
0526                             )
0527                         )
0528                     );
0529                 }
0530             } catch (Exception $e) {
0531                 // Error during authentication; report it
0532                 $error = $this->_errorMessage(
0533                     $objectEncoding, 
0534                     '', 
0535                     $e->getMessage(),
0536                     $e->getTraceAsString(),
0537                     $e->getCode(),
0538                     $e->getLine()
0539                 );
0540                 $responseType = Zend_AMF_Constants::STATUS_METHOD;
0541             }
0542         }
0543 
0544         // Iterate through each of the service calls in the AMF request
0545         foreach($request->getAmfBodies() as $body)
0546         {
0547             if ($error) {
0548                 // Error during authentication; just report it and be done
0549                 $responseURI = $body->getResponseURI() . $responseType;
0550                 $newBody     = new Zend_Amf_Value_MessageBody($responseURI, null, $error);
0551                 $response->addAmfBody($newBody);
0552                 continue;
0553             }
0554             try {
0555                 switch ($objectEncoding) {
0556                     case Zend_Amf_Constants::AMF0_OBJECT_ENCODING:
0557                         // AMF0 Object Encoding
0558                         $targetURI = $body->getTargetURI();
0559                         $message = '';
0560 
0561                         // Split the target string into its values.
0562                         $source = substr($targetURI, 0, strrpos($targetURI, '.'));
0563 
0564                         if ($source) {
0565                             // Break off method name from namespace into source
0566                             $method = substr(strrchr($targetURI, '.'), 1);
0567                             $return = $this->_dispatch($method, $body->getData(), $source);
0568                         } else {
0569                             // Just have a method name.
0570                             $return = $this->_dispatch($targetURI, $body->getData());
0571                         }
0572                         break;
0573                     case Zend_Amf_Constants::AMF3_OBJECT_ENCODING:
0574                     default:
0575                         // AMF3 read message type
0576                         $message = $body->getData();
0577                         if ($message instanceof Zend_Amf_Value_Messaging_CommandMessage) {
0578                             // async call with command message
0579                             $return = $this->_loadCommandMessage($message);
0580                         } elseif ($message instanceof Zend_Amf_Value_Messaging_RemotingMessage) {
0581                             // require_once 'Zend/Amf/Value/Messaging/AcknowledgeMessage.php';
0582                             $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
0583                             $return->body = $this->_dispatch($message->operation, $message->body, $message->source);
0584                         } else {
0585                             // Amf3 message sent with netConnection
0586                             $targetURI = $body->getTargetURI();
0587 
0588                             // Split the target string into its values.
0589                             $source = substr($targetURI, 0, strrpos($targetURI, '.'));
0590 
0591                             if ($source) {
0592                                 // Break off method name from namespace into source
0593                                 $method = substr(strrchr($targetURI, '.'), 1);
0594                                 $return = $this->_dispatch($method, $body->getData(), $source);
0595                             } else {
0596                                 // Just have a method name.
0597                                 $return = $this->_dispatch($targetURI, $body->getData());
0598                             }
0599                         }
0600                         break;
0601                 }
0602                 $responseType = Zend_AMF_Constants::RESULT_METHOD;
0603             } catch (Exception $e) {
0604                 $return = $this->_errorMessage($objectEncoding, $message,
0605                     $e->getMessage(), $e->getTraceAsString(),$e->getCode(),  $e->getLine());
0606                 $responseType = Zend_AMF_Constants::STATUS_METHOD;
0607             }
0608 
0609             $responseURI = $body->getResponseURI() . $responseType;
0610             $newBody     = new Zend_Amf_Value_MessageBody($responseURI, null, $return);
0611             $response->addAmfBody($newBody);
0612         }
0613         // Add a session header to the body if session is requested.
0614         if($this->isSession()) {
0615            $currentID = session_id();
0616            $joint = "?";
0617            if(isset($_SERVER['QUERY_STRING'])) {
0618                if(!strpos($_SERVER['QUERY_STRING'], $currentID) !== FALSE) {
0619                    if(strrpos($_SERVER['QUERY_STRING'], "?") !== FALSE) {
0620                        $joint = "&";
0621                    }
0622                }
0623            }
0624 
0625             // create a new AMF message header with the session id as a variable.
0626             $sessionValue = $joint . $this->_sessionName . "=" . $currentID;
0627             $sessionHeader = new Zend_Amf_Value_MessageHeader(Zend_Amf_Constants::URL_APPEND_HEADER, false, $sessionValue);
0628             $response->addAmfHeader($sessionHeader);
0629         }
0630 
0631         // serialize the response and return serialized body.
0632         $response->finalize();
0633     }
0634 
0635     /**
0636      * Handle an AMF call from the gateway.
0637      *
0638      * @param  null|Zend_Amf_Request $request Optional
0639      * @return Zend_Amf_Response
0640      */
0641     public function handle($request = null)
0642     {
0643         // Check if request was passed otherwise get it from the server
0644         if ($request === null || !$request instanceof Zend_Amf_Request) {
0645             $request = $this->getRequest();
0646         } else {
0647             $this->setRequest($request);
0648         }
0649         if ($this->isSession()) {
0650              // Check if a session is being sent from the amf call
0651              if (isset($_COOKIE[$this->_sessionName])) {
0652                  session_id($_COOKIE[$this->_sessionName]);
0653              }
0654         }
0655 
0656         // Check for errors that may have happend in deserialization of Request.
0657         try {
0658             // Take converted PHP objects and handle service call.
0659             // Serialize to Zend_Amf_response for output stream
0660             $this->_handle($request);
0661             $response = $this->getResponse();
0662         } catch (Exception $e) {
0663             // Handle any errors in the serialization and service  calls.
0664             // require_once 'Zend/Amf/Server/Exception.php';
0665             throw new Zend_Amf_Server_Exception('Handle error: ' . $e->getMessage() . ' ' . $e->getLine(), 0, $e);
0666         }
0667 
0668         // Return the Amf serialized output string
0669         return $response;
0670     }
0671 
0672     /**
0673      * Set request object
0674      *
0675      * @param  string|Zend_Amf_Request $request
0676      * @return Zend_Amf_Server
0677      */
0678     public function setRequest($request)
0679     {
0680         if (is_string($request) && class_exists($request)) {
0681             $request = new $request();
0682             if (!$request instanceof Zend_Amf_Request) {
0683                 // require_once 'Zend/Amf/Server/Exception.php';
0684                 throw new Zend_Amf_Server_Exception('Invalid request class');
0685             }
0686         } elseif (!$request instanceof Zend_Amf_Request) {
0687             // require_once 'Zend/Amf/Server/Exception.php';
0688             throw new Zend_Amf_Server_Exception('Invalid request object');
0689         }
0690         $this->_request = $request;
0691         return $this;
0692     }
0693 
0694     /**
0695      * Return currently registered request object
0696      *
0697      * @return null|Zend_Amf_Request
0698      */
0699     public function getRequest()
0700     {
0701         if (null === $this->_request) {
0702             // require_once 'Zend/Amf/Request/Http.php';
0703             $this->setRequest(new Zend_Amf_Request_Http());
0704         }
0705 
0706         return $this->_request;
0707     }
0708 
0709     /**
0710      * Public access method to private Zend_Amf_Server_Response reference
0711      *
0712      * @param  string|Zend_Amf_Server_Response $response
0713      * @return Zend_Amf_Server
0714      */
0715     public function setResponse($response)
0716     {
0717         if (is_string($response) && class_exists($response)) {
0718             $response = new $response();
0719             if (!$response instanceof Zend_Amf_Response) {
0720                 // require_once 'Zend/Amf/Server/Exception.php';
0721                 throw new Zend_Amf_Server_Exception('Invalid response class');
0722             }
0723         } elseif (!$response instanceof Zend_Amf_Response) {
0724             // require_once 'Zend/Amf/Server/Exception.php';
0725             throw new Zend_Amf_Server_Exception('Invalid response object');
0726         }
0727         $this->_response = $response;
0728         return $this;
0729     }
0730 
0731     /**
0732      * get a reference to the Zend_Amf_response instance
0733      *
0734      * @return Zend_Amf_Server_Response
0735      */
0736     public function getResponse()
0737     {
0738         if (null === ($response = $this->_response)) {
0739             // require_once 'Zend/Amf/Response/Http.php';
0740             $this->setResponse(new Zend_Amf_Response_Http());
0741         }
0742         return $this->_response;
0743     }
0744 
0745     /**
0746      * Attach a class or object to the server
0747      *
0748      * Class may be either a class name or an instantiated object. Reflection
0749      * is done on the class or object to determine the available public
0750      * methods, and each is attached to the server as and available method. If
0751      * a $namespace has been provided, that namespace is used to prefix
0752      * AMF service call.
0753      *
0754      * @param  string|object $class
0755      * @param  string $namespace Optional
0756      * @param  mixed $arg Optional arguments to pass to a method
0757      * @return Zend_Amf_Server
0758      * @throws Zend_Amf_Server_Exception on invalid input
0759      */
0760     public function setClass($class, $namespace = '', $argv = null)
0761     {
0762         if (is_string($class) && !class_exists($class)){
0763             // require_once 'Zend/Amf/Server/Exception.php';
0764             throw new Zend_Amf_Server_Exception('Invalid method or class');
0765         } elseif (!is_string($class) && !is_object($class)) {
0766             // require_once 'Zend/Amf/Server/Exception.php';
0767             throw new Zend_Amf_Server_Exception('Invalid method or class; must be a classname or object');
0768         }
0769 
0770         $args = null;
0771         if (2 < func_num_args()) {
0772             $args = array_slice(func_get_args(), 2);
0773         }
0774 
0775         // Use the class name as the name space by default.
0776 
0777         if ($namespace == '') {
0778             $namespace = is_object($class) ? get_class($class) : $class;
0779         }
0780 
0781         $this->_classAllowed[is_object($class) ? get_class($class) : $class] = true;
0782 
0783         $this->_methods[] = Zend_Server_Reflection::reflectClass($class, $args, $namespace);
0784         $this->_buildDispatchTable();
0785 
0786         return $this;
0787     }
0788 
0789     /**
0790      * Attach a function to the server
0791      *
0792      * Additional arguments to pass to the function at dispatch may be passed;
0793      * any arguments following the namespace will be aggregated and passed at
0794      * dispatch time.
0795      *
0796      * @param  string|array $function Valid callback
0797      * @param  string $namespace Optional namespace prefix
0798      * @return Zend_Amf_Server
0799      * @throws Zend_Amf_Server_Exception
0800      */
0801     public function addFunction($function, $namespace = '')
0802     {
0803         if (!is_string($function) && !is_array($function)) {
0804             // require_once 'Zend/Amf/Server/Exception.php';
0805             throw new Zend_Amf_Server_Exception('Unable to attach function');
0806         }
0807 
0808         $argv = null;
0809         if (2 < func_num_args()) {
0810             $argv = array_slice(func_get_args(), 2);
0811         }
0812 
0813         $function = (array) $function;
0814         foreach ($function as $func) {
0815             if (!is_string($func) || !function_exists($func)) {
0816                 // require_once 'Zend/Amf/Server/Exception.php';
0817                 throw new Zend_Amf_Server_Exception('Unable to attach function');
0818             }
0819             $this->_methods[] = Zend_Server_Reflection::reflectFunction($func, $argv, $namespace);
0820         }
0821 
0822         $this->_buildDispatchTable();
0823         return $this;
0824     }
0825 
0826 
0827     /**
0828      * Creates an array of directories in which services can reside.
0829      * TODO: add support for prefixes?
0830      *
0831      * @param string $dir
0832      */
0833     public function addDirectory($dir)
0834     {
0835         $this->getLoader()->addPrefixPath("", $dir);
0836     }
0837 
0838     /**
0839      * Returns an array of directories that can hold services.
0840      *
0841      * @return array
0842      */
0843     public function getDirectory()
0844     {
0845         return $this->getLoader()->getPaths("");
0846     }
0847 
0848     /**
0849      * (Re)Build the dispatch table
0850      *
0851      * The dispatch table consists of a an array of method name =>
0852      * Zend_Server_Reflection_Function_Abstract pairs
0853      *
0854      * @return void
0855      */
0856     protected function _buildDispatchTable()
0857     {
0858         $table = array();
0859         foreach ($this->_methods as $key => $dispatchable) {
0860             if ($dispatchable instanceof Zend_Server_Reflection_Function_Abstract) {
0861                 $ns   = $dispatchable->getNamespace();
0862                 $name = $dispatchable->getName();
0863                 $name = empty($ns) ? $name : $ns . '.' . $name;
0864 
0865                 if (isset($table[$name])) {
0866                     // require_once 'Zend/Amf/Server/Exception.php';
0867                     throw new Zend_Amf_Server_Exception('Duplicate method registered: ' . $name);
0868                 }
0869                 $table[$name] = $dispatchable;
0870                 continue;
0871             }
0872 
0873             if ($dispatchable instanceof Zend_Server_Reflection_Class) {
0874                 foreach ($dispatchable->getMethods() as $method) {
0875                     $ns   = $method->getNamespace();
0876                     $name = $method->getName();
0877                     $name = empty($ns) ? $name : $ns . '.' . $name;
0878 
0879                     if (isset($table[$name])) {
0880                         // require_once 'Zend/Amf/Server/Exception.php';
0881                         throw new Zend_Amf_Server_Exception('Duplicate method registered: ' . $name);
0882                     }
0883                     $table[$name] = $method;
0884                     continue;
0885                 }
0886             }
0887         }
0888         $this->_table = $table;
0889     }
0890 
0891 
0892 
0893     /**
0894      * Raise a server fault
0895      *
0896      * Unimplemented
0897      *
0898      * @param  string|Exception $fault
0899      * @return void
0900      */
0901     public function fault($fault = null, $code = 404)
0902     {
0903     }
0904 
0905     /**
0906      * Returns a list of registered methods
0907      *
0908      * Returns an array of dispatchables (Zend_Server_Reflection_Function,
0909      * _Method, and _Class items).
0910      *
0911      * @return array
0912      */
0913     public function getFunctions()
0914     {
0915         return $this->_table;
0916     }
0917 
0918     /**
0919      * Set server persistence
0920      *
0921      * Unimplemented
0922      *
0923      * @param  mixed $mode
0924      * @return void
0925      */
0926     public function setPersistence($mode)
0927     {
0928     }
0929 
0930     /**
0931      * Load server definition
0932      *
0933      * Unimplemented
0934      *
0935      * @param  array $definition
0936      * @return void
0937      */
0938     public function loadFunctions($definition)
0939     {
0940     }
0941 
0942     /**
0943      * Map ActionScript classes to PHP classes
0944      *
0945      * @param  string $asClass
0946      * @param  string $phpClass
0947      * @return Zend_Amf_Server
0948      */
0949     public function setClassMap($asClass, $phpClass)
0950     {
0951         // require_once 'Zend/Amf/Parse/TypeLoader.php';
0952         Zend_Amf_Parse_TypeLoader::setMapping($asClass, $phpClass);
0953         return $this;
0954     }
0955 
0956     /**
0957      * List all available methods
0958      *
0959      * Returns an array of method names.
0960      *
0961      * @return array
0962      */
0963     public function listMethods()
0964     {
0965         return array_keys($this->_table);
0966     }
0967 
0968     /**
0969      * Cast parameters
0970      *
0971      * Takes the provided parameters from the request, and attempts to cast them
0972      * to objects, if the prototype defines any as explicit object types
0973      * 
0974      * @param  Reflection $reflectionMethod 
0975      * @param  array $params 
0976      * @return array
0977      */
0978     protected function _castParameters($reflectionMethod, array $params)
0979     {
0980         $prototypes = $reflectionMethod->getPrototypes();
0981         $nonObjectTypes = array(
0982             'null',
0983             'mixed',
0984             'void',
0985             'unknown',
0986             'bool',
0987             'boolean',
0988             'number',
0989             'int',
0990             'integer',
0991             'double',
0992             'float',
0993             'string',
0994             'array',
0995             'object',
0996             'stdclass',
0997         );
0998         $types      = array();
0999         foreach ($prototypes as $prototype) {
1000             foreach ($prototype->getParameters() as $parameter) {
1001                 $type = $parameter->getType();
1002                 if (in_array(strtolower($type), $nonObjectTypes)) {
1003                     continue;
1004                 }
1005                 $position = $parameter->getPosition();
1006                 $types[$position] = $type;
1007             }
1008         }
1009 
1010         if (empty($types)) {
1011             return $params;
1012         }
1013 
1014         foreach ($params as $position => $value) {
1015             if (!isset($types[$position])) {
1016                 // No specific type to cast to? done
1017                 continue;
1018             }
1019 
1020             $type = $types[$position];
1021 
1022             if (!class_exists($type)) {
1023                 // Not a class, apparently. done
1024                 continue;
1025             }
1026 
1027             if ($value instanceof $type) {
1028                 // Already of the right type? done
1029                 continue;
1030             }
1031 
1032             if (!is_array($value) && !is_object($value)) {
1033                 // Can't cast scalars to objects easily; done
1034                 continue;
1035             }
1036 
1037             // Create instance, and loop through value to set
1038             $object = new $type;
1039             foreach ($value as $property => $defined) {
1040                 $object->{$property} = $defined;
1041             }
1042 
1043             $params[$position] = $object;
1044         }
1045 
1046         return $params;
1047     }
1048 }