One of the major deficiencies in the development of many projects is that there is no roadmap or strategy available other than in the developers' heads. The XP team publishes its decisions by documenting change requests in form of RFCs.
<?php
class Account extends Object {
public function transfer($amount) {
// TBI
}
}
class SecurityInterceptor extends Object implements InvocationInterceptor {
protected $level;
public function __construct($level) {
$this->level= $level;
}
public function invoke(InvocationChain $chain) {
if (
'transfer' == $chain->method->getName() &&
$chain->parameters[0] > 10000 &&
$this->level < 4
) {
throw new IllegalAccessException(sprintf(
'Clearance level IV needed to transfer %d EUR',
$chain->parameters[0]
);
return $chain->proceed();
}
}
}
$account= new Account();
// Use invocation chain to invoke transfer() instead of calling it directly
$chain= new InvocationChain();
$chain->addInterceptor(new SecurityInterceptor(3));
// Will throw an exception because of unmet prerequisites
$result= $chain->invoke(
$account,
$account->getClass()->getMethod('transfer'),
array(120000)
);
?>
Example: Logging:
<?php
class LoggingInterceptor extends Object implements InvocationInterceptor {
protected $cat;
public function __construct($cat) {
$this->cat= $cat;
}
public function invoke(InvocationChain $chain) {
$this->cat->debug('Invoking', $chain->method, '(', $chain->parameters, ')');
try {
$result= $chain->proceed();
} catch (Throwable $e) {
$this->cat->warn($e);
throw $e;
}
$this->cat->info('Result', $result);
return $result;
}
}
?>
Lazy initialization
The util.AbstractDeferredInvokationHandler class will be moved to invoke
and be renamed to AbstractDeferredInvocationHandler (invocation is spelled with
a "c", while invoke is spelled with a "k").
Caller
Finding out a method's caller:
<?php
class Account extends Object {
public function deposit($amount) {
$this->cat->debug('Called from', InvocationContext::getCaller());
// TBI
}
public function withdraw($amount) {
$this->cat->debug('Called from', InvocationContext::getCaller());
// TBI
}
}
class MonetaryTransaction extends Object {
public function transfer(Account $origin, Account $destination, $amount) {
$origin->withdraw($amount);
$destination->deposit($amount);
}
}
$t= new MonetaryTransaction();
$t->transfer(Account::getByAccountNumber(1), Account::getByAccountNumber(2), 100);
?>
Scope of Change
A new package invoke will be added.
Rationale
Method invocation utilities.
Functionality
Invocation chain
Example: Prerequisites-check:
Example: Logging:
Lazy initialization
The util.AbstractDeferredInvokationHandler class will be moved to invoke and be renamed to AbstractDeferredInvocationHandler (invocation is spelled with a "c", while invoke is spelled with a "k").
Caller
Finding out a method's caller:
This will print somthing like:
Another example of using the invocation class:
Invocation comparison
Instead of manually comparing class name, method and parameter counts in interceptors, one can make use of the PointCutExpression class.
Syntax that can be used in forName():
Where:
Security considerations
n/a
Speed impact
n/a
Dependencies
n/a
Related documents