Idea for DX improvement
The trait pattern works well, but it does seem a shame we can't have IDE support and things registered by hooks.
We could though:
- on boot or such, look for a php file in civi:files called LocallyRegistered.php and
include()
it if present. - declare
class FluentImport extends Civi\FluentImport\LocallyRegistered
- create empty
LocallyRegistered
class in normal place. - cron/cli function: sources the empty file (3) so we have a definition. Use FluentImport::factory() then call a getMethods method on the store and use reflection to create our civi:files/LocallyRegistered file like:
<?php
namespace Civi\FluentImport;
/**
* @method mixed fred($colour, int $value)
*/
class LocallyRegistered {}
That way as long as IDEs load this file first (hmmm) then we'll have autocopmlete.
Proof of concept:
<?php
$f = FluentImport::factory();
$f->fred('red', 123);
$f->registerMethod('fred',
function($colour = 'green', int $value = 0) { return $colour . $value;
);
$methods = $f->getStore()->getMethods();
$i = ['<' . "?php", 'namespace Civi\\FluentImport;', '', '/**'];
foreach ($methods as $x => $c) {
$method = new ReflectionFunction($c);
$rt = $method->getReturnType() ?: 'mixed' ;
$line = " * @method $rt $x(";
$args = [];
foreach ($method->getParameters() as $param) {
▏ $args[] = trim($param->getType() . ' $' . $param->getName());
}
$line .= implode(', ' , $args) . ")";
$i[] = $line;
}
$i[] = " */\nclass LocallyRegistered {}\n";
file_put_contents(__DIR__ . '/LocallyRegistered.php', implode("\n", $i));
Another way would be to recreate that file during the container hook to register global methods.
Q. how to stop IDEs loading the dummy file? Maybe it doesn't matter because it's empty? Maybe something like instead of defining with class
use class_alias(...)
- intelephense at least isn't clever enough to use that.
More on why
Registering global methods is nice, but really I thought about this because things like filters.
I don't like $f->validEmail()
which is a wrapper around the Filter class. It means all the filters have to be declared in FI.
Maybe better to do an accessor for filters, like
$m = $f->getFilterAceessor();
$f->clean('first_name', [$m->removeEmoji(), 'ucfirst', ...])
Then I'd have the same problem/solution with user-declared filters. But at least the filter code would be outside the FI class itself.