ChatExtension   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Test Coverage

Coverage 96.92%

Importance

Changes 6
Bugs 1 Features 0
Metric Value
wmc 23
eloc 68
c 6
b 1
f 0
dl 0
loc 132
ccs 63
cts 65
cp 0.9692
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A loadConfiguration() 0 22 4
A validateFactory() 0 9 4
A getDatabaseAdapter() 0 6 2
A getChats() 0 7 2
A registerMessageProcessors() 0 10 3
A beforeCompile() 0 3 1
A registerChatCommands() 0 11 3
A getMessageProcessors() 0 9 3
A getConfigSchema() 0 7 1
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
  /** @internal */
28
  public const SERVICE_CHAT_COMMANDS_PROCESSOR = "commandsProcessor";
29
  /** @internal */
30
  public const SERVICE_DATABASE_ADAPTER = "databaseAdapter";
31
  /** @internal */
32
  public const SERVICE_NEW_MESSAGE_FORM = "newMessageForm";
33
  /** @internal */
34
  public const TAG_CHAT = "chat.chat";
35
36
  public function getConfigSchema(): \Nette\Schema\Schema {
37 1
    return Expect::from(new Config(), [
38 1
      "chats" => Expect::arrayOf("interface")->default([])->required(),
39 1
      "messageProcessors" => Expect::arrayOf("class")->default([
40 1
        self::SERVICE_CHAT_COMMANDS_PROCESSOR => ChatCommandsProcessor::class,
41
      ]),
42 1
      "databaseAdapter" => Expect::type("class")->required(),
43
    ]);
44
  }
45
46
  /**
47
   * @throws InvalidChatControlFactoryException
48
   */
49
  private function validateFactory(string $interface): void {
50
    try {
51 1
      $rm = new \ReflectionMethod($interface, "create");
52 1
    } catch(\ReflectionException $e) {
53 1
      throw new InvalidChatControlFactoryException("Interface $interface does not contain method create.", 0, $e);
54
    }
55 1
    $returnType = $rm->getReturnType();
56 1
    if($returnType === null || !is_subclass_of($returnType->getName(), ChatControl::class)) {
0 ignored issues
show
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

56
    if($returnType === null || !is_subclass_of($returnType->/** @scrutinizer ignore-call */ getName(), ChatControl::class)) {
Loading history...
57 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...
58
    }
59 1
  }
60
  
61
  /**
62
   * @throws InvalidChatControlFactoryException
63
   */
64
  private function getChats(): array {
65 1
    $chats = [];
66 1
    foreach($this->config->chats as $name => $interface) {
67 1
      $this->validateFactory($interface);
68 1
      $chats[$name] = $interface;
69
    }
70 1
    return $chats;
71
  }
72
  
73
  /**
74
   * @throws InvalidMessageProcessorException
75
   */
76
  private function getMessageProcessors(): array {
77 1
    $messageProcessors = [];
78 1
    foreach($this->config->messageProcessors as $name => $processor) {
79 1
      if(!is_subclass_of($processor, ChatMessageProcessor::class)) {
80 1
        throw new InvalidMessageProcessorException("Invalid message processor $processor.");
81
      }
82 1
      $messageProcessors[$name] = $processor;
83
    }
84 1
    return $messageProcessors;
85
  }
86
  
87
  /**
88
   * @throws InvalidDatabaseAdapterException
89
   */
90
  private function getDatabaseAdapter(): string {
91 1
    $adapter = $this->config->databaseAdapter;
92 1
    if(!is_subclass_of($adapter, DatabaseAdapter::class)) {
93 1
      throw new InvalidDatabaseAdapterException("Invalid database adapter $adapter.");
94
    }
95 1
    return $adapter;
96
  }
97
  
98
  /**
99
   * @throws InvalidChatControlFactoryException
100
   * @throws InvalidMessageProcessorException
101
   * @throws InvalidDatabaseAdapterException
102
   */
103
  public function loadConfiguration(): void {
104 1
    $builder = $this->getContainerBuilder();
105 1
    $chats = $this->getChats();
106 1
    $characterProfileLink = $this->config->characterProfileLink;
107 1
    foreach($chats as $name => $interface) {
108 1
      $chat = $builder->addFactoryDefinition($this->prefix($name))
109 1
        ->setImplement($interface)
110 1
        ->addTag(self::TAG_CHAT);
111 1
      if($characterProfileLink !== "") {
112 1
        $chat->getResultDefinition()->addSetup("setCharacterProfileLink", [$characterProfileLink]);
113
      }
114
    }
115 1
    $messageProcessors = $this->getMessageProcessors();
116 1
    foreach($messageProcessors as $name => $processor) {
117 1
      $builder->addDefinition($this->prefix($name))
118 1
        ->setType($processor);
119
    }
120 1
    $databaseAdapter = $this->getDatabaseAdapter();
121 1
    $builder->addDefinition($this->prefix(self::SERVICE_DATABASE_ADAPTER))
122 1
      ->setType($databaseAdapter);
123 1
    $builder->addDefinition($this->prefix(self::SERVICE_NEW_MESSAGE_FORM))
124 1
      ->setType(NewChatMessageFormFactory::class);
125 1
  }
126
  
127
  private function registerMessageProcessors(): void {
128 1
    $builder = $this->getContainerBuilder();
129 1
    $chats = $this->getChats();
130 1
    $messageProcessors = $this->getMessageProcessors();
131 1
    foreach($chats as $chat => $chatClass) {
132
      /** @var FactoryDefinition $chatService */
133 1
      $chatService = $builder->getDefinition($this->prefix($chat));
134 1
      foreach($messageProcessors as $processor => $processorClass) {
135 1
        $processorService = $builder->getDefinition($this->prefix($processor));
136 1
        $chatService->getResultDefinition()->addSetup("addMessageProcessor", [$processorService]);
137
      }
138
    }
139 1
  }
140
  
141
  private function registerChatCommands(): void {
142 1
    $builder = $this->getContainerBuilder();
143
    try {
144
      /** @var ServiceDefinition $processor */
145 1
      $processor = $builder->getDefinition($this->prefix(self::SERVICE_CHAT_COMMANDS_PROCESSOR));
146
    } catch(MissingServiceException ) {
147
      return;
148
    }
149 1
    $chatCommands = $builder->findByType(IChatCommand::class);
150 1
    foreach($chatCommands as $command) {
151 1
      $processor->addSetup("addCommand", [$command]);
152
    }
153 1
  }
154
  
155
  public function beforeCompile(): void {
156 1
    $this->registerMessageProcessors();
157 1
    $this->registerChatCommands();
158 1
  }
159
}
160
?>