1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace hiapi\event; |
4
|
|
|
|
5
|
|
|
use League\Event\AbstractListener; |
6
|
|
|
use League\Event\EventInterface; |
7
|
|
|
use PhpAmqpLib\Connection\AMQPStreamConnection; |
8
|
|
|
use PhpAmqpLib\Channel\AMQPChannel; |
9
|
|
|
use PhpAmqpLib\Message\AMQPMessage; |
10
|
|
|
use Psr\Log\LoggerInterface; |
11
|
|
|
use yii\base\InvalidConfigException; |
12
|
|
|
use yii\helpers\Inflector; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Class PublishToExchangeListener published events to AMQP exchange. |
16
|
|
|
* |
17
|
|
|
* @author Dmytro Naumenko <[email protected]> |
18
|
|
|
*/ |
19
|
|
|
class PublishToExchangeListener extends AbstractListener |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* @var AMQPStreamConnection |
23
|
|
|
*/ |
24
|
|
|
protected $amqp; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var LoggerInterface |
28
|
|
|
*/ |
29
|
|
|
private $logger; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var AMQPChannel |
33
|
|
|
*/ |
34
|
|
|
protected $channel; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var string the exchange name for the published messages |
38
|
|
|
*/ |
39
|
|
|
public $exchange; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var string the exchange type. Defaults to 'direct' |
43
|
|
|
*/ |
44
|
|
|
public $exchangeType = 'direct'; |
45
|
|
|
|
46
|
|
|
public function __construct(AMQPStreamConnection $amqp, LoggerInterface $logger) |
47
|
|
|
{ |
48
|
|
|
$this->amqp = $amqp; |
49
|
|
|
$this->logger = $logger; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Handle an event. |
54
|
|
|
* @param EventInterface $event |
55
|
|
|
* @return void |
56
|
|
|
*/ |
57
|
|
|
public function handle(EventInterface $event): void |
58
|
|
|
{ |
59
|
|
|
if ($this->exchange === null) { |
60
|
|
|
throw new \RuntimeException('Property PublishToQueueListener::queue must be set'); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
try { |
64
|
|
|
$message = $this->createMessage($event); |
65
|
|
|
$this->getChannel()->basic_publish($message, $this->exchange, $this->buildRoutingKey($event)); |
66
|
|
|
} catch (InvalidConfigException $exception) { |
|
|
|
|
67
|
|
|
$this->logger->critical($exception->getMessage()); |
68
|
|
|
} |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
protected function getChannel(): AMQPChannel |
72
|
|
|
{ |
73
|
|
|
if ($this->channel === null) { |
74
|
|
|
$this->channel = $channel = $this->amqp->channel(); |
75
|
|
|
$channel->exchange_declare($this->exchange, $this->exchangeType, false, true, true, false, false); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
return $this->channel; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
private function createMessage($event): AMQPMessage |
82
|
|
|
{ |
83
|
|
|
if (!$event instanceof \JsonSerializable) { |
84
|
|
|
throw new InvalidConfigException('Event "' . get_class($event) . '" can not be sent to exchange'); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
return new AMQPMessage(json_encode($event->jsonSerialize(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), [ |
88
|
|
|
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, |
89
|
|
|
'content_type' => 'application/json', |
90
|
|
|
]); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Builds routing key for $event. Default logic: |
95
|
|
|
* |
96
|
|
|
* For events named with `Was` (or `WillBe`) keyword (e.g. `ObjectWasChanged`) routing key will be `object.was.changed`. |
97
|
|
|
* For other events [[InvalidConfigException]] will be thrown. |
98
|
|
|
* |
99
|
|
|
* You can override this method to implement own routing key generation logic. |
100
|
|
|
* |
101
|
|
|
* @param EventInterface $event |
102
|
|
|
* @return string |
103
|
|
|
* @throws InvalidConfigException when `Was` keyword |
104
|
|
|
*/ |
105
|
|
|
public function buildRoutingKey(EventInterface $event) |
106
|
|
|
{ |
107
|
|
|
$className = (new \ReflectionClass($event))->getShortName(); |
108
|
|
|
|
109
|
|
|
foreach (['Was', 'WillBe', 'Will'] as $keyword) { |
110
|
|
|
if (strpos($className, $keyword) === false) { |
111
|
|
|
continue; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
[$object, $eventName] = explode($keyword, $className); |
115
|
|
|
|
116
|
|
|
$object = Inflector::camel2id($object); |
117
|
|
|
$eventName = Inflector::camel2id($eventName); |
118
|
|
|
$lowerKeyword = strtolower($keyword); |
119
|
|
|
|
120
|
|
|
return mb_strtolower("$object.$lowerKeyword.$eventName"); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
throw new InvalidConfigException("Event class name \"$className\" does not contain \"Was\" or \"WillBe\" keywords and can not be processed with default logic."); |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
|
Scrutinizer analyzes your
composer.json
/composer.lock
file if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.