Zend Framework

Integrating Zend_Amf in MVC

Details

  • Type: Docs:  Improvement Docs: Improvement
  • Status: Open Open
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: Zend_Amf
  • Labels:
    None
  • Language:
    English
  • Tags:

Description

An example controller for integrating in MVC:

require_once 'Zend/Amf/Server.php';
require_once 'Zend/Controller/Action.php';
class Services_AmfController extends Zend_Controller_Action
{
	/**
	 * @var Zend_Amf_Server
	 */
	protected $_server;
	
	public function init ()
	{
		//	set the server up 
		$this->_server = new Zend_Amf_Server();
		$this->_server->addDirectory(dirname(dirname(__FILE__)) . '/services/');
		
		//	turn the renderer off
		$this->_helper->viewRenderer->setNoRender();
	}
	
	public function postDispatch ()
	{
		//	shut the layout off
		$this->_helper->layout->disableLayout();
		//	set the correct header if no error or forwarding occurred during dispatch  
		$this->_response->setHeader('Content-Type', 'application/x-amf', true);
	}

	public function indexAction ()
	{
		//	process the request and set the response
		$amfResponse = $this->_server->handle();
		$this->_response->setBody($amfResponse);
	}

}

Activity

Hide
Matthias Steinböck added a comment -

concerning the $amfResponse variable you are using: do you want to give it an AMF-Byte-String or the object that should be serialized?

For integration in MVC you already have different possibilities:

1. Register $this in the extended ActionController-Class (in your case "Services AmfController") as the handling class and let it call its methods:

// in your library-folder:
class YourNS_Controller_Action_AMF extends Zend_Controller_Action
{
    protected $_server;

    public function init() {
        $this->_server = new Zend_Amf_Server();
        $this->_server->setClass($this);
        $this->_helper->viewRenderer->setNoRender(); 
        echo $this->_server->handle();
        exit;
    }
}

// in your application/controllers folder
class MyController extends YourNS_Controller_Action_AMF
{
    public function funcToBeCalledByFlash($a, $b) {
        return $a+$b;
    }
}

this has the downside, that any public function Zend_Action_Controller has can be called by AMF.

2. create the request yourself and change the target to handle this yourself (and allow only methods you want it to allow):

class YourNS_Controller_Action_AMF extends Zend_Controller_Action
{
    private $_server;
    private $_forbiddenMethods = array();
    private static $_originalTargetMethods = array();

    public function init () {
        $this->_server = new Zend_Amf_Server();
        $this->_server->setClass($this);
        // make a reflection of this class (YourNS_Controller_Action_AMF) and get all methods of it, so we can forbid them
        $this->_forbiddenMethods = array(); // TODO

        $this->_request = new Zend_AMF_Request_Http();

        // iterate over the requests bodies
        $responseBody = $this->_request->getAmfBodies();

        foreach($responseBody as $body) {
            // on each $body get its targetURI (dont forget about AMF0/AMF3-switching)
            // with $body->getTargetURI()
            $method = '...TODO:get the correct method out of target URI...';
            if (in_array($method, $this->_forbiddenMethods))
                throw new Zend_Amf_Server_Exception('Method "' . $method . '" must not be called');

            self::$_originalTargetMethods[] = $method;

            // now set the target URI to our own function
            // also concern 0/3 switch here
            $body->setTargetURI('TODO:getthecorrectleading'.'handlerFunction');
        }

        // now handle it
        echo $this->_server->handle();
        exit;
    }

    public function handlerFunction() {
        $method = array_shift(self::$_originalTargetMethods);
        // TODO: make a reflection of $this and call $method on it with this functions arguments
        // TODO: now you can look inside the response created in your function
        return $this->_response->getBody();
    }
}

Is this what you intended or did i understand you wrong?

Show
Matthias Steinböck added a comment - concerning the $amfResponse variable you are using: do you want to give it an AMF-Byte-String or the object that should be serialized? For integration in MVC you already have different possibilities: 1. Register $this in the extended ActionController-Class (in your case "Services AmfController") as the handling class and let it call its methods:
// in your library-folder:
class YourNS_Controller_Action_AMF extends Zend_Controller_Action
{
    protected $_server;

