Passed
Push — master ( 64171d...4510f4 )
by Vladimir
10:35
created

ConversationManager::getCacheKeyForContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FondBot\Conversation;
6
7
use FondBot\Channels\Chat;
8
use FondBot\Channels\User;
9
use FondBot\Channels\Channel;
10
use InvalidArgumentException;
11
use Illuminate\Cache\Repository;
12
use FondBot\Events\MessageReceived;
13
use FondBot\Contracts\Conversation\Manager;
14
use FondBot\Contracts\Conversation\Conversable;
15
use Illuminate\Contracts\Foundation\Application;
16
17
class ConversationManager implements Manager
18
{
19
    private $intents = [];
20
    private $fallbackIntent;
21
22
    private $application;
23
    private $cache;
24
25
    private $transitioned = false;
26
27
    private $messageReceived;
28
29 88
    public function __construct(Application $application, Repository $cache)
30
    {
31 88
        $this->application = $application;
32 88
        $this->cache = $cache;
33 88
    }
34
35
    /** {@inheritdoc} */
36 2
    public function registerIntent(string $class): void
37
    {
38 2
        $this->intents[] = $class;
39 2
    }
40
41
    /** {@inheritdoc} */
42 88
    public function registerFallbackIntent(string $class): void
43
    {
44 88
        $this->fallbackIntent = $class;
45 88
    }
46
47
    /** {@inheritdoc} */
48 1
    public function getIntents(): array
49
    {
50 1
        return $this->intents;
51
    }
52
53
    /** {@inheritdoc} */
54 1
    public function matchIntent(MessageReceived $messageReceived): ?Intent
55
    {
56 1
        foreach ($this->intents as $intent) {
57
            /** @var Intent $intent */
58 1
            $intent = resolve($intent);
0 ignored issues
show
Documentation introduced by
$intent is of type object<FondBot\Conversation\Intent>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
59
60 1
            $activators = ActivatorParser::parse($intent->activators());
61
62 1
            foreach ($activators as $activator) {
63 1
                if ($activator->matches($messageReceived) && $intent->passesAuthorization($messageReceived)) {
64 1
                    return $intent;
65
                }
66
            }
67
        }
68
69
        // Otherwise, return fallback intent
70 1
        return resolve($this->fallbackIntent);
71
    }
72
73
    /** {@inheritdoc} */
74 1
    public function resolveContext(Channel $channel, Chat $chat, User $user): Context
75
    {
76 1
        $value = $this->cache->get($this->getCacheKeyForContext($channel, $chat, $user), [
77 1
            'chat' => $chat,
78 1
            'user' => $user,
79
            'intent' => null,
80
            'interaction' => null,
81
            'items' => [],
82
        ]);
83
84 1
        $context = new Context($channel, $chat, $user, $value['items'] ?? []);
85
86 1
        if (isset($value['intent'])) {
87 1
            $context->setIntent(resolve($value['intent']));
88
        }
89
90 1
        if (isset($value['interaction'])) {
91 1
            $context->setInteraction(resolve($value['interaction']));
92
        }
93
94
        // Bind resolved instance to the container
95 1
        $this->application->instance('fondbot.conversation.context', $context);
96
97 1
        return $context;
98
    }
99
100
    /** {@inheritdoc} */
101 1
    public function saveContext(Context $context): void
102
    {
103 1
        $this->cache->forever(
104 1
            $this->getCacheKeyForContext($context->getChannel(), $context->getChat(), $context->getUser()),
105 1
            $context->toArray()
106
        );
107 1
    }
108
109
    /** {@inheritdoc} */
110
    public function flushContext(Context $context): void
111
    {
112
        $this->cache->forget(
113
            $this->getCacheKeyForContext($context->getChannel(), $context->getChat(), $context->getUser())
114
        );
115
    }
116
117
    /** {@inheritdoc} */
118 88
    public function getContext(): ?Context
119
    {
120 88
        if (!$this->application->has('fondbot.conversation.context')) {
121 88
            return null;
122
        }
123
124 13
        return $this->application->get('fondbot.conversation.context');
125
    }
126
127
    /** {@inheritdoc} */
128
    public function setReceivedMessage(MessageReceived $messageReceived): void
129
    {
130
        $this->messageReceived = $messageReceived;
131
    }
132
133
    /** {@inheritdoc} */
134
    public function markAsTransitioned(): void
135
    {
136
        $this->transitioned = true;
137
    }
138
139
    /** {@inheritdoc} */
140
    public function transitioned(): bool
141
    {
142
        return $this->transitioned;
143
    }
144
145
    /** {@inheritdoc} */
146
    public function converse(Conversable $conversable): void
147
    {
148
        if ($conversable instanceof Intent) {
149
            context()->setIntent($conversable)->setInteraction(null);
150
        }
151
152
        $conversable->handle($this->messageReceived);
153
    }
154
155
    /** {@inheritdoc} */
156
    public function transition(string $conversable): void
157
    {
158
        /** @var Interaction $instance */
159
        $instance = resolve($conversable);
160
161
        if (!$instance instanceof Conversable) {
162
            throw new InvalidArgumentException('Invalid conversable `'.$conversable.'`');
163
        }
164
165
        $this->converse($instance, $this->messageReceived);
0 ignored issues
show
Unused Code introduced by
The call to ConversationManager::converse() has too many arguments starting with $this->messageReceived.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
166
        $this->markAsTransitioned();
167
    }
168
169
    /** {@inheritdoc} */
170
    public function restart(Conversable $conversable): void
171
    {
172
        if ($conversable instanceof Intent) {
173
            $this->markAsTransitioned();
174
        }
175
176
        $this->converse($conversable);
177
178
        if ($conversable instanceof Interaction) {
179
            $this->markAsTransitioned();
180
        }
181
    }
182
183 88
    public function __destruct()
184
    {
185 88
        $context = $this->getContext();
186
187 88
        if ($context === null) {
188 88
            return;
189
        }
190
191
        // Close session if conversation has not been transitioned
192
        if (!$this->transitioned()) {
193
            $this->flushContext($context);
194
        }
195
196
        // Save context if exists
197
        if ($this->transitioned() && $context = context()) {
198
            $this->saveContext($context);
199
        }
200
    }
201
202 2
    private function getCacheKeyForContext(Channel $channel, Chat $chat, User $user): string
203
    {
204 2
        return implode('.', ['context', $channel->getName(), $chat->getId(), $user->getId()]);
205
    }
206
}
207