heroesofabenez /
chat
| 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
Loading history...
|
|||
| 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 |