    public function init() {
        $this->_server = new Zend_Amf_Server();
        $this->_server->setClass($this);
        $this->_helper->viewRenderer->setNoRender(); 
        echo $this->_server->handle();
        exit;
    }
}

// in your application/controllers folder
class MyController extends YourNS_Controller_Action_AMF
{
    public function funcToBeCalledByFlash($a, $b) {
        return $a+$b;
    }
}
this has the downside, that any public function Zend_Action_Controller has can be called by AMF. 2. create the request yourself and change the target to handle this yourself (and allow only methods you want it to allow):
class YourNS_Controller_Action_AMF extends Zend_Controller_Action
{
    private $_server;
    private $_forbiddenMethods = array();
    private static $_originalTargetMethods = array();

    public function init () {
        $this->_server = new Zend_Amf_Server();
        $this->_server->setClass($this);
        // make a reflection of this class (YourNS_Controller_Action_AMF) and get all methods of it, so we can forbid them
        $this->_forbiddenMethods = array(); // TODO

        $this->_request = new Zend_AMF_Request_Http();

        // iterate over the requests bodies
        $responseBody = $this->_request->getAmfBodies();

        foreach($responseBody as $body) {
            // on each $body get its targetURI (dont forget about AMF0/AMF3-switching)
            // with $body->getTargetURI()
            $method = '...TODO:get the correct method out of target URI...';
            if (in_array($method, $this->_forbiddenMethods))
                throw new Zend_Amf_Server_Exception('Method "' . $method . '" must not be called');

            self::$_originalTargetMethods[] = $method;

            // now set the target URI to our own function
            // also concern 0/3 switch here
            $body->setTargetURI('TODO:getthecorrectleading'.'handlerFunction');
        }

        // now handle it
        echo $this->_server->handle();
        exit;
    }

    public function handlerFunction() {
        $method = array_shift(self::$_originalTargetMethods);
        // TODO: make a reflection of $this and call $method on it with this functions arguments
        // TODO: now you can look inside the response created in your function
        return $this->_response->getBody();
    }
}
Is this what you intended or did i understand you wrong?
Hide
Nikita Y Volkov added a comment -

That was the solution what I posted, not an issue. The only purpose of it is to integrate Zend_Amf server into your MVC application instead of separating it to a different one, so that you could use your controller plugins and leave a single entry-point making the strucure of your application more understandable and stable.

This is a single controller with single action which serves as an entry-point for amf server. And the only thing that could be changed in it throughout different applications is mapping of classes. That's it. All the rest is done by Zend_Amf calling services you make.

On your proposal. I think this is an example of what is called over-programming. Looks like a solution of inexistent problem. As I understood all its intensions conclude in turning your controller into a service. This triggers a list of questions, but I'll skip to the main one: what in hell could make you need that?

Show
Nikita Y Volkov added a comment - That was the solution what I posted, not an issue. The only purpose of it is to integrate Zend_Amf server into your MVC application instead of separating it to a different one, so that you could use your controller plugins and leave a single entry-point making the strucure of your application more understandable and stable. This is a single controller with single action which serves as an entry-point for amf server. And the only thing that could be changed in it throughout different applications is mapping of classes. That's it. All the rest is done by Zend_Amf calling services you make. On your proposal. I think this is an example of what is called over-programming. Looks like a solution of inexistent problem. As I understood all its intensions conclude in turning your controller into a service. This triggers a list of questions, but I'll skip to the main one: what in hell could make you need that?
Hide
Matthias Steinböck added a comment -

sorry, i totally missed the point in your first post - thought you search for a solution - should read things better before commenting on them..... sry again. i'm new on zf issues and didn't saw that you made this post a doc improvement.

and yes - you're totally right - why the hell would i like to do so? in a current project i'm working on we use Zend_AMF exactly the way you described it - i posted the second solution only because i thought you're trying to do so - so once again: sorry.

in our project we use the following code - i hope this corresponds with your proposal (what dou you think about that?):

