Optional::parse()   B
last analyzed

Complexity

Conditions 9
Paths 4

Size

Total Lines 62
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 25
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 62
rs 8.0555

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * This file is part of Rivescript-php
4
 *
5
 * (c) Johnny Mast <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Axiom\Rivescript\Cortex\Triggers;
12
13
use Axiom\Rivescript\Cortex\Input;
14
use Axiom\Rivescript\Traits\Regex;
15
16
/**
17
 * Optional class
18
 *
19
 * An Optional trigger is "trigger" sentence that has optional
20
 * keywords. The optional trigger will be valid when ever the
21
 * optional keyword is used or not.
22
 *
23
 * Note: Optionals do NOT match like wildcards do. They do NOT go into the <star> tags. The reason for this is that
24
 * optionals are optional, and won't always match anything if the client didn't actually say the optional word(s).
25
 *
26
 * PHP version 7.4 and higher.
27
 *
28
 * @category Core
29
 * @package  Cortext\Triggers
30
 * @author   Johnny Mast <[email protected]>
31
 * @license  https://opensource.org/licenses/MIT MIT
32
 * @link     https://github.com/axiom-labs/rivescript-php
33
 * @since    0.4.0
34
 */
35
class Optional extends Trigger
36
{
37
    use Regex;
38
39
    /**
40
     * The Regex pattern to find sets
41
     * in the trigger.
42
     *
43
     * Note: This pattern ignores the set if a @ character
44
     * is inside to make sure we don't confuse them with arrays.
45
     *
46
     * @var string
47
     */
48
    protected string $pattern = "/(\[)(?!\@)(.+?=*)(\])/ui";
49
50
    /**
51
     * Parse the trigger.
52
     *
53
     * @return bool|string
54
     */
55
    public function parse(string $trigger, Input $input): bool
56
    {
57
        if ($this->matchesPattern($this->pattern, $trigger) === true) {
58
            $triggerString = $trigger;
59
            $matches = $this->getMatchesFromPattern($this->pattern, $triggerString);
60
            $sets = [];
61
62
            /**
63
             * Replace every "set" in the trigger to their index number
64
             * found in the string.
65
             *
66
             * Example:
67
             *
68
             * "I (am|love) a robot. I like (my|style)"
69
             *
70
             * Will be replaced with:
71
             *
72
             * "I {0} a robot. I like {1}"
73
             */
74
            foreach ($matches as $index => $match) {
0 ignored issues
show
Bug introduced by
The expression $matches of type false is not traversable.
Loading history...
75
                $set = explode("|", $match[2]);
76
77
                /**
78
                 * To the set we add and empty value. This will emulate
79
                 * the optional keywords not being used.
80
                 */
81
                $set[] = "";
82
83
                if (count($set) > 0) {
84
                    $triggerString = str_replace($match[0], "{{$index}}", $triggerString);
85
                    $sets [] = $set;
86
                }
87
            }
88
89
            $combinations = $this->getCombinations(...$sets);
90
91
            if (count($combinations) > 0) {
92
                $sentences = [];
93
94
                foreach ($combinations as $combination) {
95
                    $tmp = $triggerString;
96
                    foreach ($combination as $index => $string) {
97
                        $tmp = str_replace("{{$index}}", $string, $tmp);
98
99
                        if (empty($string) === true) {
100
                            $tmp = str_replace("\x20\x20", " ", $tmp);
101
                        }
102
                    }
103
104
                    $sentences [] = trim($tmp);
105
                }
106
107
                $result = array_filter($sentences, static function (string $sentence) use ($input) {
108
                    return (strtolower($sentence) === strtolower($input->source()));
109
                });
110
111
                if (count($result) > 0) {
112
                    return $input->source();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $input->source() returns the type string which is incompatible with the type-hinted return boolean.
Loading history...
113
                }
114
            }
115
        }
116
        return false;
117
    }
118
119
    /**
120
     * Create a set of possible combinations for given arrays.
121
     *
122
     * Note: This function is taken from stackoverflow.com
123
     * first posted by Guilhermo Luna and later edited by user Amlette.
124
     *
125
     * @see https://stackoverflow.com/questions/8567082/how-to-generate-in-php-all-combinations-of-items-in-multiple-arrays/33259643#33259643
126
     *
127
     * @param array ...$arrays A set of arrays to combine.
128
     *
129
     * @return array|array[]
130
     *
131
     */
132
    private function getCombinations(array ...$arrays): array
133
    {
134
        $result = [[]];
135
        foreach ($arrays as $property => $property_values) {
136
            $tmp = [];
137
            foreach ($result as $result_item) {
138
                foreach ($property_values as $property_value) {
139
                    $tmp[] = array_merge($result_item, [$property => $property_value]);
140
                }
141
            }
142
            $result = $tmp;
143
        }
144
        return $result;
145
    }
146
}
147