Passed
Push — master ( 970c61...032e68 )
by Johnny
04:05 queued 38s
created

ResponseQueue::mergeContinues()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 13
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 23
rs 9.8333
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
    /**
101
     * Merge the ^ continue responses to the last - response.
102
     *
103
     * @param Collection<ResponseQueueItem> $responses The array containing the responses.
104
     *
105
     * @return Collection<ResponseQueueItem>
106
     */
107
    protected function mergeContinues(Collection $responses): Collection
108
    {
109
        $lastData = $responses->first();
110
        $lastResponse = "";
111
        $responses->each(
112
            function (ResponseQueueItem $data, $response) use (&$lastData, &$lastResponse, &$responses) {
113
114
                if ($data->type == 'continue' && $lastData->command == '-') {
115
                    $responses->remove($lastResponse);
116
                    $responses->remove($response);
117
118
                    $lastResponse .= $response;
119
                    $responses->put($lastResponse, $lastData);
120
                }
121
122
                if ($data->command !== '^') {
123
                    $lastData = $data;
124
                    $lastResponse = $response;
125
                }
126
            }
127
        );
128
129
        return $responses;
130
    }
131
132
    /**
133
     * Determine the order of responses by type.
134
     *
135
     * @param Collection<ResponseQueueItem> $responses The responses to inspect.
136
     *
137
     * @return Collection<ResponseQueueItem>
138
     */
139
    private function determineResponseOrder(Collection $responses): Collection
140
    {
141
        return $responses->each(
142
            function (ResponseQueueItem $data, $response) use ($responses) {
143
                if (isset($data->type)) {
144
                    switch ($data->type) {
145
                        case 'condition':
146
                            $data->order += 3000000;
147
                            break;
148
                        case 'weighted':
149
                        case 'atomic':
150
                            $data->order += 1000000;
151
                            break;
152
                    }
153
154
                    $responses->put($response, $data);
155
                }
156
            }
157
        );
158
    }
159
160
    /**
161
     * Determine the response type.
162
     *
163
     * @param string $response
164
     *
165
     * @return string
166
     */
167
    public function determineResponseType(string $response): string
168
    {
169
        $wildcards = [
170
            'weighted' => '{weight=(.+?)}',
171
            'condition' => '/^\*/',
172
            'continue' => '/^\^/',
173
            'atomic' => '/-/',
174
        ];
175
176
        foreach ($wildcards as $type => $pattern) {
177
            if (@preg_match_all($pattern, $response, $matches)) {
178
                return $type;
179
            }
180
        }
181
182
        return 'atomic';
183
    }
184
185
    /**
186
     * Process the Response Queue.
187
     *
188
     * @return false|int|string|null
189
     */
190
    public function process()
191
    {
192
        $this->responses = $this->mergeContinues($this->responses);
193
        $this->responses = $this->determineResponseOrder($this->responses);
194
195
        $validResponses = new Collection([]);
196
        foreach ($this->responses as $response => $item) {
197
            $result = $this->validateResponse($response, $item);
198
199
            if ($result !== false) {
200
                $validResponses->put($result, $item);
201
            }
202
        }
203
204
        $validResponses = $this->sortResponses($validResponses);
205
206
        if ($validResponses->count() > 0) {
207
            return $validResponses->keys()->first();
208
        }
209
210
        return false;
211
    }
212
}
213