Issues (3)

src/DI/ChatExtension.php (2 issues)

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\IChatMessageProcessor;
11
use HeroesofAbenez\Chat\IDatabaseAdapter;
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
 */
25
final class ChatExtension extends \Nette\DI\CompilerExtension {
26
  /** @internal */
27
  public const SERVICE_CHAT_COMMANDS_PROCESSOR = "commandsProcessor";
28
  /** @internal */
29
  public const SERVICE_DATABASE_ADAPTER = "databaseAdapter";
30
  /** @internal */
31
  public const SERVICE_NEW_MESSAGE_FORM = "newMessageForm";
32
  /** @internal */
33
  public const TAG_CHAT = "chat.chat";
34
  /** @var \stdClass */
35
  protected $config;
36
37
  public function getConfigSchema(): \Nette\Schema\Schema {
38 1
    return Expect::structure([
39 1
      "chats" => Expect::arrayOf("interface")->default([])->required(),
40 1
      "messageProcessors" => Expect::arrayOf("class")->default([
41 1
        self::SERVICE_CHAT_COMMANDS_PROCESSOR => ChatCommandsProcessor::class,
42
        ]),
43 1
      "databaseAdapter" => Expect::type("class")->required(),
44 1
      "characterProfileLink" => Expect::string(""),
45
    ]);
46
  }
47
48
  /**
49
   * @throws InvalidChatControlFactoryException
50
   */
51
  protected function validateFactory(string $interface): void {
52
    try {
53 1
      $rm = new \ReflectionMethod($interface, "create");
54 1
    } catch(\ReflectionException $e) {
55 1
      throw new InvalidChatControlFactoryException("Interface $interface does not contain method create.", 0, $e);
56
    }
57 1
    $returnType = $rm->getReturnType();
58 1
    if($returnType === null || !is_subclass_of($returnType->getName(), ChatControl::class)) {
0 ignored issues
show
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

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