1008 lines
32 KiB
PHP
1008 lines
32 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This source file is subject to the new BSD license that is bundled
|
|
* with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://framework.zend.com/license/new-bsd
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@zend.com so we can send you a copy immediately.
|
|
*
|
|
* @category Zend
|
|
* @package Zend_Amf
|
|
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
* @version $Id$
|
|
*/
|
|
|
|
/** @see Zend_Server_Interface */
|
|
|
|
/** @see Zend_Server_Reflection */
|
|
|
|
/** @see Zend_Amf_Constants */
|
|
|
|
/** @see Zend_Amf_Value_MessageBody */
|
|
|
|
/** @see Zend_Amf_Value_MessageHeader */
|
|
|
|
/** @see Zend_Amf_Value_Messaging_CommandMessage */
|
|
|
|
/** @see Zend_Loader_PluginLoader */
|
|
|
|
/** @see Zend_Amf_Parse_TypeLoader */
|
|
|
|
/** @see Zend_Auth */
|
|
/**
|
|
* An AMF gateway server implementation to allow the connection of the Adobe Flash Player to
|
|
* Zend Framework
|
|
*
|
|
* @todo Make the reflection methods cache and autoload.
|
|
* @package Zend_Amf
|
|
* @subpackage Server
|
|
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
class Zend_Amf_Server implements Zend_Server_Interface
|
|
{
|
|
/**
|
|
* Array of dispatchables
|
|
* @var array
|
|
*/
|
|
protected $_methods = array();
|
|
|
|
/**
|
|
* Array of classes that can be called without being explicitly loaded
|
|
*
|
|
* Keys are class names.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_classAllowed = array();
|
|
|
|
/**
|
|
* Loader for classes in added directories
|
|
* @var Zend_Loader_PluginLoader
|
|
*/
|
|
protected $_loader;
|
|
|
|
/**
|
|
* @var bool Production flag; whether or not to return exception messages
|
|
*/
|
|
protected $_production = true;
|
|
|
|
/**
|
|
* Request processed
|
|
* @var null|Zend_Amf_Request
|
|
*/
|
|
protected $_request = null;
|
|
|
|
/**
|
|
* Class to use for responses
|
|
* @var null|Zend_Amf_Response
|
|
*/
|
|
protected $_response;
|
|
|
|
/**
|
|
* Dispatch table of name => method pairs
|
|
* @var array
|
|
*/
|
|
protected $_table = array();
|
|
|
|
/**
|
|
*
|
|
* @var bool session flag; whether or not to add a session to each response.
|
|
*/
|
|
protected $_session = false;
|
|
|
|
/**
|
|
* Namespace allows all AMF calls to not clobber other PHP session variables
|
|
* @var Zend_Session_NameSpace default session namespace zend_amf
|
|
*/
|
|
protected $_sesionNamespace = 'zend_amf';
|
|
|
|
/**
|
|
* Set the default session.name if php_
|
|
* @var string
|
|
*/
|
|
protected $_sessionName = 'PHPSESSID';
|
|
|
|
/**
|
|
* Authentication handler object
|
|
*
|
|
* @var Zend_Amf_Auth_Abstract
|
|
*/
|
|
protected $_auth;
|
|
/**
|
|
* ACL handler object
|
|
*
|
|
* @var Zend_Acl
|
|
*/
|
|
protected $_acl;
|
|
/**
|
|
* The server constructor
|
|
*/
|
|
public function __construct()
|
|
{
|
|
Zend_Amf_Parse_TypeLoader::setResourceLoader(new Zend_Loader_PluginLoader(array("Zend_Amf_Parse_Resource" => "Zend/Amf/Parse/Resource")));
|
|
}
|
|
|
|
/**
|
|
* Set authentication adapter
|
|
*
|
|
* If the authentication adapter implements a "getAcl()" method, populate
|
|
* the ACL of this instance with it (if none exists already).
|
|
*
|
|
* @param Zend_Amf_Auth_Abstract $auth
|
|
* @return Zend_Amf_Server
|
|
*/
|
|
public function setAuth(Zend_Amf_Auth_Abstract $auth)
|
|
{
|
|
$this->_auth = $auth;
|
|
if ((null === $this->getAcl()) && method_exists($auth, 'getAcl')) {
|
|
$this->setAcl($auth->getAcl());
|
|
}
|
|
return $this;
|
|
}
|
|
/**
|
|
* Get authentication adapter
|
|
*
|
|
* @return Zend_Amf_Auth_Abstract
|
|
*/
|
|
public function getAuth()
|
|
{
|
|
return $this->_auth;
|
|
}
|
|
|
|
/**
|
|
* Set ACL adapter
|
|
*
|
|
* @param Zend_Acl $acl
|
|
* @return Zend_Amf_Server
|
|
*/
|
|
public function setAcl(Zend_Acl $acl)
|
|
{
|
|
$this->_acl = $acl;
|
|
return $this;
|
|
}
|
|
/**
|
|
* Get ACL adapter
|
|
*
|
|
* @return Zend_Acl
|
|
*/
|
|
public function getAcl()
|
|
{
|
|
return $this->_acl;
|
|
}
|
|
|
|
/**
|
|
* Set production flag
|
|
*
|
|
* @param bool $flag
|
|
* @return Zend_Amf_Server
|
|
*/
|
|
public function setProduction($flag)
|
|
{
|
|
$this->_production = (bool) $flag;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Whether or not the server is in production
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isProduction()
|
|
{
|
|
return $this->_production;
|
|
}
|
|
|
|
/**
|
|
* @param namespace of all incoming sessions defaults to Zend_Amf
|
|
* @return Zend_Amf_Server
|
|
*/
|
|
public function setSession($namespace = 'Zend_Amf')
|
|
{
|
|
$this->_session = true;
|
|
$this->_sesionNamespace = new Zend_Session_Namespace($namespace);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Whether of not the server is using sessions
|
|
* @return bool
|
|
*/
|
|
public function isSession()
|
|
{
|
|
return $this->_session;
|
|
}
|
|
|
|
/**
|
|
* Check if the ACL allows accessing the function or method
|
|
*
|
|
* @param string|object $object Object or class being accessed
|
|
* @param string $function Function or method being accessed
|
|
* @return unknown_type
|
|
*/
|
|
protected function _checkAcl($object, $function)
|
|
{
|
|
if(!$this->_acl) {
|
|
return true;
|
|
}
|
|
if($object) {
|
|
$class = is_object($object)?get_class($object):$object;
|
|
if(!$this->_acl->has($class)) {
|
|
$this->_acl->add(new Zend_Acl_Resource($class));
|
|
}
|
|
$call = array($object, "initAcl");
|
|
if(is_callable($call) && !call_user_func($call, $this->_acl)) {
|
|
// if initAcl returns false, no ACL check
|
|
return true;
|
|
}
|
|
} else {
|
|
$class = null;
|
|
}
|
|
|
|
$auth = Zend_Auth::getInstance();
|
|
if($auth->hasIdentity()) {
|
|
$role = $auth->getIdentity()->role;
|
|
} else {
|
|
if($this->_acl->hasRole(Zend_Amf_Constants::GUEST_ROLE)) {
|
|
$role = Zend_Amf_Constants::GUEST_ROLE;
|
|
} else {
|
|
throw new Zend_Amf_Server_Exception("Unauthenticated access not allowed");
|
|
}
|
|
}
|
|
if($this->_acl->isAllowed($role, $class, $function)) {
|
|
return true;
|
|
} else {
|
|
throw new Zend_Amf_Server_Exception("Access not allowed");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get PluginLoader for the Server
|
|
*
|
|
* @return Zend_Loader_PluginLoader
|
|
*/
|
|
protected function getLoader()
|
|
{
|
|
if(empty($this->_loader)) {
|
|
$this->_loader = new Zend_Loader_PluginLoader();
|
|
}
|
|
return $this->_loader;
|
|
}
|
|
|
|
/**
|
|
* Loads a remote class or method and executes the function and returns
|
|
* the result
|
|
*
|
|
* @param string $method Is the method to execute
|
|
* @param mixed $param values for the method
|
|
* @return mixed $response the result of executing the method
|
|
* @throws Zend_Amf_Server_Exception
|
|
*/
|
|
protected function _dispatch($method, $params = null, $source = null)
|
|
{
|
|
if($source) {
|
|
if(($mapped = Zend_Amf_Parse_TypeLoader::getMappedClassName($source)) !== false) {
|
|
$source = $mapped;
|
|
}
|
|
}
|
|
$qualifiedName = empty($source) ? $method : $source . '.' . $method;
|
|
|
|
if (!isset($this->_table[$qualifiedName])) {
|
|
// if source is null a method that was not defined was called.
|
|
if ($source) {
|
|
$className = str_replace('.', '_', $source);
|
|
if(class_exists($className, false) && !isset($this->_classAllowed[$className])) {
|
|
throw new Zend_Amf_Server_Exception('Can not call "' . $className . '" - use setClass()');
|
|
}
|
|
try {
|
|
$this->getLoader()->load($className);
|
|
} catch (Exception $e) {
|
|
throw new Zend_Amf_Server_Exception('Class "' . $className . '" does not exist: '.$e->getMessage(), 0, $e);
|
|
}
|
|
// Add the new loaded class to the server.
|
|
$this->setClass($className, $source);
|
|
}
|
|
|
|
if (!isset($this->_table[$qualifiedName])) {
|
|
// Source is null or doesn't contain specified method
|
|
throw new Zend_Amf_Server_Exception('Method "' . $method . '" does not exist');
|
|
}
|
|
}
|
|
|
|
$info = $this->_table[$qualifiedName];
|
|
$argv = $info->getInvokeArguments();
|
|
|
|
if (0 < count($argv)) {
|
|
$params = array_merge($params, $argv);
|
|
}
|
|
|
|
$params = $this->_castParameters($info, $params);
|
|
|
|
if ($info instanceof Zend_Server_Reflection_Function) {
|
|
$func = $info->getName();
|
|
$this->_checkAcl(null, $func);
|
|
$return = call_user_func_array($func, $params);
|
|
} elseif ($info instanceof Zend_Server_Reflection_Method) {
|
|
// Get class
|
|
$class = $info->getDeclaringClass()->getName();
|
|
if ('static' == $info->isStatic()) {
|
|
// for some reason, invokeArgs() does not work the same as
|
|
// invoke(), and expects the first argument to be an object.
|
|
// So, using a callback if the method is static.
|
|
$this->_checkAcl($class, $info->getName());
|
|
$return = call_user_func_array(array($class, $info->getName()), $params);
|
|
} else {
|
|
// Object methods
|
|
try {
|
|
$object = $info->getDeclaringClass()->newInstance();
|
|
} catch (Exception $e) {
|
|
throw new Zend_Amf_Server_Exception('Error instantiating class ' . $class . ' to invoke method ' . $info->getName() . ': '.$e->getMessage(), 621, $e);
|
|
}
|
|
$this->_checkAcl($object, $info->getName());
|
|
$return = $info->invokeArgs($object, $params);
|
|
}
|
|
} else {
|
|
throw new Zend_Amf_Server_Exception('Method missing implementation ' . get_class($info));
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Handles each of the 11 different command message types.
|
|
*
|
|
* A command message is a flex.messaging.messages.CommandMessage
|
|
*
|
|
* @see Zend_Amf_Value_Messaging_CommandMessage
|
|
* @param Zend_Amf_Value_Messaging_CommandMessage $message
|
|
* @return Zend_Amf_Value_Messaging_AcknowledgeMessage
|
|
*/
|
|
protected function _loadCommandMessage(Zend_Amf_Value_Messaging_CommandMessage $message)
|
|
{
|
|
switch($message->operation) {
|
|
case Zend_Amf_Value_Messaging_CommandMessage::DISCONNECT_OPERATION :
|
|
case Zend_Amf_Value_Messaging_CommandMessage::CLIENT_PING_OPERATION :
|
|
$return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
|
|
break;
|
|
case Zend_Amf_Value_Messaging_CommandMessage::LOGIN_OPERATION :
|
|
$data = explode(':', base64_decode($message->body));
|
|
$userid = $data[0];
|
|
$password = isset($data[1])?$data[1]:"";
|
|
if(empty($userid)) {
|
|
throw new Zend_Amf_Server_Exception('Login failed: username not supplied');
|
|
}
|
|
if(!$this->_handleAuth($userid, $password)) {
|
|
throw new Zend_Amf_Server_Exception('Authentication failed');
|
|
}
|
|
$return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
|
|
break;
|
|
case Zend_Amf_Value_Messaging_CommandMessage::LOGOUT_OPERATION :
|
|
if($this->_auth) {
|
|
Zend_Auth::getInstance()->clearIdentity();
|
|
}
|
|
$return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
|
|
break;
|
|
default :
|
|
throw new Zend_Amf_Server_Exception('CommandMessage::' . $message->operation . ' not implemented');
|
|
break;
|
|
}
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Create appropriate error message
|
|
*
|
|
* @param int $objectEncoding Current AMF encoding
|
|
* @param string $message Message that was being processed when error happened
|
|
* @param string $description Error description
|
|
* @param mixed $detail Detailed data about the error
|
|
* @param int $code Error code
|
|
* @param int $line Error line
|
|
* @return Zend_Amf_Value_Messaging_ErrorMessage|array
|
|
*/
|
|
protected function _errorMessage($objectEncoding, $message, $description, $detail, $code, $line)
|
|
{
|
|
$return = null;
|
|
switch ($objectEncoding) {
|
|
case Zend_Amf_Constants::AMF0_OBJECT_ENCODING :
|
|
return array (
|
|
'description' => ($this->isProduction ()) ? '' : $description,
|
|
'detail' => ($this->isProduction ()) ? '' : $detail,
|
|
'line' => ($this->isProduction ()) ? 0 : $line,
|
|
'code' => $code
|
|
);
|
|
case Zend_Amf_Constants::AMF3_OBJECT_ENCODING :
|
|
$return = new Zend_Amf_Value_Messaging_ErrorMessage ( $message );
|
|
$return->faultString = $this->isProduction () ? '' : $description;
|
|
$return->faultCode = $code;
|
|
$return->faultDetail = $this->isProduction () ? '' : $detail;
|
|
break;
|
|
}
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Handle AMF authentication
|
|
*
|
|
* @param string $userid
|
|
* @param string $password
|
|
* @return boolean
|
|
*/
|
|
protected function _handleAuth( $userid, $password)
|
|
{
|
|
if (!$this->_auth) {
|
|
return true;
|
|
}
|
|
$this->_auth->setCredentials($userid, $password);
|
|
$auth = Zend_Auth::getInstance();
|
|
$result = $auth->authenticate($this->_auth);
|
|
if ($result->isValid()) {
|
|
if (!$this->isSession()) {
|
|
$this->setSession();
|
|
}
|
|
return true;
|
|
} else {
|
|
// authentication failed, good bye
|
|
throw new Zend_Amf_Server_Exception(
|
|
"Authentication failed: " . join("\n",
|
|
$result->getMessages()), $result->getCode());
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Takes the deserialized AMF request and performs any operations.
|
|
*
|
|
* @todo should implement and SPL observer pattern for custom AMF headers
|
|
* @todo DescribeService support
|
|
* @param Zend_Amf_Request $request
|
|
* @return Zend_Amf_Response
|
|
* @throws Zend_Amf_server_Exception|Exception
|
|
*/
|
|
protected function _handle(Zend_Amf_Request $request)
|
|
{
|
|
// Get the object encoding of the request.
|
|
$objectEncoding = $request->getObjectEncoding();
|
|
|
|
// create a response object to place the output from the services.
|
|
$response = $this->getResponse();
|
|
|
|
// set response encoding
|
|
$response->setObjectEncoding($objectEncoding);
|
|
|
|
// Authenticate, if we have credential headers
|
|
$error = false;
|
|
$headers = $request->getAmfHeaders();
|
|
if (isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER])
|
|
&& isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->userid)
|
|
&& isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->password)
|
|
) {
|
|
try {
|
|
if ($this->_handleAuth(
|
|
$headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->userid,
|
|
$headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->password
|
|
)) {
|
|
// use RequestPersistentHeader to clear credentials
|
|
$response->addAmfHeader(
|
|
new Zend_Amf_Value_MessageHeader(
|
|
Zend_Amf_Constants::PERSISTENT_HEADER,
|
|
false,
|
|
new Zend_Amf_Value_MessageHeader(
|
|
Zend_Amf_Constants::CREDENTIALS_HEADER,
|
|
false, null
|
|
)
|
|
)
|
|
);
|
|
}
|
|
} catch (Exception $e) {
|
|
// Error during authentication; report it
|
|
$error = $this->_errorMessage(
|
|
$objectEncoding,
|
|
'',
|
|
$e->getMessage(),
|
|
$e->getTraceAsString(),
|
|
$e->getCode(),
|
|
$e->getLine()
|
|
);
|
|
$responseType = Zend_AMF_Constants::STATUS_METHOD;
|
|
}
|
|
}
|
|
|
|
// Iterate through each of the service calls in the AMF request
|
|
foreach($request->getAmfBodies() as $body)
|
|
{
|
|
if ($error) {
|
|
// Error during authentication; just report it and be done
|
|
$responseURI = $body->getResponseURI() . $responseType;
|
|
$newBody = new Zend_Amf_Value_MessageBody($responseURI, null, $error);
|
|
$response->addAmfBody($newBody);
|
|
continue;
|
|
}
|
|
try {
|
|
switch ($objectEncoding) {
|
|
case Zend_Amf_Constants::AMF0_OBJECT_ENCODING:
|
|
// AMF0 Object Encoding
|
|
$targetURI = $body->getTargetURI();
|
|
$message = '';
|
|
|
|
// Split the target string into its values.
|
|
$source = substr($targetURI, 0, strrpos($targetURI, '.'));
|
|
|
|
if ($source) {
|
|
// Break off method name from namespace into source
|
|
$method = substr(strrchr($targetURI, '.'), 1);
|
|
$return = $this->_dispatch($method, $body->getData(), $source);
|
|
} else {
|
|
// Just have a method name.
|
|
$return = $this->_dispatch($targetURI, $body->getData());
|
|
}
|
|
break;
|
|
case Zend_Amf_Constants::AMF3_OBJECT_ENCODING:
|
|
default:
|
|
// AMF3 read message type
|
|
$message = $body->getData();
|
|
if ($message instanceof Zend_Amf_Value_Messaging_CommandMessage) {
|
|
// async call with command message
|
|
$return = $this->_loadCommandMessage($message);
|
|
} elseif ($message instanceof Zend_Amf_Value_Messaging_RemotingMessage) {
|
|
$return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
|
|
$return->body = $this->_dispatch($message->operation, $message->body, $message->source);
|
|
} else {
|
|
// Amf3 message sent with netConnection
|
|
$targetURI = $body->getTargetURI();
|
|
|
|
// Split the target string into its values.
|
|
$source = substr($targetURI, 0, strrpos($targetURI, '.'));
|
|
|
|
if ($source) {
|
|
// Break off method name from namespace into source
|
|
$method = substr(strrchr($targetURI, '.'), 1);
|
|
$return = $this->_dispatch($method, $body->getData(), $source);
|
|
} else {
|
|
// Just have a method name.
|
|
$return = $this->_dispatch($targetURI, $body->getData());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
$responseType = Zend_AMF_Constants::RESULT_METHOD;
|
|
} catch (Exception $e) {
|
|
$return = $this->_errorMessage($objectEncoding, $message,
|
|
$e->getMessage(), $e->getTraceAsString(),$e->getCode(), $e->getLine());
|
|
$responseType = Zend_AMF_Constants::STATUS_METHOD;
|
|
}
|
|
|
|
$responseURI = $body->getResponseURI() . $responseType;
|
|
$newBody = new Zend_Amf_Value_MessageBody($responseURI, null, $return);
|
|
$response->addAmfBody($newBody);
|
|
}
|
|
// Add a session header to the body if session is requested.
|
|
if($this->isSession()) {
|
|
$currentID = session_id();
|
|
$joint = "?";
|
|
if(isset($_SERVER['QUERY_STRING'])) {
|
|
if(!strpos($_SERVER['QUERY_STRING'], $currentID) !== FALSE) {
|
|
if(strrpos($_SERVER['QUERY_STRING'], "?") !== FALSE) {
|
|
$joint = "&";
|
|
}
|
|
}
|
|
}
|
|
|
|
// create a new AMF message header with the session id as a variable.
|
|
$sessionValue = $joint . $this->_sessionName . "=" . $currentID;
|
|
$sessionHeader = new Zend_Amf_Value_MessageHeader(Zend_Amf_Constants::URL_APPEND_HEADER, false, $sessionValue);
|
|
$response->addAmfHeader($sessionHeader);
|
|
}
|
|
|
|
// serialize the response and return serialized body.
|
|
$response->finalize();
|
|
}
|
|
|
|
/**
|
|
* Handle an AMF call from the gateway.
|
|
*
|
|
* @param null|Zend_Amf_Request $request Optional
|
|
* @return Zend_Amf_Response
|
|
*/
|
|
public function handle($request = null)
|
|
{
|
|
// Check if request was passed otherwise get it from the server
|
|
if ($request === null || !$request instanceof Zend_Amf_Request) {
|
|
$request = $this->getRequest();
|
|
} else {
|
|
$this->setRequest($request);
|
|
}
|
|
if ($this->isSession()) {
|
|
// Check if a session is being sent from the amf call
|
|
if (isset($_COOKIE[$this->_sessionName])) {
|
|
session_id($_COOKIE[$this->_sessionName]);
|
|
}
|
|
}
|
|
|
|
// Check for errors that may have happend in deserialization of Request.
|
|
try {
|
|
// Take converted PHP objects and handle service call.
|
|
// Serialize to Zend_Amf_response for output stream
|
|
$this->_handle($request);
|
|
$response = $this->getResponse();
|
|
} catch (Exception $e) {
|
|
// Handle any errors in the serialization and service calls.
|
|
throw new Zend_Amf_Server_Exception('Handle error: ' . $e->getMessage() . ' ' . $e->getLine(), 0, $e);
|
|
}
|
|
|
|
// Return the Amf serialized output string
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Set request object
|
|
*
|
|
* @param string|Zend_Amf_Request $request
|
|
* @return Zend_Amf_Server
|
|
*/
|
|
public function setRequest($request)
|
|
{
|
|
if (is_string($request) && class_exists($request)) {
|
|
$request = new $request();
|
|
if (!$request instanceof Zend_Amf_Request) {
|
|
throw new Zend_Amf_Server_Exception('Invalid request class');
|
|
}
|
|
} elseif (!$request instanceof Zend_Amf_Request) {
|
|
throw new Zend_Amf_Server_Exception('Invalid request object');
|
|
}
|
|
$this->_request = $request;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Return currently registered request object
|
|
*
|
|
* @return null|Zend_Amf_Request
|
|
*/
|
|
public function getRequest()
|
|
{
|
|
if (null === $this->_request) {
|
|
$this->setRequest(new Zend_Amf_Request_Http());
|
|
}
|
|
|
|
return $this->_request;
|
|
}
|
|
|
|
/**
|
|
* Public access method to private Zend_Amf_Server_Response reference
|
|
*
|
|
* @param string|Zend_Amf_Server_Response $response
|
|
* @return Zend_Amf_Server
|
|
*/
|
|
public function setResponse($response)
|
|
{
|
|
if (is_string($response) && class_exists($response)) {
|
|
$response = new $response();
|
|
if (!$response instanceof Zend_Amf_Response) {
|
|
throw new Zend_Amf_Server_Exception('Invalid response class');
|
|
}
|
|
} elseif (!$response instanceof Zend_Amf_Response) {
|
|
throw new Zend_Amf_Server_Exception('Invalid response object');
|
|
}
|
|
$this->_response = $response;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* get a reference to the Zend_Amf_response instance
|
|
*
|
|
* @return Zend_Amf_Server_Response
|
|
*/
|
|
public function getResponse()
|
|
{
|
|
if (null === ($response = $this->_response)) {
|
|
$this->setResponse(new Zend_Amf_Response_Http());
|
|
}
|
|
return $this->_response;
|
|
}
|
|
|
|
/**
|
|
* Attach a class or object to the server
|
|
*
|
|
* Class may be either a class name or an instantiated object. Reflection
|
|
* is done on the class or object to determine the available public
|
|
* methods, and each is attached to the server as and available method. If
|
|
* a $namespace has been provided, that namespace is used to prefix
|
|
* AMF service call.
|
|
*
|
|
* @param string|object $class
|
|
* @param string $namespace Optional
|
|
* @param mixed $arg Optional arguments to pass to a method
|
|
* @return Zend_Amf_Server
|
|
* @throws Zend_Amf_Server_Exception on invalid input
|
|
*/
|
|
public function setClass($class, $namespace = '', $argv = null)
|
|
{
|
|
if (is_string($class) && !class_exists($class)){
|
|
throw new Zend_Amf_Server_Exception('Invalid method or class');
|
|
} elseif (!is_string($class) && !is_object($class)) {
|
|
throw new Zend_Amf_Server_Exception('Invalid method or class; must be a classname or object');
|
|
}
|
|
|
|
$argv = null;
|
|
if (2 < func_num_args()) {
|
|
$argv = array_slice(func_get_args(), 2);
|
|
}
|
|
|
|
// Use the class name as the name space by default.
|
|
|
|
if ($namespace == '') {
|
|
$namespace = is_object($class) ? get_class($class) : $class;
|
|
}
|
|
|
|
$this->_classAllowed[is_object($class) ? get_class($class) : $class] = true;
|
|
|
|
$this->_methods[] = Zend_Server_Reflection::reflectClass($class, $argv, $namespace);
|
|
$this->_buildDispatchTable();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Attach a function to the server
|
|
*
|
|
* Additional arguments to pass to the function at dispatch may be passed;
|
|
* any arguments following the namespace will be aggregated and passed at
|
|
* dispatch time.
|
|
*
|
|
* @param string|array $function Valid callback
|
|
* @param string $namespace Optional namespace prefix
|
|
* @return Zend_Amf_Server
|
|
* @throws Zend_Amf_Server_Exception
|
|
*/
|
|
public function addFunction($function, $namespace = '')
|
|
{
|
|
if (!is_string($function) && !is_array($function)) {
|
|
throw new Zend_Amf_Server_Exception('Unable to attach function');
|
|
}
|
|
|
|
$argv = null;
|
|
if (2 < func_num_args()) {
|
|
$argv = array_slice(func_get_args(), 2);
|
|
}
|
|
|
|
$function = (array) $function;
|
|
foreach ($function as $func) {
|
|
if (!is_string($func) || !function_exists($func)) {
|
|
throw new Zend_Amf_Server_Exception('Unable to attach function');
|
|
}
|
|
$this->_methods[] = Zend_Server_Reflection::reflectFunction($func, $argv, $namespace);
|
|
}
|
|
|
|
$this->_buildDispatchTable();
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates an array of directories in which services can reside.
|
|
* TODO: add support for prefixes?
|
|
*
|
|
* @param string $dir
|
|
*/
|
|
public function addDirectory($dir)
|
|
{
|
|
$this->getLoader()->addPrefixPath("", $dir);
|
|
}
|
|
|
|
/**
|
|
* Returns an array of directories that can hold services.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getDirectory()
|
|
{
|
|
return $this->getLoader()->getPaths("");
|
|
}
|
|
|
|
/**
|
|
* (Re)Build the dispatch table
|
|
*
|
|
* The dispatch table consists of a an array of method name =>
|
|
* Zend_Server_Reflection_Function_Abstract pairs
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function _buildDispatchTable()
|
|
{
|
|
$table = array();
|
|
foreach ($this->_methods as $key => $dispatchable) {
|
|
if ($dispatchable instanceof Zend_Server_Reflection_Function_Abstract) {
|
|
$ns = $dispatchable->getNamespace();
|
|
$name = $dispatchable->getName();
|
|
$name = empty($ns) ? $name : $ns . '.' . $name;
|
|
|
|
if (isset($table[$name])) {
|
|
throw new Zend_Amf_Server_Exception('Duplicate method registered: ' . $name);
|
|
}
|
|
$table[$name] = $dispatchable;
|
|
continue;
|
|
}
|
|
|
|
if ($dispatchable instanceof Zend_Server_Reflection_Class) {
|
|
foreach ($dispatchable->getMethods() as $method) {
|
|
$ns = $method->getNamespace();
|
|
$name = $method->getName();
|
|
$name = empty($ns) ? $name : $ns . '.' . $name;
|
|
|
|
if (isset($table[$name])) {
|
|
throw new Zend_Amf_Server_Exception('Duplicate method registered: ' . $name);
|
|
}
|
|
$table[$name] = $method;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
$this->_table = $table;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Raise a server fault
|
|
*
|
|
* Unimplemented
|
|
*
|
|
* @param string|Exception $fault
|
|
* @return void
|
|
*/
|
|
public function fault($fault = null, $code = 404)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Returns a list of registered methods
|
|
*
|
|
* Returns an array of dispatchables (Zend_Server_Reflection_Function,
|
|
* _Method, and _Class items).
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getFunctions()
|
|
{
|
|
return $this->_table;
|
|
}
|
|
|
|
/**
|
|
* Set server persistence
|
|
*
|
|
* Unimplemented
|
|
*
|
|
* @param mixed $mode
|
|
* @return void
|
|
*/
|
|
public function setPersistence($mode)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Load server definition
|
|
*
|
|
* Unimplemented
|
|
*
|
|
* @param array $definition
|
|
* @return void
|
|
*/
|
|
public function loadFunctions($definition)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Map ActionScript classes to PHP classes
|
|
*
|
|
* @param string $asClass
|
|
* @param string $phpClass
|
|
* @return Zend_Amf_Server
|
|
*/
|
|
public function setClassMap($asClass, $phpClass)
|
|
{
|
|
Zend_Amf_Parse_TypeLoader::setMapping($asClass, $phpClass);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* List all available methods
|
|
*
|
|
* Returns an array of method names.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function listMethods()
|
|
{
|
|
return array_keys($this->_table);
|
|
}
|
|
|
|
/**
|
|
* Cast parameters
|
|
*
|
|
* Takes the provided parameters from the request, and attempts to cast them
|
|
* to objects, if the prototype defines any as explicit object types
|
|
*
|
|
* @param Reflection $reflectionMethod
|
|
* @param array $params
|
|
* @return array
|
|
*/
|
|
protected function _castParameters($reflectionMethod, array $params)
|
|
{
|
|
$prototypes = $reflectionMethod->getPrototypes();
|
|
$nonObjectTypes = array(
|
|
'null',
|
|
'mixed',
|
|
'void',
|
|
'unknown',
|
|
'bool',
|
|
'boolean',
|
|
'number',
|
|
'int',
|
|
'integer',
|
|
'double',
|
|
'float',
|
|
'string',
|
|
'array',
|
|
'object',
|
|
'stdclass',
|
|
);
|
|
$types = array();
|
|
foreach ($prototypes as $prototype) {
|
|
foreach ($prototype->getParameters() as $parameter) {
|
|
$type = $parameter->getType();
|
|
if (in_array(strtolower($type), $nonObjectTypes)) {
|
|
continue;
|
|
}
|
|
$position = $parameter->getPosition();
|
|
$types[$position] = $type;
|
|
}
|
|
}
|
|
|
|
if (empty($types)) {
|
|
return $params;
|
|
}
|
|
|
|
foreach ($params as $position => $value) {
|
|
if (!isset($types[$position])) {
|
|
// No specific type to cast to? done
|
|
continue;
|
|
}
|
|
|
|
$type = $types[$position];
|
|
|
|
if (!class_exists($type)) {
|
|
// Not a class, apparently. done
|
|
continue;
|
|
}
|
|
|
|
if ($value instanceof $type) {
|
|
// Already of the right type? done
|
|
continue;
|
|
}
|
|
|
|
if (!is_array($value) && !is_object($value)) {
|
|
// Can't cast scalars to objects easily; done
|
|
continue;
|
|
}
|
|
|
|
// Create instance, and loop through value to set
|
|
$object = new $type;
|
|
foreach ($value as $property => $defined) {
|
|
$object->{$property} = $defined;
|
|
}
|
|
|
|
$params[$position] = $object;
|
|
}
|
|
|
|
return $params;
|
|
}
|
|
}
|