hiraeth / signal
一个通用的回调包装器,它支持懒加载依赖注入的实例化
3.0-beta
2023-11-04 00:48 UTC
Requires
- hiraeth/app: ^3.0
- psr/container: ^2.0
Requires (Dev)
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2024-09-14 17:37:36 UTC
README
Signal是一个通用的回调包装器/代理,它支持自定义解析,包括懒加载实例化和依赖注入,这些在其它情况下可能无法支持。
如果你认为你知道上面的意思并且想要跳过解释,你只需
安装
composer require hiraeth/signal
否则,继续阅读以了解...
问题是什么?
我在查看使用igorw/evenement
,看到了这个
$emitter->on('user.created', function (User $user) use ($logger) { $logger->log(sprintf("User '%s' was created.", $user->getLogin())); });
许多库支持泛型回调,通常显示为匿名函数。如果你编写大量自定义配置,这很棒,但如果将配置/配置数据与配置逻辑分开,并使用类,例如:
文件:config.php
return [ 'events' => [ 'user.created' => 'UserCreatedHandler' ] ]
现在当你读取配置时,你可以这样做
foreach ($config['events'] as $event => $handler) { $emitter->on($event, $container->get($handler)) }
但这样你会在前端解析所有处理程序及其依赖项。所以也许你可以这样做
foreach ($config['events'] as $event => $handler) { $emitter->on($event, function(...$params) use ($container, $handler) { $handler = $container->get($handler); return $handler(...$params); } }
好吧,这并不那么糟糕,但现在你几乎是在假设你的处理程序总是会实现__invoke
。如果你需要在其他地方处理不同的回调样式怎么办?现在你到处都是自定义代理回调。
有更好的方法吗?
是的!一个回调(解析器)统治所有!
$signal = new Hiraeth\Utils\Signal(function($signal) use ($container) { if (is_string($signal)) { if (function_exists($signal)) { return $signal; } if (strpos($signal, '::') !== FALSE) { list($class, $method) = explode('::', $signal); return [$container->get($class), $method]; } if (class_exists($signal)) { return [$container->get($signal), '__invoke']; } } return NULL; });
那么
foreach ($config['events'] as $event => $handler) { $emitter->on($event, $signal->create($handler)) }
这不是很棒吗?
如果你想让它更漂亮,你可以将解析器功能移动到单独的类中,该类实现__invoke($signal)
$resolver = new Resolver($container); $signal = new Hiraeth\Utils\Signal($resolver);
这就全部了吗?
不...因为无论什么$handler
被传递给你的自定义解析器,你可以在解析处理程序时做任何你想做的事情。例如,你可能想处理"artisan"回调(不知道为什么,但随便)
$signal = str_replace('@', '::', $signal);
也许你想创建URL回调
if (preg_match('#^https?://#', $signal)) { $client = $container->get('APIClient'); $client->setUrl($signal); return function() use ($client) { $client->setData(func_get_args()); $client->send(); }; }
谁在乎呢!世界是你的牡蛎。
好吧,你不是还在前端实例化一切吗?
不... $signal->create($handler)
不返回解析后的处理程序。相反,它只跟踪$handler
,并返回一个代理回调,这样处理程序就不会在真正需要时解析。看看它有一个测试
class SignalTest extends PHPUnit\Framework\TestCase { public function testProxy() { // // Create a new instance of signal with a totally useless // resolver that always returns the same callback. // $signal = new Hiraeth\Utils\Signal(function($signal) use (&$target) { $this->assertSame($signal, 'fake_signal'); return $target = new class { public function __invoke($foo, $bar) { return $foo . ' ' . $bar; } }; }); // // Create a new proxy for the signal as $foobar // this does not yet call the resolver, rather $foobar // contains a callback that will call the resolver when // it gets called. // $foobar = $signal->create('fake_signal'); $this->assertNotSame($target, $foobar); // // Calling foobar resolves the signal to a target handler // our anonymous class above, and calls it to get the result. // $result = $foobar('foo', 'bar'); $this->assertSame($result, 'foo bar'); // // Once resolved, calling $signal->create() will return the // resolved handler directly without a proxy. // $newbar = $signal->create('fake_signal'); $this->assertSame($target, $newbar); } }
亲自运行测试
php vendor/bin/phpunit --bootstrap vendor/autoload.php test/cases