Completed
Push — master ( 5a2ee7...b08e77 )
by Jakub
02:05
created

ChatExtension   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 156
Duplicated Lines 0 %

Test Coverage

Coverage 96%

Importance

Changes 0
Metric Value
wmc 27
dl 0
loc 156
ccs 72
cts 75
cp 0.96
rs 10
c 0
b 0
f 0

9 Methods

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