1 | <?php |
||
2 | declare(strict_types=1); |
||
3 | |||
4 | namespace HeroesofAbenez\Chat\DI; |
||
5 | |||
6 | use HeroesofAbenez\Chat\ChatCommandsProcessor; |
||
7 | use HeroesofAbenez\Chat\IChatCommand; |
||
8 | use HeroesofAbenez\Chat\ChatControl; |
||
9 | use HeroesofAbenez\Chat\NewChatMessageFormFactory; |
||
10 | use HeroesofAbenez\Chat\ChatMessageProcessor; |
||
11 | use HeroesofAbenez\Chat\DatabaseAdapter; |
||
12 | use Nette\DI\MissingServiceException; |
||
13 | use HeroesofAbenez\Chat\InvalidChatControlFactoryException; |
||
14 | use HeroesofAbenez\Chat\InvalidMessageProcessorException; |
||
15 | use HeroesofAbenez\Chat\InvalidDatabaseAdapterException; |
||
16 | use Nette\Schema\Expect; |
||
17 | use Nette\DI\Definitions\FactoryDefinition; |
||
18 | use Nette\DI\Definitions\ServiceDefinition; |
||
19 | |||
20 | /** |
||
21 | * ChatExtension |
||
22 | * |
||
23 | * @author Jakub Konečný |
||
24 | * @property Config $config |
||
25 | */ |
||
26 | final class ChatExtension extends \Nette\DI\CompilerExtension |
||
27 | { |
||
28 | /** @internal */ |
||
29 | public const SERVICE_CHAT_COMMANDS_PROCESSOR = "commandsProcessor"; |
||
30 | /** @internal */ |
||
31 | public const SERVICE_DATABASE_ADAPTER = "databaseAdapter"; |
||
32 | /** @internal */ |
||
33 | public const SERVICE_NEW_MESSAGE_FORM = "newMessageForm"; |
||
34 | /** @internal */ |
||
35 | public const TAG_CHAT = "chat.chat"; |
||
36 | |||
37 | public function getConfigSchema(): \Nette\Schema\Schema |
||
38 | { |
||
39 | 1 | return Expect::from(new Config(), [ |
|
40 | 1 | "chats" => Expect::arrayOf("interface")->default([])->required(), |
|
41 | 1 | "messageProcessors" => Expect::arrayOf("class")->default([ |
|
42 | 1 | self::SERVICE_CHAT_COMMANDS_PROCESSOR => ChatCommandsProcessor::class, |
|
43 | ]), |
||
44 | 1 | "databaseAdapter" => Expect::type("class")->required(), |
|
45 | ]); |
||
46 | } |
||
47 | |||
48 | /** |
||
49 | * @throws InvalidChatControlFactoryException |
||
50 | */ |
||
51 | private function validateFactory(string $interface): void |
||
52 | { |
||
53 | try { |
||
54 | 1 | $rm = new \ReflectionMethod($interface, "create"); |
|
55 | 1 | } catch (\ReflectionException $e) { |
|
56 | 1 | throw new InvalidChatControlFactoryException("Interface $interface does not contain method create.", 0, $e); |
|
57 | } |
||
58 | 1 | $returnType = $rm->getReturnType(); |
|
59 | 1 | if ($returnType === null || !is_subclass_of($returnType->getName(), ChatControl::class)) { |
|
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
60 | 1 | throw new InvalidChatControlFactoryException( |
|
61 | 1 | "Return type of $interface::create() is not a subtype of " . ChatControl::class . "." |
|
62 | ); |
||
63 | } |
||
64 | 1 | } |
|
65 | |||
66 | /** |
||
67 | * @throws InvalidChatControlFactoryException |
||
68 | */ |
||
69 | private function getChats(): array |
||
70 | { |
||
71 | 1 | $chats = []; |
|
72 | 1 | foreach ($this->config->chats as $name => $interface) { |
|
73 | 1 | $this->validateFactory($interface); |
|
74 | 1 | $chats[$name] = $interface; |
|
75 | } |
||
76 | 1 | return $chats; |
|
77 | } |
||
78 | |||
79 | /** |
||
80 | * @throws InvalidMessageProcessorException |
||
81 | */ |
||
82 | private function getMessageProcessors(): array |
||
83 | { |
||
84 | 1 | $messageProcessors = []; |
|
85 | 1 | foreach ($this->config->messageProcessors as $name => $processor) { |
|
86 | 1 | if (!is_subclass_of($processor, ChatMessageProcessor::class)) { |
|
87 | 1 | throw new InvalidMessageProcessorException("Invalid message processor $processor."); |
|
88 | } |
||
89 | 1 | $messageProcessors[$name] = $processor; |
|
90 | } |
||
91 | 1 | return $messageProcessors; |
|
92 | } |
||
93 | |||
94 | /** |
||
95 | * @throws InvalidDatabaseAdapterException |
||
96 | */ |
||
97 | private function getDatabaseAdapter(): string |
||
98 | { |
||
99 | 1 | $adapter = $this->config->databaseAdapter; |
|
100 | 1 | if (!is_subclass_of($adapter, DatabaseAdapter::class)) { |
|
101 | 1 | throw new InvalidDatabaseAdapterException("Invalid database adapter $adapter."); |
|
102 | } |
||
103 | 1 | return $adapter; |
|
104 | } |
||
105 | |||
106 | /** |
||
107 | * @throws InvalidChatControlFactoryException |
||
108 | * @throws InvalidMessageProcessorException |
||
109 | * @throws InvalidDatabaseAdapterException |
||
110 | */ |
||
111 | public function loadConfiguration(): void |
||
112 | { |
||
113 | 1 | $builder = $this->getContainerBuilder(); |
|
114 | 1 | $chats = $this->getChats(); |
|
115 | 1 | $characterProfileLink = $this->config->characterProfileLink; |
|
116 | 1 | foreach ($chats as $name => $interface) { |
|
117 | 1 | $chat = $builder->addFactoryDefinition($this->prefix($name)) |
|
118 | 1 | ->setImplement($interface) |
|
119 | 1 | ->addTag(self::TAG_CHAT); |
|
120 | 1 | if ($characterProfileLink !== "") { |
|
121 | 1 | $chat->getResultDefinition()->addSetup("setCharacterProfileLink", [$characterProfileLink]); |
|
122 | } |
||
123 | } |
||
124 | 1 | $messageProcessors = $this->getMessageProcessors(); |
|
125 | 1 | foreach ($messageProcessors as $name => $processor) { |
|
126 | 1 | $builder->addDefinition($this->prefix($name)) |
|
127 | 1 | ->setType($processor); |
|
128 | } |
||
129 | 1 | $databaseAdapter = $this->getDatabaseAdapter(); |
|
130 | 1 | $builder->addDefinition($this->prefix(self::SERVICE_DATABASE_ADAPTER)) |
|
131 | 1 | ->setType($databaseAdapter); |
|
132 | 1 | $builder->addDefinition($this->prefix(self::SERVICE_NEW_MESSAGE_FORM)) |
|
133 | 1 | ->setType(NewChatMessageFormFactory::class); |
|
134 | 1 | } |
|
135 | |||
136 | private function registerMessageProcessors(): void |
||
137 | { |
||
138 | 1 | $builder = $this->getContainerBuilder(); |
|
139 | 1 | $chats = $this->getChats(); |
|
140 | 1 | $messageProcessors = $this->getMessageProcessors(); |
|
141 | 1 | foreach ($chats as $chat => $chatClass) { |
|
142 | /** @var FactoryDefinition $chatService */ |
||
143 | 1 | $chatService = $builder->getDefinition($this->prefix($chat)); |
|
144 | 1 | foreach ($messageProcessors as $processor => $processorClass) { |
|
145 | 1 | $processorService = $builder->getDefinition($this->prefix($processor)); |
|
146 | 1 | $chatService->getResultDefinition()->addSetup("addMessageProcessor", [$processorService]); |
|
147 | } |
||
148 | } |
||
149 | 1 | } |
|
150 | |||
151 | private function registerChatCommands(): void |
||
152 | { |
||
153 | 1 | $builder = $this->getContainerBuilder(); |
|
154 | try { |
||
155 | /** @var ServiceDefinition $processor */ |
||
156 | 1 | $processor = $builder->getDefinition($this->prefix(self::SERVICE_CHAT_COMMANDS_PROCESSOR)); |
|
157 | } catch (MissingServiceException) { |
||
158 | return; |
||
159 | } |
||
160 | 1 | $chatCommands = $builder->findByType(IChatCommand::class); |
|
161 | 1 | foreach ($chatCommands as $command) { |
|
162 | 1 | $processor->addSetup("addCommand", [$command]); |
|
163 | } |
||
164 | 1 | } |
|
165 | |||
166 | public function beforeCompile(): void |
||
167 | { |
||
168 | 1 | $this->registerMessageProcessors(); |
|
169 | 1 | $this->registerChatCommands(); |
|
170 | 1 | } |
|
171 | } |
||
172 |