(dev/core#873) civi.api.prepare - Allow dynamic wrappers
Overview
--------
Allow extensions to dynamically wrap an API. There are some existing mechanisms which sort-of allow wrapping, but
this enables additional use-cases.
Before
------
There are a few techniques for wrapping or overriding an API, but none is suitable to my current use-case. Limitations:
* `hook_civicrm_apiWrappers` - This allows registering things before and after the API call, but it does not allow
changing the underlying API call.
* `civi.api.{authorize,prepare,respond}` events - Same as above. These are a bit more nuanced/fine-grained, but still does not allow changing
* `civi.api.resolve` event with `AdhocProvider` - This allows you to swap an API completely, but it doesn't allow you
to delegate back to the original API call (if you've got nothing to add).
After
------
One may subscribe to `civi.api.prepare` and then call the `wrapApi()` helper:
```php
function onPrepare($event) {
if ($event->getApiRequestSig() === '3.widget.frobnicate') {
$event->wrapApi(function($apiRequest, $continue){
echo "Hello\n";
$r = $continue($apiRequest);
echo "Goodbye\n";
return $r;
});
}
}
```
Key characteristics:
* The wrapper only applies if you register it specifically for the given API call.
* The wrapper allows you to defer to the original implementation (`$continue`).
* The wrapper allows you to perform logic before and after.
* The wrapper allows you to *conditionally* replace -- you might call `$continue` or something entirely different.
The style here is more event-oriented, but you can see the same concept in OOP systems, such as PHP's function-override notation.
This would be analogous:
```php
class MyChild extends MyParent {
function frobnicate($arg1) {
echo "Hello\n";
$r = parent::frobnicate($arg1);
echo "Goodbye\n";
return $r;
}
}
```