ChatExtension::validateFactory()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 8
c 3
b 0
f 0
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 10
cc 4
nc 3
nop 1
crap 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 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
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

59
        if ($returnType === null || !is_subclass_of($returnType->/** @scrutinizer ignore-call */ getName(), ChatControl::class)) {
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