public function indexAction()
	{
		try {
			Zend_Registry::get('logger')->log('creating amf server', Zend_Log::DEBUG);
			$server = new Zend_Amf_Server();
			$server->setProduction(false);
			
			$classes = glob(APPLICATION_PATH.'/models/*.php');
			foreach ($classes as $class) {
				$class = basename($class);
				$class = substr($class, 0, strlen($class) - 4);
				$server->setClassMap($class, $class);
			}
			
			$fileName = basename(
				$this->getRequest()->getParam('service', 'Default').
				'Service.php'
			);
			
			// check if service really exists
			if (!file_exists(APPLICATION_PATH.'/services/OurNs/'.$fileName)) {
				throw new Zend_Amf_Exception('Service '.
					$this->getRequest()->getParam('service', 'Default').
					' not found!');
			}
			$server->setClass('OurNs_'.
				$this->getRequest()->getParam('service', 'Default').
				'Service');
			
			$response = $server->handle();
			Zend_Registry::get('logger')->log('responding', Zend_Log::DEBUG);
			echo $response;
		} catch (Exception $e) {
			Zend_Registry::get('logger')->log('Error handling AMF-Request: '.
				$e->getMessage()."\n".$e->getTraceAsString(), Zend_Log::ERR);
		}
		exit;
	}
Show
Matthias Steinböck added a comment - sorry, i totally missed the point in your first post - thought you search for a solution - should read things better before commenting on them..... sry again. i'm new on zf issues and didn't saw that you made this post a doc improvement. and yes - you're totally right - why the hell would i like to do so? in a current project i'm working on we use Zend_AMF exactly the way you described it - i posted the second solution only because i thought you're trying to do so - so once again: sorry. in our project we use the following code - i hope this corresponds with your proposal (what dou you think about that?):
public function indexAction()
	{
		try {
			Zend_Registry::get('logger')->log('creating amf server', Zend_Log::DEBUG);
			$server = new Zend_Amf_Server();
			$server->setProduction(false);
			
			$classes = glob(APPLICATION_PATH.'/models/*.php');
			foreach ($classes as $class) {
				$class = basename($class);
				$class = substr($class, 0, strlen($class) - 4);
				$server->setClassMap($class, $class);
			}
			
			$fileName = basename(
				$this->getRequest()->getParam('service', 'Default').
				'Service.php'
			);
			
			// check if service really exists
			if (!file_exists(APPLICATION_PATH.'/services/OurNs/'.$fileName)) {
				throw new Zend_Amf_Exception('Service '.
					$this->getRequest()->getParam('service', 'Default').
					' not found!');
			}
			$server->setClass('OurNs_'.
				$this->getRequest()->getParam('service', 'Default').
				'Service');
			
			$response = $server->handle();
			Zend_Registry::get('logger')->log('responding', Zend_Log::DEBUG);
			echo $response;
		} catch (Exception $e) {
			Zend_Registry::get('logger')->log('Error handling AMF-Request: '.
				$e->getMessage()."\n".$e->getTraceAsString(), Zend_Log::ERR);
		}
		exit;
	}
Hide
Nikita Y Volkov added a comment -

No problem.

On your question. Yes it kinda does the same thing, but doesn't correctly realize Zend MVC, though in most general cases you won't feel the difference.

You break two secret rules of Zend MVC:
1. Never echo anything - you have a response object for that.
2. Never exit or die the process - thus the postDispatch and other dispatch-cycles of the front controller get ignored.

Show
Nikita Y Volkov added a comment - No problem. On your question. Yes it kinda does the same thing, but doesn't correctly realize Zend MVC, though in most general cases you won't feel the difference. You break two secret rules of Zend MVC: 1. Never echo anything - you have a response object for that. 2. Never exit or die the process - thus the postDispatch and other dispatch-cycles of the front controller get ignored.
Hide
Matthias Steinböck added a comment -

Thanks a lot for your comments!

Show
Matthias Steinböck added a comment - Thanks a lot for your comments!

People

Vote (0)
Watch (2)

Dates

  • Created:
    Updated: