Passed
Push — develop ( dfbdff...d56661 )
by Johnny
02:04
created

ResponseQueue::mergeContinues()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 1
nop 1
dl 0
loc 23
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * The ResponseQueue sorts responses order and
5
 * determines what responses are valid or not.
6
 *
7
 * @package      Rivescript-php
8
 * @subpackage   Core
9
 * @category     ResponseQueue
10
 * @author       Johnny Mast <[email protected]>
11
 */
12
13
namespace Axiom\Rivescript\Cortex\ResponseQueue;
14
15
use Axiom\Collections\Collection;
16
use Axiom\Rivescript\Cortex\Node;
17
use Axiom\Rivescript\Traits\Tags;
18
19
/**
20
 * Class ResponseQueue
21
 */
22
class ResponseQueue extends Collection
23
{
24
25
    use Tags;
0 ignored issues
show
introduced by
The trait Axiom\Rivescript\Traits\Tags requires some properties which are not provided by Axiom\Rivescript\Cortex\...onseQueue\ResponseQueue: $tags, $input
Loading history...
26
27
    /**
28
     * A container with responses.
29
     *
30
     * @var Collection<ResponseQueueItem>
31
     */
32
    protected $responses;
33
34
    /**
35
     * ResponseQueue constructor.
36
     */
37
    public function __construct()
38
    {
39
        $this->responses = new Collection([]);
40
    }
41
42
    /**
43
     * Attach a response to the queue.
44
     *
45
     * @param Node $node The node contains information about the command.
46
     *
47
     * @return void
48
     */
49
    public function attach(Node $node)
50
    {
51
        $type = $this->determineResponseType($node->source());
52
53
        $this->responses->put($node->value(), new ResponseQueueItem($node->command(), $type, 0));
54
    }
55
56
    /**
57
     * Sort the responses by order.
58
     *
59
     * @param Collection<ResponseQueueItem> $responses The array containing the resources.
60
     *
61
     * @return Collection<ResponseQueueItem>
62
     */
63
    private function sortResponses(Collection $responses): Collection
64
    {
65
        return $responses->sort(
66
            function ($current, $previous) {
67
                return ($current->order < $previous->order) ? -1 : 1;
68
            }
69
        )->reverse();
70
    }
71
72
    /**
73
     * Check if a response is allowed to be returned by the bot or not.
74
     *
75
     * @param string            $response The response to validate.
76
     * @param ResponseQueueItem $item     The ResponseQueueItem.
77
     *
78
     * @return false|mixed
79
     */
80
    private function validateResponse(string $response, ResponseQueueItem $item)
81
    {
82
        $response = $this->parseTags($response);
83
84
        foreach (synapse()->responses as $class) {
85
            if (ucfirst($item->type) == $class and class_exists("\\Axiom\\Rivescript\\Cortex\\Responses\\{$class}")) {
86
                $class = "\\Axiom\\Rivescript\\Cortex\\Responses\\{$class}";
87
                $class = new $class($response, $item);
88
89
                $result = $class->parse();
90
91
                if ($result) {
92
                    return $result;
93
                }
94
            }
95
        }
96
97
        return false;
98
    }
99
100
    private function mergeContinues(Collection $responses): Collection
101
    {
102
        $lastData = $responses->first();
103
        $lastResponse = "";
104
        $responses->each(
105
            function (ResponseQueueItem $data, $response) use (&$lastData, &$lastResponse, &$responses) {
106
107
                if ($data->type == 'continue' && $lastData->command == '-') {
108
                    $responses->remove($lastResponse);
109
                    $responses->remove($response);
110
111
                    $lastResponse .= $response;
112
                    $responses->put($lastResponse, $lastData);
113
                }
114
115
                if ($data->command !== '^') {
116
                    $lastData = $data;
117
                    $lastResponse = $response;
118
                }
119
            }
120
        );
121
122
        return $responses;
123
    }
124
125
    /**
126
     * Determine the order of responses by type.
127
     *
128
     * @param Collection<ResponseQueueItem> $responses The responses to inspect.
129
     *
130
     * @return Collection<ResponseQueueItem>
131
     * @noinspection PhpVoidFunctionResultUsedInspection
132
     */
133
    private function determineResponseOrder(Collection $responses): Collection
134
    {
135
        return $responses->each(
136
            function (ResponseQueueItem $data, $response) use ($responses) {
137
                if (isset($data->type)) {
138
                    switch ($data->type) {
139
                        case 'condition':
140
                            $data->order += 3000000;
141
                            break;
142
                        case 'weighted':
143
                        case 'atomic':
144
                            $data->order += 1000000;
145
                            break;
146
                    }
147
148
                    $responses->put($response, $data);
149
                }
150
            }
151
        );
152
    }
153
154
    /**
155
     * Determine the response type.
156
     *
157
     * @param string $response
158
     *
159
     * @return string
160
     */
161
    public function determineResponseType(string $response): string
162
    {
163
        $wildcards = [
164
            'weighted' => '{weight=(.+?)}',
165
            'condition' => '/^\*/',
166
            'continue' => '/^\^/',
167
            'atomic' => '/-/',
168
        ];
169
170
        foreach ($wildcards as $type => $pattern) {
171
            if (@preg_match_all($pattern, $response, $matches)) {
172
                return $type;
173
            }
174
        }
175
176
        return 'atomic';
177
    }
178
179
    /**
180
     * Process the Response Queue.
181
     *
182
     * @return false|int|string|null
183
     */
184
    public function process()
185
    {
186
        $this->responses = $this->mergeContinues($this->responses);
187
        $this->responses = $this->determineResponseOrder($this->responses);
188
189
        $validResponses = new Collection([]);
190
        foreach ($this->responses as $response => $item) {
191
            $result = $this->validateResponse($response, $item);
192
193
            if ($result !== false) {
194
                $validResponses->put($result, $item);
195
            }
196
        }
197
198
        $validResponses = $this->sortResponses($validResponses);
199
200
        if ($validResponses->count() > 0) {
201
            return $validResponses->keys()->first();
202
        }
203
204
        return false;
205
    }
206
}
207