From 19fafc5727406fb859f53ee827a6c2dc9a202301 Mon Sep 17 00:00:00 2001 From: Kris Jordan Date: Tue, 2 Jun 2009 11:54:41 -0400 Subject: [PATCH] Adding Before and After annotations. Modified interface to IWrapper. --- recess/recess/lang/AfterAnnotation.class.php | 75 +++++++++++++ recess/recess/lang/BeforeAnnotation.class.php | 73 ++++++++++++ recess/recess/lang/IWrapper.class.php | 3 +- .../recess/lang/MethodCallWrapper.class.php | 106 ++++++++++++++++++ recess/recess/lang/Object.class.php | 2 + recess/recess/lang/WrappedMethod.class.php | 2 +- .../test/recess/lang/FrameworkObjectTest.php | 8 +- 7 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 recess/recess/lang/AfterAnnotation.class.php create mode 100644 recess/recess/lang/BeforeAnnotation.class.php create mode 100644 recess/recess/lang/MethodCallWrapper.class.php diff --git a/recess/recess/lang/AfterAnnotation.class.php b/recess/recess/lang/AfterAnnotation.class.php new file mode 100644 index 0000000..01bea71 --- /dev/null +++ b/recess/recess/lang/AfterAnnotation.class.php @@ -0,0 +1,75 @@ +minimumParameterCount(1); + $this->acceptsNoKeyedValues(); + } + + /** + * The expansion step of After creates a MethodCallWrapper + * for each wrappableMethod in the methods listed in the Annotation's + * parameters. + * + * @param string $class Classname the annotation is applied to. + * @param mixed $reflection The Reflection(Class|Method|Property) object the annotation is applied to. + * @param ClassDescriptor $descriptor The ClassDescriptor being manipulated. + */ + protected function expand($class, $reflection, $descriptor) { + $methodName = $reflection->getName(); + + foreach($this->values as $wrappableMethod) { + $wrapper = new MethodCallWrapper(); + $wrapper->addCallAfter($methodName); + $descriptor->addWrapper($wrappableMethod, $wrapper); + } + } +} +?> \ No newline at end of file diff --git a/recess/recess/lang/BeforeAnnotation.class.php b/recess/recess/lang/BeforeAnnotation.class.php new file mode 100644 index 0000000..0d7bbbd --- /dev/null +++ b/recess/recess/lang/BeforeAnnotation.class.php @@ -0,0 +1,73 @@ +minimumParameterCount(1); + $this->acceptsNoKeyedValues(); + } + + /** + * The expansion step of Before creates a MethodCallWrapper + * for each wrappableMethod in the methods listed in the Annotation's + * parameters. + * + * @param string $class Classname the annotation is applied to. + * @param mixed $reflection The Reflection(Class|Method|Property) object the annotation is applied to. + * @param ClassDescriptor $descriptor The ClassDescriptor being manipulated. + */ + protected function expand($class, $reflection, $descriptor) { + $methodName = $reflection->getName(); + + foreach($this->values as $wrappableMethod) { + $wrapper = new MethodCallWrapper(); + $wrapper->addCallBefore($methodName); + $descriptor->addWrapper($wrappableMethod, $wrapper); + } + } +} +?> \ No newline at end of file diff --git a/recess/recess/lang/IWrapper.class.php b/recess/recess/lang/IWrapper.class.php index 5716654..ffc08e8 100644 --- a/recess/recess/lang/IWrapper.class.php +++ b/recess/recess/lang/IWrapper.class.php @@ -27,10 +27,11 @@ function before($object, &$args); * after is non-null then after's return value will override the return value * of the wrapped method. * + * @param $object * @param $returnValue * @return mixed Should be of the same type as the wrapped method returns. */ - function after($returnValue); + function after($object, $returnValue); /** * Attempt to combine a wrapper with another. Example usage: diff --git a/recess/recess/lang/MethodCallWrapper.class.php b/recess/recess/lang/MethodCallWrapper.class.php new file mode 100644 index 0000000..b379ee6 --- /dev/null +++ b/recess/recess/lang/MethodCallWrapper.class.php @@ -0,0 +1,106 @@ +callBefore[] = $method; + } + } + } + + /** + * Add methods to be called after the wrapped method has returned. + * Methods called after must take a single argument that is the + * return value of the called method. Methods called after are expected + * to return the value returned by the wrapped method unless it + * needs to be massaged in some way. + */ + public function addCallAfter() { + $args = func_get_args(); + foreach($args as $method) { + if(is_string($method)){ + $this->callAfter[] = $method; + } + } + } + + /** + * Call all other methods on this class registered to be called before + * the wrapped method. + * + * @param $object + * @param $args + * @return mixed If a value is returned during a call to before that value short-circuits the method call. + */ + function before($object, &$args) { + $reflectedClass = new RecessReflectionClass($object); + foreach($this->callBefore as $method) { + $reflectedMethod = $reflectedClass->getMethod($method); + $result = $reflectedMethod->invokeArgs($object, $args); + if($result !== null) { + return $result; + } + } + // Return null so that method call proceeds as expected + return null; + } + + /** + * Call all other methods on this class registered to be called after + * the wrapped method has returned. + * + * @param $object + * @param $returnValue + * @return mixed Should be of the same type as the wrapped method returns. + */ + function after($object, $returnValue) { + $reflectedClass = new RecessReflectionClass($object); + foreach($this->callAfter as $method) { + $reflectedMethod = $reflectedClass->getMethod($method); + $result = $reflectedMethod->invoke($object, $returnValue); + if($result !== null) { + $returnValue = $result; + } + } + return $returnValue; + } + + /** + * Attempt to combine a wrapper with another. Example usage: + * Required wrappers declared on multiple fields of a model + * can be combined into a single wrapper. + * + * @param IWrapper $wrapper + * @return true on success, false on failure + */ + function combine(IWrapper $that) { + if($that instanceof MethodCallWrapper) { + $this->callBefore = array_merge($this->callBefore, $that->callBefore); + $this->callAfter = array_merge($this->callAfter, $that->callAfter); + return true; + } else { + return false; + } + } +} +?> \ No newline at end of file diff --git a/recess/recess/lang/Object.class.php b/recess/recess/lang/Object.class.php index bbcb7d7..f8ec084 100644 --- a/recess/recess/lang/Object.class.php +++ b/recess/recess/lang/Object.class.php @@ -2,6 +2,8 @@ Library::import('recess.lang.ClassDescriptor'); Library::import('recess.lang.AttachedMethod'); Library::import('recess.lang.WrappableAnnotation'); +Library::import('recess.lang.BeforeAnnotation'); +Library::import('recess.lang.AfterAnnotation'); /** * Object is the base class for extensible classes in the Recess. diff --git a/recess/recess/lang/WrappedMethod.class.php b/recess/recess/lang/WrappedMethod.class.php index 3bc582f..90bc0dd 100644 --- a/recess/recess/lang/WrappedMethod.class.php +++ b/recess/recess/lang/WrappedMethod.class.php @@ -107,7 +107,7 @@ function call() { $returns = $this->reflectedMethod->invokeArgs($object, $args); foreach(array_reverse($this->wrappers) as $wrapper) { - $wrapperReturn = $wrapper->after($returns); + $wrapperReturn = $wrapper->after($object, $returns); if($wrapperReturn != null) { $returns = $wrapperReturn; } diff --git a/recess/test/recess/lang/FrameworkObjectTest.php b/recess/test/recess/lang/FrameworkObjectTest.php index 919793b..d990242 100644 --- a/recess/test/recess/lang/FrameworkObjectTest.php +++ b/recess/test/recess/lang/FrameworkObjectTest.php @@ -38,9 +38,9 @@ function shapeDescriptor($class, $reflection, $descriptor) { function combine(IWrapper $requiredWrapper) {} - function before(&$args) {} + function before($object, &$args) {} - function after($result) { return $result + 1; } + function after($object, $result) { return $result + 1; } } class ConcatAnnotation extends Annotation implements IWrapper { @@ -60,9 +60,9 @@ function shapeDescriptor($class, $reflection, $descriptor) { function combine(IWrapper $requiredWrapper) {} - function before(&$args) { $args[1] = $this->prepend . $args[1]; } + function before($object, &$args) { $args[1] = $this->prepend . $args[1]; } - function after($result) { return $result . $this->append; } + function after($object, $result) { return $result . $this->append; } } /**