Completed
Push — master ( 4317fb...cd535a )
by Vladimir
02:31
created

Bot::intentManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FondBot;
6
7
use FondBot\Traits\Loggable;
8
use FondBot\Channels\Channel;
9
use FondBot\Conversation\Context;
10
use FondBot\Contracts\Channels\User;
11
use FondBot\Contracts\Channels\Driver;
12
use FondBot\Conversation\IntentManager;
13
use FondBot\Conversation\ContextManager;
14
use FondBot\Contracts\Container\Container;
15
use FondBot\Contracts\Conversation\Intent;
16
use FondBot\Contracts\Conversation\Keyboard;
17
use FondBot\Contracts\Channels\OutgoingMessage;
18
use FondBot\Contracts\Conversation\Conversable;
19
use FondBot\Contracts\Conversation\Interaction;
20
use FondBot\Channels\Exceptions\InvalidChannelRequest;
21
use FondBot\Contracts\Channels\Extensions\WebhookVerification;
22
23
class Bot
24
{
25
    use Loggable;
26
27
    /** @var Bot */
28
    private static $instance;
29
30
    private $container;
31
    private $channel;
32
    private $driver;
33
    private $request;
34
    private $headers;
35
36
    /** @var Context|null */
37
    private $context;
38
39
    protected function __construct(
40
        Container $container,
41
        Channel $channel,
42
        Driver $driver,
43
        array $request,
44
        array $headers
45
    ) {
46
        $this->container = $container;
47
        $this->channel = $channel;
48
        $this->driver = $driver;
49
        $this->request = $request;
50
        $this->headers = $headers;
51
    }
52
53
    /**
54
     * Create new bot instance.
55
     *
56
     * @param Container $container
57
     * @param Channel   $channel
58
     * @param Driver    $driver
59
     * @param array     $request
60
     * @param array     $headers
61
     */
62
    public static function createInstance(
63
        Container $container,
64
        Channel $channel,
65
        Driver $driver,
66
        array $request,
67
        array $headers
68
    ): void {
69
        static::setInstance(
70
            new static($container, $channel, $driver, $request, $headers)
71
        );
72
    }
73
74
    /**
75
     * Get current instance.
76
     *
77
     * @return Bot
78
     */
79
    public static function getInstance(): Bot
80
    {
81
        return static::$instance;
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
82
    }
83
84
    /**
85
     * Set instance of the bot.
86
     *
87
     * @param Bot $instance
88
     */
89
    public static function setInstance(Bot $instance): void
90
    {
91
        static::$instance = $instance;
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
92
    }
93
94
    /**
95
     * Get context instance.
96
     *
97
     * @return Context|null
98
     */
99
    public function getContext(): ?Context
100
    {
101
        return $this->context;
102
    }
103
104
    /**
105
     * Set context instance.
106
     *
107
     * @param Context $context
108
     */
109
    public function setContext(Context $context): void
110
    {
111
        $this->context = $context;
112
    }
113
114
    /**
115
     * Clear context.
116
     */
117
    public function clearContext(): void
118
    {
119
        if ($this->context !== null) {
120
            $this->contextManager()->clear($this->context);
121
            $this->context = null;
122
        }
123
    }
124
125
    /**
126
     * Resolve from container.
127
     *
128
     * @param string $class
129
     *
130
     * @return mixed
131
     */
132
    public function get(string $class)
133
    {
134
        return $this->container->make($class);
135
    }
136
137
    /**
138
     * Process webhook request.
139
     *
140
     * @return mixed
141
     */
142
    public function process()
143
    {
144
        try {
145
            $this->debug('process', [
146
                'channel' => $this->channel->getName(),
147
                'request' => $this->request,
148
                'headers' => $this->headers,
149
            ]);
150
151
            // Driver has webhook verification
152
            if ($this->driver instanceof WebhookVerification && $this->driver->isVerificationRequest()) {
153
                $this->debug('process.verifyWebhook');
154
155
                return $this->driver->verifyWebhook();
156
            }
157
158
            // Verify request
159
            $this->driver->verifyRequest();
160
161
            // Resolve context
162
            $this->context = $this->contextManager()->resolve($this->channel->getName(), $this->driver);
163
164
            if ($this->context->getIntent() !== null && $this->context->getInteraction() !== null) {
165
                $this->converse($this->context->getInteraction());
0 ignored issues
show
Documentation introduced by
$this->context->getInteraction() is of type object<FondBot\Contracts...nversation\Interaction>, but the function expects a object<FondBot\Contracts...nversation\Conversable>.

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...
166
            } else {
167
                // Start or resume conversation
168
                $intent = $this->intentManager()->find($this->context, $this->driver->getMessage());
169
170
                if ($intent !== null) {
171
                    $this->converse($intent);
0 ignored issues
show
Documentation introduced by
$intent is of type object<FondBot\Contracts\Conversation\Intent>, but the function expects a object<FondBot\Contracts...nversation\Conversable>.

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...
172
                }
173
            }
174
175
            if ($this->context !== null) {
176
                $this->contextManager()->save($this->context);
177
            }
178
179
            return 'OK';
180
        } catch (InvalidChannelRequest $exception) {
181
            $this->error($exception->getMessage());
182
183
            return $exception->getMessage();
184
        }
185
    }
186
187
    /**
188
     * Start conversation.
189
     *
190
     * @param Conversable|Intent|Interaction $conversable
191
     */
192
    public function converse(Conversable $conversable): void
193
    {
194
        if ($conversable instanceof Intent) {
195
            $this->context->setIntent($conversable);
196
            $this->context->setInteraction(null);
197
            $this->context->setValues([]);
198
199
            $conversable->handle($this);
200
        } elseif ($conversable instanceof Interaction) {
201
            $conversable->handle($this);
202
203
            // If context is not cleared remember interaction
204
            if ($this->context !== null) {
205
                $this->context->setInteraction($conversable);
206
            }
207
        }
208
    }
209
210
    /**
211
     * Send message.
212
     *
213
     * @param User          $recipient
214
     * @param string        $text
215
     * @param Keyboard|null $keyboard
216
     *
217
     * @return OutgoingMessage
218
     */
219
    public function sendMessage(User $recipient, string $text, Keyboard $keyboard = null): OutgoingMessage
220
    {
221
        return $this->driver->sendMessage(
222
            $recipient,
223
            $text,
224
            $keyboard
225
        );
226
    }
227
228
    /**
229
     * Get context manager.
230
     *
231
     * @return ContextManager
232
     */
233
    private function contextManager(): ContextManager
234
    {
235
        return $this->get(ContextManager::class);
236
    }
237
238
    /**
239
     * Get intent manager.
240
     *
241
     * @return IntentManager
242
     */
243
    private function intentManager(): IntentManager
244
    {
245
        return $this->get(IntentManager::class);
246
    }
247
}
248