Location: PHPKode > projects > Recess PHP Framework > recess/recess/lang/WrappedMethod.class.php
<?php
Library::import('recess.lang.IWrapper');
Library::import('recess.lang.reflection.ReflectionMethod');

/**
 * WrappedMethod is used as an attached method provider on a Recess Object.
 * WrappedMethod provides an additional level of indirection prior to
 * invoking a method on an Object that allows classes implementing
 * IWrapper to register callbacks before() and after(). before() callbacks
 * are able to modify the arguments being passed to a method, and
 * can short-circuit a return value. after() callbacks can modify the
 * return value. Shares similarities to the aspect oriented notion of join
 * points.
 * 
 * @author Kris Jordan <hide@address.com>
 * @copyright 2008, 2009 Kris Jordan
 * @package Recess PHP Framework
 * @license MIT
 * @link http://www.recessframework.org/
 */
class WrappedMethod {
	
	const CALL = 'call';
	
	/**
	 * Registered wrappers whose callbacks are invoked during call()
	 * @var array(IWrapper)
	 */
	protected $wrappers = array();
	
	/**
	 * Name of the method to invoke on the object passed to call()
	 * @var string
	 */
	public $method;
	
	/**
	 * The constructor takes an optional method name.
	 * 
	 * @param String $method name of the method to invoke
	 */
	function __construct($method = '') {
		$this->method = $method;
	}
	
	/**
	 * Wrap the method with another IWrapper
	 * 
	 * @param IWrapper $wrapper
	 */
	function addWrapper(IWrapper $wrapper) {
		foreach($this->wrappers as $existingWrapper) {
			if($existingWrapper->combine($wrapper)) {
				return;
			}
		}
		$this->wrappers[] = $wrapper;
	}
	
	/**
	 * Assume takes the wrappers from another WrappedMethod
	 * and makes them their own. Assume is necessary because
	 * the actual WrappedMethod in a ClassDescriptor may not exist
	 * prior to it needing to be wrapped (by a Class-level annotation)
	 * so we create a place-holder WrappedMethod until the actual
	 * wrapped method Assumes its rightful place.
	 * 
	 * @param WrappedMethod $wrappedMethod the place-holder WrappedMethod.
	 * @return WrappedMethod
	 */
	function assume(WrappedMethod $wrappedMethod) {
		$this->wrappers = $wrappedMethod->wrappers;
		return $this;
	}
	
	/**
	 * The wrappers and wrapped method are invoked in call().
	 * First the before() methods of wrappers are invoked in the order
	 * in which the Wrappers were applied. The before methods are passed
	 * the arguments which will eventually be passed to the actual wrapped
	 * method by reference for possible manipulation. If a before method
	 * returns a value this value is short-circuits the calling process
	 * and returns that value immediately. Next the wrapped method is
	 * invoked. The result is then passed to the after() methods of wrappers
	 * in the reverse order in which they were applied. The after methods
	 * can manipulate the returned value.
	 * 
	 * @return mixed
	 */
	function call() {
		$args = func_get_args();
		
		$object = array_shift($args);
		
		foreach($this->wrappers as $wrapper) {
			$returns = $wrapper->before($object, $args);
			if($returns !== null) { 
				// Short-circuit return
				return $returns;
			}
		}
		
		if(!isset($this->reflectedMethod)) {
			$this->reflectedMethod = new ReflectionMethod($object, $this->method);
		}
		
		$returns = $this->reflectedMethod->invokeArgs($object, $args);
		
		foreach(array_reverse($this->wrappers) as $wrapper) {
			$wrapperReturn = $wrapper->after($object, $returns);
			if($wrapperReturn !== null) {
				$returns = $wrapperReturn;
			}
		}
		
		return $returns;
	}
}
?>
Return current item: Recess PHP Framework