hiraeth/signal

一个通用的回调包装器,它支持懒加载依赖注入的实例化

安装次数: 6,381

依赖者: 4

建议者: 0

安全性: 0

星星: 1

关注者: 2

分支: 0

公开问题: 0

类型:opus-package

3.0-beta 2023-11-04 00:48 UTC

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