PluginHost::load()   F
last analyzed

Complexity

Conditions 24
Paths 160

Size

Total Lines 89
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 24
eloc 52
c 0
b 0
f 0
nc 160
nop 4
dl 0
loc 89
rs 3.6666

How to fix   Long Method    Complexity   

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
class PluginHost {
3
    private $pdo;
4
    private $hooks = array();
5
    private $plugins = array();
6
    private $handlers = array();
7
    private $commands = array();
8
    private $storage = array();
9
    private $feeds = array();
10
    private $api_methods = array();
11
    private $plugin_actions = array();
12
    private $owner_uid;
13
    private $last_registered;
14
    private static $instance;
15
16
    const API_VERSION = 2;
17
18
    // Hooks marked with *1 are run in global context and available
19
    // to plugins loaded in config.php only
20
21
    const HOOK_ARTICLE_BUTTON = 1;
22
    const HOOK_ARTICLE_FILTER = 2;
23
    const HOOK_PREFS_TAB = 3;
24
    const HOOK_PREFS_TAB_SECTION = 4;
25
    const HOOK_PREFS_TABS = 5;
26
    const HOOK_FEED_PARSED = 6;
27
    const HOOK_UPDATE_TASK = 7; // *1
28
    const HOOK_AUTH_USER = 8;
29
    const HOOK_HOTKEY_MAP = 9;
30
    const HOOK_RENDER_ARTICLE = 10;
31
    const HOOK_RENDER_ARTICLE_CDM = 11;
32
    const HOOK_FEED_FETCHED = 12;
33
    const HOOK_SANITIZE = 13;
34
    const HOOK_RENDER_ARTICLE_API = 14;
35
    const HOOK_TOOLBAR_BUTTON = 15;
36
    const HOOK_ACTION_ITEM = 16;
37
    const HOOK_HEADLINE_TOOLBAR_BUTTON = 17;
38
    const HOOK_HOTKEY_INFO = 18;
39
    const HOOK_ARTICLE_LEFT_BUTTON = 19;
40
    const HOOK_PREFS_EDIT_FEED = 20;
41
    const HOOK_PREFS_SAVE_FEED = 21;
42
    const HOOK_FETCH_FEED = 22;
43
    const HOOK_QUERY_HEADLINES = 23;
44
    const HOOK_HOUSE_KEEPING = 24; // *1
45
    const HOOK_SEARCH = 25;
46
    const HOOK_FORMAT_ENCLOSURES = 26;
47
    const HOOK_SUBSCRIBE_FEED = 27;
48
    const HOOK_HEADLINES_BEFORE = 28;
49
    const HOOK_RENDER_ENCLOSURE = 29;
50
    const HOOK_ARTICLE_FILTER_ACTION = 30;
51
    const HOOK_ARTICLE_EXPORT_FEED = 31;
52
    const HOOK_MAIN_TOOLBAR_BUTTON = 32;
53
    const HOOK_ENCLOSURE_ENTRY = 33;
54
    const HOOK_FORMAT_ARTICLE = 34;
55
    const HOOK_FORMAT_ARTICLE_CDM = 35; /* RIP */
56
    const HOOK_FEED_BASIC_INFO = 36;
57
    const HOOK_SEND_LOCAL_FILE = 37;
58
    const HOOK_UNSUBSCRIBE_FEED = 38;
59
    const HOOK_SEND_MAIL = 39;
60
    const HOOK_FILTER_TRIGGERED = 40;
61
    const HOOK_GET_FULL_TEXT = 41;
62
    const HOOK_ARTICLE_IMAGE = 42;
63
    const HOOK_FEED_TREE = 43;
64
    const HOOK_IFRAME_WHITELISTED = 44;
65
66
    const KIND_ALL = 1;
67
    const KIND_SYSTEM = 2;
68
    const KIND_USER = 3;
69
70
    public static function object_to_domain($plugin) {
71
        return strtolower(get_class($plugin));
72
    }
73
74
    public function __construct() {
75
        $this->pdo = Db::pdo();
76
77
        $this->storage = array();
78
    }
79
80
    private function __clone() {
81
        //
82
    }
83
84
    public static function getInstance() {
85
        if (self::$instance == null) {
86
                    self::$instance = new self();
87
        }
88
89
        return self::$instance;
90
    }
91
92
    private function register_plugin($name, $plugin) {
93
        //array_push($this->plugins, $plugin);
94
        $this->plugins[$name] = $plugin;
95
    }
96
97
    // needed for compatibility with API 1
98
    public function get_link() {
99
        return false;
100
    }
101
102
    public function get_dbh() {
103
        return Db::get();
104
    }
105
106
    public function get_pdo() {
107
        return $this->pdo;
108
    }
109
110
    public function get_plugin_names() {
111
        $names = array();
112
113
        foreach ($this->plugins as $p) {
114
            array_push($names, get_class($p));
115
        }
116
117
        return $names;
118
    }
119
120
    public function get_plugins() {
121
        return $this->plugins;
122
    }
123
124
    public function get_plugin($name) {
125
        return $this->plugins[strtolower($name)];
126
    }
127
128
    public function run_hooks($type, $method, $args) {
129
        foreach ($this->get_hooks($type) as $hook) {
130
            $hook->$method($args);
131
        }
132
    }
133
134
    public function add_hook($type, $sender, $priority = 50) {
135
        $priority = (int) $priority;
136
137
        if (!is_array($this->hooks[$type])) {
138
            $this->hooks[$type] = [];
139
        }
140
141
        if (!is_array($this->hooks[$type][$priority])) {
142
            $this->hooks[$type][$priority] = [];
143
        }
144
145
        array_push($this->hooks[$type][$priority], $sender);
146
        ksort($this->hooks[$type]);
147
    }
148
149
    public function del_hook($type, $sender) {
150
        if (is_array($this->hooks[$type])) {
151
            foreach (array_keys($this->hooks[$type]) as $prio) {
152
                $key = array_search($sender, $this->hooks[$type][$prio]);
153
154
                if ($key !== false) {
155
                    unset($this->hooks[$type][$prio][$key]);
156
                }
157
            }
158
        }
159
    }
160
161
    public function get_hooks($type) {
162
        if (isset($this->hooks[$type])) {
163
            $tmp = [];
164
165
            foreach (array_keys($this->hooks[$type]) as $prio) {
166
                $tmp = array_merge($tmp, $this->hooks[$type][$prio]);
167
            }
168
169
            return $tmp;
170
        } else {
171
            return [];
172
        }
173
    }
174
    public function load_all($kind, $owner_uid = false, $skip_init = false) {
175
176
        $plugins = array_merge(glob("plugins/*"), glob("plugins.local/*"));
0 ignored issues
show
Bug introduced by
It seems like glob('plugins/*') can also be of type false; however, parameter $array1 of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
        $plugins = array_merge(/** @scrutinizer ignore-type */ glob("plugins/*"), glob("plugins.local/*"));
Loading history...
Bug introduced by
It seems like glob('plugins.local/*') can also be of type false; however, parameter $array2 of array_merge() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
        $plugins = array_merge(glob("plugins/*"), /** @scrutinizer ignore-type */ glob("plugins.local/*"));
Loading history...
177
        $plugins = array_filter($plugins, "is_dir");
178
        $plugins = array_map("basename", $plugins);
179
180
        asort($plugins);
181
182
        $this->load(join(",", $plugins), $kind, $owner_uid, $skip_init);
183
    }
184
185
    public function load($classlist, $kind, $owner_uid = false, $skip_init = false) {
186
        $plugins = explode(",", $classlist);
187
188
        $this->owner_uid = (int) $owner_uid;
189
190
        foreach ($plugins as $class) {
191
            $class = trim($class);
192
            $class_file = strtolower(clean_filename($class));
193
194
            if (!is_dir(__DIR__."/../plugins/$class_file") &&
195
                    !is_dir(__DIR__."/../plugins.local/$class_file")) {
196
                continue;
197
            }
198
199
            // try system plugin directory first
200
            $file = __DIR__."/../plugins/$class_file/init.php";
201
            $vendor_dir = __DIR__."/../plugins/$class_file/vendor";
202
203
            if (!file_exists($file)) {
204
                $file = __DIR__."/../plugins.local/$class_file/init.php";
205
                $vendor_dir = __DIR__."/../plugins.local/$class_file/vendor";
206
            }
207
208
            if (!isset($this->plugins[$class])) {
209
                if (file_exists($file)) {
210
                    require_once $file;
211
                }
212
213
                if (class_exists($class) && is_subclass_of($class, "Plugin")) {
214
215
                    // register plugin autoloader if necessary, for namespaced classes ONLY
216
                    // layout corresponds to tt-rss main /vendor/author/Package/Class.php
217
218
                    if (file_exists($vendor_dir)) {
219
                        spl_autoload_register(function($class) use ($vendor_dir) {
220
221
                            if (strpos($class, '\\') !== false) {
222
                                list ($namespace, $class_name) = explode('\\', $class, 2);
223
224
                                if ($namespace && $class_name) {
225
                                    $class_file = "$vendor_dir/$namespace/".str_replace('\\', '/', $class_name).".php";
226
227
                                    if (file_exists($class_file)) {
228
                                                                            require_once $class_file;
229
                                    }
230
                                }
231
                            }
232
                        });
233
                    }
234
235
                    $plugin = new $class($this);
236
237
                    $plugin_api = $plugin->api_version();
238
239
                    if ($plugin_api < PluginHost::API_VERSION) {
240
                        user_error("plugin $class is not compatible with current API version (need: ".PluginHost::API_VERSION.", got: $plugin_api)", E_USER_WARNING);
241
                        continue;
242
                    }
243
244
                    if (file_exists(dirname($file)."/locale")) {
245
                        _bindtextdomain($class, dirname($file)."/locale");
246
                        _bind_textdomain_codeset($class, "UTF-8");
247
                    }
248
249
                    $this->last_registered = $class;
250
251
                    switch ($kind) {
252
                    case $this::KIND_SYSTEM:
253
                        if ($this->is_system($plugin)) {
254
                            if (!$skip_init) {
255
                                $plugin->init($this);
256
                            }
257
                            $this->register_plugin($class, $plugin);
258
                        }
259
                        break;
260
                    case $this::KIND_USER:
261
                        if (!$this->is_system($plugin)) {
262
                            if (!$skip_init) {
263
                                $plugin->init($this);
264
                            }
265
                            $this->register_plugin($class, $plugin);
266
                        }
267
                        break;
268
                    case $this::KIND_ALL:
269
                        if (!$skip_init) {
270
                            $plugin->init($this);
271
                        }
272
                        $this->register_plugin($class, $plugin);
273
                        break;
274
                    }
275
                }
276
            }
277
        }
278
    }
279
280
    public function is_system($plugin) {
281
        $about = $plugin->about();
282
283
        return @$about[3];
284
    }
285
286
    // only system plugins are allowed to modify routing
287
    public function add_handler($handler, $method, $sender) {
288
        $handler = str_replace("-", "_", strtolower($handler));
289
        $method = strtolower($method);
290
291
        if ($this->is_system($sender)) {
292
            if (!is_array($this->handlers[$handler])) {
293
                $this->handlers[$handler] = array();
294
            }
295
296
            $this->handlers[$handler][$method] = $sender;
297
        }
298
    }
299
300
    public function del_handler($handler, $method, $sender) {
301
        $handler = str_replace("-", "_", strtolower($handler));
302
        $method = strtolower($method);
303
304
        if ($this->is_system($sender)) {
305
            unset($this->handlers[$handler][$method]);
306
        }
307
    }
308
309
    public function lookup_handler($handler, $method) {
310
        $handler = str_replace("-", "_", strtolower($handler));
311
        $method = strtolower($method);
312
313
        if (is_array($this->handlers[$handler])) {
314
            if (isset($this->handlers[$handler]["*"])) {
315
                return $this->handlers[$handler]["*"];
316
            } else {
317
                return $this->handlers[$handler][$method];
318
            }
319
        }
320
321
        return false;
322
    }
323
324
    public function add_command($command, $description, $sender, $suffix = "", $arghelp = "") {
325
        $command = str_replace("-", "_", strtolower($command));
326
327
        $this->commands[$command] = array("description" => $description,
328
            "suffix" => $suffix,
329
            "arghelp" => $arghelp,
330
            "class" => $sender);
331
    }
332
333
    public function del_command($command) {
334
        $command = "-".strtolower($command);
335
336
        unset($this->commands[$command]);
337
    }
338
339
    public function lookup_command($command) {
340
        $command = "-".strtolower($command);
341
342
        if (is_array($this->commands[$command])) {
343
            return $this->commands[$command]["class"];
344
        } else {
345
            return false;
346
        }
347
    }
348
349
    public function get_commands() {
350
        return $this->commands;
351
    }
352
353
    public function run_commands($args) {
354
        foreach ($this->get_commands() as $command => $data) {
355
            if (isset($args[$command])) {
356
                $command = str_replace("-", "", $command);
357
                $data["class"]->$command($args);
358
            }
359
        }
360
    }
361
362
    public function load_data() {
363
        if ($this->owner_uid) {
364
            $sth = $this->pdo->prepare("SELECT name, content FROM ttrss_plugin_storage
365
				WHERE owner_uid = ?");
366
            $sth->execute([$this->owner_uid]);
367
368
            while ($line = $sth->fetch()) {
369
                $this->storage[$line["name"]] = unserialize($line["content"]);
370
            }
371
        }
372
    }
373
374
    private function save_data($plugin) {
375
        if ($this->owner_uid) {
376
            $this->pdo->beginTransaction();
377
378
            $sth = $this->pdo->prepare("SELECT id FROM ttrss_plugin_storage WHERE
379
				owner_uid= ? AND name = ?");
380
            $sth->execute([$this->owner_uid, $plugin]);
381
382
            if (!isset($this->storage[$plugin])) {
383
                            $this->storage[$plugin] = array();
384
            }
385
386
            $content = serialize($this->storage[$plugin]);
387
388
            if ($sth->fetch()) {
389
                $sth = $this->pdo->prepare("UPDATE ttrss_plugin_storage SET content = ?
390
					WHERE owner_uid= ? AND name = ?");
391
                $sth->execute([(string) $content, $this->owner_uid, $plugin]);
392
393
            } else {
394
                $sth = $this->pdo->prepare("INSERT INTO ttrss_plugin_storage
395
					(name,owner_uid,content) VALUES
396
					(?, ?, ?)");
397
                $sth->execute([$plugin, $this->owner_uid, (string) $content]);
398
            }
399
400
            $this->pdo->commit();
401
        }
402
    }
403
404
    public function set($sender, $name, $value, $sync = true) {
405
        $idx = get_class($sender);
406
407
        if (!isset($this->storage[$idx])) {
408
                    $this->storage[$idx] = array();
409
        }
410
411
        $this->storage[$idx][$name] = $value;
412
413
        if ($sync) {
414
            $this->save_data(get_class($sender));
415
        }
416
    }
417
418
    public function get($sender, $name, $default_value = false) {
419
        $idx = get_class($sender);
420
421
        if (isset($this->storage[$idx][$name])) {
422
            return $this->storage[$idx][$name];
423
        } else {
424
            return $default_value;
425
        }
426
    }
427
428
    public function get_all($sender) {
429
        $idx = get_class($sender);
430
431
        $data = $this->storage[$idx];
432
433
        return $data ? $data : [];
434
    }
435
436
    public function clear_data($sender) {
437
        if ($this->owner_uid) {
438
            $idx = get_class($sender);
439
440
            unset($this->storage[$idx]);
441
442
            $sth = $this->pdo->prepare("DELETE FROM ttrss_plugin_storage WHERE name = ?
443
				AND owner_uid = ?");
444
            $sth->execute([$idx, $this->owner_uid]);
445
        }
446
    }
447
448
    // Plugin feed functions are *EXPERIMENTAL*!
449
450
    // cat_id: only -1 is supported (Special)
451
    public function add_feed($cat_id, $title, $icon, $sender) {
452
        if (!$this->feeds[$cat_id]) {
453
            $this->feeds[$cat_id] = array();
454
        }
455
456
        $id = count($this->feeds[$cat_id]);
457
458
        array_push($this->feeds[$cat_id],
459
            array('id' => $id, 'title' => $title, 'sender' => $sender, 'icon' => $icon));
460
461
        return $id;
462
    }
463
464
    public function get_feeds($cat_id) {
465
        return $this->feeds[$cat_id];
466
    }
467
468
    // convert feed_id (e.g. -129) to pfeed_id first
469
    public function get_feed_handler($pfeed_id) {
470
        foreach ($this->feeds as $cat) {
471
            foreach ($cat as $feed) {
472
                if ($feed['id'] == $pfeed_id) {
473
                    return $feed['sender'];
474
                }
475
            }
476
        }
477
    }
478
479
    public static function pfeed_to_feed_id($label) {
480
        return PLUGIN_FEED_BASE_INDEX - 1 - abs($label);
481
    }
482
483
    public static function feed_to_pfeed_id($feed) {
484
        return PLUGIN_FEED_BASE_INDEX - 1 + abs($feed);
485
    }
486
487
    public function add_api_method($name, $sender) {
488
        if ($this->is_system($sender)) {
489
            $this->api_methods[strtolower($name)] = $sender;
490
        }
491
    }
492
493
    public function get_api_method($name) {
494
        return $this->api_methods[$name];
495
    }
496
497
    public function add_filter_action($sender, $action_name, $action_desc) {
498
        $sender_class = get_class($sender);
499
500
        if (!isset($this->plugin_actions[$sender_class])) {
501
                    $this->plugin_actions[$sender_class] = array();
502
        }
503
504
        array_push($this->plugin_actions[$sender_class],
505
            array("action" => $action_name, "description" => $action_desc, "sender" => $sender));
506
    }
507
508
    public function get_filter_actions() {
509
        return $this->plugin_actions;
510
    }
511
512
    public function get_owner_uid() {
513
        return $this->owner_uid;
514
    }
515
516
    // handled by classes/pluginhandler.php, requires valid session
517
    public function get_method_url($sender, $method, $params) {
518
        return get_self_url_prefix()."/backend.php?".
519
            http_build_query(
520
                array_merge(
521
                    [
522
                        "op" => "pluginhandler",
523
                        "plugin" => strtolower(get_class($sender)),
524
                        "method" => $method
525
                    ],
526
                    $params));
527
    }
528
529
    // WARNING: endpoint in public.php, exposed to unauthenticated users
530
    public function get_public_method_url($sender, $method, $params) {
531
        if ($sender->is_public_method($method)) {
532
            return get_self_url_prefix()."/public.php?".
533
                http_build_query(
534
                    array_merge(
535
                        [
536
                            "op" => "pluginhandler",
537
                            "plugin" => strtolower(get_class($sender)),
538
                            "pmethod" => $method
539
                        ],
540
                        $params));
541
        } else {
542
            user_error("get_public_method_url: requested method '$method' of '".get_class($sender)."' is private.");
543
        }
544
    }
545
}
546