PageRenderTime 39ms CodeModel.GetById 19ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/zf/library/Zend/Amf/Server.php

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