thekid/rfc — Issue #109: RFC 0109: Invocation utilities package
Author: thekid
Created: 2011-10-09 15:09:19+00:00
State: open
Labels: rfc

Scope of Change
====
A new package invoke will be added.

Rationale
====
Method invocation utilities.

Functionality
====

Invocation chain
----
Example: Prerequisites-check:

```php
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
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
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);
?>
```

This will print somthing like:

```
[15:55:19 29540 debug] Called from invoke.Invocation@{
[instance ] com.example.banking.MonetaryTransaction{}
[class ] lang.XPClass
[method ] public transfer($origin, $destination, $amount)
[arguments ] [
0 => com.example.banking.Account<#1>
1 => com.example.banking.Account<#2>
2 => 100
]
}

```

Another example of using the invocation class:

```php
getClass()->getMethod('toString')->invoke($instance);

$i= new Invocation($instance, 'toString');
$repr= $i->invoke();
?>
```

Invocation comparison
----
Instead of manually comparing class name, method and parameter counts in
interceptors, one can make use of the PointCutExpression class.

```php
instance->getClassName() &&
'transfer' == $chain->method->getName() &&
1 == sizeof($chain->parameters) &&
$chain->parameters[0] > 10000 &&
$this->level < 4
) {
// ...
}

$pc= new PointCutExpression('call:com.example.banking.Account::transfer(1)');
if (
$pc->matches($chain->invocation()) &&
$chain->parameters[0] > 10000 &&
$this->level < 4
) {
// ...
}
?>
```

Syntax that can be used in forName():

```
call:[class]::[method]([narguments])
new:[class]([narguments])

```

Where:
- "class" is the fully qualified class name

- "method" name is the method name or the '*' (for any method)

- "narguments" is either a number of arguments or the '*'
(for any given number of parameters)

Security considerations
====
n/a

Speed impact
====
n/a

Dependencies
====
n/a

Related documents
====
- http://www.google.com/search?q=InvocationChain
Other InvocationChain implementations

- xp-framework/rfc #29: util.AbstractDeferredInvokationHandler
This will be deprecated and moved to invoke

- http://javaspecialists.co.za/archive/newsletter.do?issue=005&locale=en_US
Dynamic Proxies - Short Tutorial

- http://video.google.com/videoplay?docid=8566923311315412414
Aspect Oriented Programming

- http://javaspecialists.co.za/archive/newsletter.do?issue=087&locale=en_US
sun.reflect.Reflection

Comments
====
- friebe, Wed Oct 10 21:46:14 2007
Reset to draft - this needs some more thought