Completed
Push — master ( 8be5d6...f2bcc2 )
by Dan
02:19
created

PluginTrait::validRegExp()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
1
<?php
2
3
namespace Nopolabs\Yabot\Plugin;
4
5
use Exception;
6
use Nopolabs\Yabot\Helpers\ConfigTrait;
7
use Nopolabs\Yabot\Helpers\LogTrait;
8
use Nopolabs\Yabot\Message\Message;
9
use Throwable;
10
11
trait PluginTrait
12
{
13
    use LogTrait;
14
    use ConfigTrait;
15
16
    private $pluginId;
17
18
    /** @var PluginMatcher */
19
    private $pluginMatcher;
20
21
    /** @var MethodMatcher[] */
22
    private $methodMatchers;
23
24
    public function help(): string
25
    {
26
        return $this->get('help', 'no help available');
27
    }
28
29
    public function status(): string
30
    {
31
        return 'running';
32
    }
33
34
    public function init(string $pluginId, array $params)
35
    {
36
        $this->pluginId = $pluginId;
37
        $this->overrideConfig($params);
38
        $this->methodMatchers = $this->buildMethodMatchers($this->get('matchers'));
39
        $this->pluginMatcher = new PluginMatcher($pluginId, $this->getIsBot(), $this->getChannels(), $this->getUsers(), $this->getLog());
40
41
        $this->info("inited $pluginId config:", $this->getConfig());
42
    }
43
44
    public function replaceInPatterns($search, $replace, array $matchers): array
45
    {
46
        $replaced = [];
47
        foreach ($matchers as $name => $params) {
48
            $params['patterns'] = array_map(function($pattern) use ($search, $replace) {
49
                return str_replace($search, $replace, $pattern);
50
            }, $params['patterns']);
51
            $replaced[$name] = $params;
52
        }
53
        return $replaced;
54
    }
55
56
    public function getPrefix(): string
57
    {
58
        return $this->get('prefix');
59
    }
60
61
    public function handle(Message $message)
62
    {
63
        if (!$this->pluginMatch($message)) {
64
            return;
65
        }
66
67
        if ($matched = $this->methodMatch($message)) {
68
            list($method, $matches) = $matched;
69
70
            $this->dispatch($method, $message, $matches);
71
        }
72
    }
73
74
    protected function pluginMatch(Message $message): bool
75
    {
76
        return $this->pluginMatcher->matches($message);
77
    }
78
79
    protected function methodMatch(Message $message): array
80
    {
81
        foreach ($this->methodMatchers as $methodMatcher) {
82
            if (($matches = $methodMatcher->matches($message)) === false) {
83
                continue;
84
            }
85
86
            $method = $methodMatcher->getMethod();
87
88
            $this->info("{$this->pluginId}:{$methodMatcher->getName()}:$method matched");
89
90
            return [$method, $matches];
91
        }
92
93
        return [];
94
    }
95
96
    protected function dispatch(string $method, Message $message, array $matches)
97
    {
98
        if (!method_exists($this, $method)) {
99
            $this->warning("{$this->pluginId} no method named: $method");
100
            return;
101
        }
102
103
        try {
104
105
            $this->$method($message, $matches);
106
107
        } catch (Exception $e) {
108
            $this->warning('Exception in '.static::class.'::'.$method);
109
            $this->warning($e->getMessage());
110
            $this->warning($e->getTraceAsString());
111
        }
112
    }
113
114
    protected function overrideConfig(array $params)
115
    {
116
        $config = $this->canonicalConfig(array_merge($this->getConfig(), $params));
117
118
        $this->setConfig($config);
119
    }
120
121
    protected function setPluginId($pluginId)
122
    {
123
        $this->pluginId = $pluginId;
124
    }
125
126
    protected function getUsers()
127
    {
128
        return $this->get('users');
129
    }
130
131
    protected function getChannels()
132
    {
133
        return $this->get('channels');
134
    }
135
136
    /**
137
     * @return bool|null
138
     */
139
    protected function getIsBot()
140
    {
141
        return $this->get('isBot');
142
    }
143
144
    protected function getMatchers(): array
145
    {
146
        return $this->get('matchers');
147
    }
148
149
    protected function canonicalConfig(array $config): array
150
    {
151
        $config['prefix'] = $config['prefix'] ?? '';
152
        $config['isBot'] = $config['isBot'] ?? null;
153
        $config['channels'] = $config['channels'] ?? array_filter([$config['channel'] ?? null]);
154
        $config['users'] = $config['users'] ?? array_filter([$config['user'] ?? null]);
155
        $config['matchers'] = $this->canonicalMatchers($config['matchers'] ?? []);
156
157
        return $config;
158
    }
159
160
    protected function canonicalMatchers(array $matchers): array
161
    {
162
        $expanded = [];
163
164
        foreach ($matchers as $name => $params) {
165
            $params = is_array($params) ? $params : ['patterns' => [$params]];
166
            if (isset($params['pattern'])) {
167
                $params['patterns'] = [$params['pattern']];
168
                unset($params['pattern']);
169
            }
170
            $params['isBot'] = $params['isBot'] ?? null;
171
            $params['channels'] = $params['channels'] ?? array_filter([$params['channel'] ?? null]);
172
            $params['users'] = $params['users'] ?? array_filter([$params['user'] ?? null]);
173
            $params['method'] = $params['method'] ?? $name;
174
175
            if (!method_exists($this, $params['method'])) {
176
                $this->warning("{$this->pluginId} no method named: {$params['method']}");
177
            }
178
179
            $expanded[$name] = $params;
180
        }
181
182
        if (!$expanded) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $expanded of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
183
            $this->warning("{$this->pluginId} has no matchers");
184
        }
185
186
        return $expanded;
187
    }
188
189
    protected function buildMethodMatchers(array $matchers) : array
190
    {
191
        $methodMatchers = [];
192
193
        foreach ($matchers as $name => $params) {
194
            $patterns = [];
195
            foreach ($params['patterns'] as $pattern) {
196
                if ($this->validRegExp($pattern, $name)) {
197
                    $patterns[] = $pattern;
198
                }
199
            }
200
            $params['patterns'] = $patterns;
201
202
            $methodMatchers[] = new MethodMatcher(
203
                $name,
204
                $params['isBot'],
205
                $params['channels'],
206
                $params['users'],
207
                $params['patterns'],
208
                $params['method'],
209
                $this->getLog()
210
            );
211
        }
212
213
        return $methodMatchers;
214
    }
215
216
    protected function validRegExp($pattern, $name) : bool
217
    {
218
        try {
219
            preg_match($pattern, '');
220
            return true;
221
        } catch (Throwable $e) {
222
            $this->warning("$name.pattern='$pattern' ".$e->getMessage());
223
            return false;
224
        }
225
    }
226
}