Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding Before and After annotations. Modified interface to IWrapper.
- Loading branch information
1 parent
3365b10
commit 19fafc5
Showing
7 changed files
with
263 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
Library::import('recess.lang.Annotation'); | ||
Library::import('recess.lang.MethodCallWrapper'); | ||
|
||
/** | ||
* The After annotation can be used as a shortcut for registering | ||
* methods that should be called after a wrapped method. It is placed | ||
* on the method to be called and referencess the wrappable method. | ||
* | ||
* For example, on a subclass of Model: | ||
* /** !After create, insert *_/ | ||
* function log($success) { echo $success ? "Success!" : "Fail!"; } | ||
* | ||
* There are a couple of nuances worth noting: | ||
* | ||
* -Your after method must take one argument that is the value returned | ||
* the wrapped method. | ||
* -Your method should return a value of the same type as expected | ||
* to be returned by the wrapped method. Returning null, or not returning, | ||
* has the same effect as returning the value returned by the wrapped | ||
* method. | ||
* | ||
* @author Kris Jordan | ||
* @since 0.20 | ||
*/ | ||
class AfterAnnotation extends Annotation { | ||
|
||
/** | ||
* Returns a string representation of the intended usage of an annotation. | ||
* | ||
* @return string | ||
*/ | ||
public function usage() { | ||
return '!After wrappableMethod[, wrappableMethod]*'; | ||
} | ||
|
||
/** | ||
* After is only used on methods. | ||
* | ||
* @return integer | ||
*/ | ||
public function isFor() { | ||
return Annotation::FOR_METHOD; | ||
} | ||
|
||
/** | ||
* Validates some constraints of the annotation's specification. | ||
* | ||
* @param $class The classname the annotation is on. | ||
*/ | ||
protected function validate($class) { | ||
$this->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); | ||
} | ||
} | ||
} | ||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
Library::import('recess.lang.Annotation'); | ||
Library::import('recess.lang.MethodCallWrapper'); | ||
|
||
/** | ||
* The Before annotation can be used as a shortcut for registering | ||
* methods that should be called before a wrapped method. It is placed | ||
* on the method to be called and referencess the wrappable method. | ||
* | ||
* For example, on a subclass of Model: | ||
* /** !Before create, insert *_/ | ||
* function validate() { echo "validate"; } | ||
* | ||
* There are a couple of nuances worth noting: | ||
* | ||
* -Your Before method must take the same arguments as the wrapped method. | ||
* -If your method returns a non-null value it will short-circuit the call | ||
* and immediately return your returned value without calling the wrapped | ||
* method. | ||
* | ||
* @author Kris Jordan | ||
* @since 0.20 | ||
*/ | ||
class BeforeAnnotation extends Annotation { | ||
|
||
/** | ||
* Returns a string representation of the intended usage of an annotation. | ||
* | ||
* @return string | ||
*/ | ||
public function usage() { | ||
return '!Before wrappableMethod[, wrappableMethod]*'; | ||
} | ||
|
||
/** | ||
* Before is only used on methods. | ||
* | ||
* @return integer | ||
*/ | ||
public function isFor() { | ||
return Annotation::FOR_METHOD; | ||
} | ||
|
||
/** | ||
* Validates some constraints of the annotation's specification. | ||
* | ||
* @param $class The classname the annotation is on. | ||
*/ | ||
protected function validate($class) { | ||
$this->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); | ||
} | ||
} | ||
} | ||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
<?php | ||
Library::import('recess.lang.IWrapper'); | ||
|
||
/** | ||
* A Wrapper that will call methods on a class before or after calling the | ||
* wrapped method. Used by Before and After annotations. | ||
* | ||
* @author Kris Jordan | ||
* @since 0.20 | ||
*/ | ||
class MethodCallWrapper implements IWrapper { | ||
protected $callBefore = array(); | ||
protected $callAfter = array(); | ||
|
||
/** | ||
* Add methods to be called before the wrapped method. | ||
* Methods must have same argument signature as the wrapped method. | ||
* If a non-null value is returned from any method called | ||
* before the wrapped method its return value will short circuit | ||
* the call and return immediately without calling the method. | ||
*/ | ||
public function addCallBefore() { | ||
$args = func_get_args(); | ||
foreach($args as $method) { | ||
if(is_string($method)){ | ||
$this->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; | ||
} | ||
} | ||
} | ||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters