Completed
Push — master ( 347f8f...da21b0 )
by Dan
02:15
created

PluginTrait::isValidRegExp()   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 getPriority(): int
57
    {
58
        return $this->get('priority');
59
    }
60
61
    public function getPrefix(): string
62
    {
63
        return $this->get('prefix');
64
    }
65
66
    public function handle(Message $message)
67
    {
68
        if (!$this->pluginMatch($message)) {
69
            return;
70
        }
71
72
        if ($matched = $this->methodMatch($message)) {
73
            list($method, $matches) = $matched;
74
75
            $this->dispatch($method, $message, $matches);
76
        }
77
    }
78
79
    protected function pluginMatch(Message $message): bool
80
    {
81
        return $this->pluginMatcher->matches($message);
82
    }
83
84
    protected function methodMatch(Message $message): array
85
    {
86
        foreach ($this->methodMatchers as $methodMatcher) {
87
            if (($matches = $methodMatcher->matches($message)) === false) {
88
                continue;
89
            }
90
91
            $method = $methodMatcher->getMethod();
92
93
            $this->info("{$this->pluginId}:{$methodMatcher->getName()}:$method matched");
94
95
            return [$method, $matches];
96
        }
97
98
        return [];
99
    }
100
101
    protected function dispatch(string $method, Message $message, array $matches)
102
    {
103
        if (!method_exists($this, $method)) {
104
            $this->warning("{$this->pluginId} no method named: $method");
105
            return;
106
        }
107
108
        try {
109
110
            $this->$method($message, $matches);
111
112
        } catch (Throwable $throwable) {
113
            $errmsg = 'Exception in '.static::class.'::'.$method."\n"
114
                .$throwable->getMessage()."\n"
115
                .$throwable->getTraceAsString();
116
            $this->warning($errmsg);
117
        }
118
    }
119
120
    protected function overrideConfig(array $params)
121
    {
122
        $config = $this->canonicalConfig(array_merge($this->getConfig(), $params));
123
124
        $this->setConfig($config);
125
    }
126
127
    protected function setPluginId($pluginId)
128
    {
129
        $this->pluginId = $pluginId;
130
    }
131
132
    protected function getUsers()
133
    {
134
        return $this->get('users');
135
    }
136
137
    protected function getChannels()
138
    {
139
        return $this->get('channels');
140
    }
141
142
    /**
143
     * @return bool|null
144
     */
145
    protected function getIsBot()
146
    {
147
        return $this->get('isBot');
148
    }
149
150
    protected function getMatchers(): array
151
    {
152
        return $this->get('matchers');
153
    }
154
155
    protected function canonicalConfig(array $config): array
156
    {
157
        return [
158
            'priority' => $config['priority'] ?? PluginManager::DEFAULT_PRIORITY,
159
            'prefix' => ($config['prefix'] ?? '') ? $config['prefix'] : PluginManager::NO_PREFIX,
160
            'isBot' => $config['isBot'] ?? null,
161
            'channels' => $config['channels'] ?? array_filter([$config['channel'] ?? null]),
162
            'users' => $config['users'] ?? array_filter([$config['user'] ?? null]),
163
            'matchers' => $this->canonicalMatchers($config['matchers'] ?? []),
164
        ];
165
    }
166
167
    protected function canonicalMatchers(array $matchers): array
168
    {
169
        $expanded = [];
170
171
        foreach ($matchers as $name => $params) {
172
            $expanded[$name] = $this->canonicalMatcher($name, $params);
173
        }
174
175
        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...
176
            $this->warning("{$this->pluginId} has no matchers");
177
        }
178
179
        return $expanded;
180
    }
181
182
    protected function canonicalMatcher(string $name, $params) : array
183
    {
184
        $params = is_array($params) ? $params : ['patterns' => [$params]];
185
186
        $method = $params['method'] ?? $name;
187
188
        if (!method_exists($this, $method)) {
189
            $this->warning("{$this->pluginId} no method named: $method");
190
        }
191
192
        return [
193
            'isBot' => $params['isBot'] ?? null,
194
            'channels' => $params['channels'] ?? array_filter([$params['channel'] ?? null]),
195
            'users' => $params['users'] ?? array_filter([$params['user'] ?? null]),
196
            'patterns' => $params['patterns'] ?? array_filter([$params['pattern'] ?? null]),
197
            'method' => $params['method'] ?? $name,
198
        ];
199
    }
200
201
    protected function buildMethodMatchers(array $matchers) : array
202
    {
203
        $methodMatchers = [];
204
205
        foreach ($matchers as $name => $params) {
206
            $methodMatchers[] = new MethodMatcher(
207
                $name,
208
                $params['isBot'],
209
                $params['channels'],
210
                $params['users'],
211
                $this->validPatterns($params['patterns'], $name),
212
                $params['method'],
213
                $this->getLog()
214
            );
215
        }
216
217
        return $methodMatchers;
218
    }
219
220
    protected function validPatterns(array $patterns, $name) : array
221
    {
222
        $valid = [];
223
        foreach ($patterns as $pattern) {
224
            if ($this->isValidRegExp($pattern, $name)) {
225
                $valid[] = $pattern;
226
            }
227
        }
228
        return $valid;
229
    }
230
231
    protected function isValidRegExp($pattern, $name) : bool
232
    {
233
        try {
234
            preg_match($pattern, '');
235
            return true;
236
        } catch (Throwable $e) {
237
            $this->warning("$name.pattern='$pattern' ".$e->getMessage());
238
            return false;
239
        }
240
    }
241
}