FeedHandler   F
last analyzed

Complexity

Total Complexity 87

Size/Duplication

Total Lines 389
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 238
c 5
b 0
f 0
dl 0
loc 389
rs 2
wmc 87

17 Methods

Rating   Name   Duplication   Size   Complexity  
A feedSelectBox() 0 17 5
A subFeedUrl() 0 9 2
A strrposDetect() 0 7 2
D getItems() 0 51 18
A rssTimeStamp() 0 3 1
A buildFeed() 0 5 1
A getActivatedSubfeeds() 0 19 5
A sortTimestamp() 0 7 3
A cleanupChars() 0 14 6
A __construct() 0 13 1
A substrDetect() 0 7 2
A checkSubFeed() 0 17 4
B getSticky() 0 26 9
B doSubstr() 0 26 9
C getChannel() 0 51 16
A specUrl() 0 7 2
A wrapCdata() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like FeedHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FeedHandler, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace XoopsModules\Rssfit;
6
7
/*
8
 * You may not change or alter any portion of this comment or credits
9
 * of supporting developers from this source code or any supporting source code
10
 * which is considered copyrighted (c) material of the original comment or credit authors.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
 */
16
17
/**
18
 * @copyright    XOOPS Project (https://xoops.org)
19
 * @license      GNU GPL 2 or later (https://www.gnu.org/licenses/gpl-2.0.html)
20
 * @package      RSSFit - Extendable XML news feed generator
21
 * @author       NS Tai (aka tuff) <http://www.brandycoke.com>
22
 * @author       XOOPS Development Team
23
 */
24
25
if (!\defined('RSSFIT_ROOT_PATH')) {
26
    exit();
27
}
28
29
/**
30
 * Class FeedHandler
31
 * @package XoopsModules\Rssfit
32
 */
33
class FeedHandler
34
{
35
    public $rssmod;
36
    public $pluginHandler;
37
    public $miscHandler;
38
    public $helper;
39
    public $channelreq;
40
    public $subHandler;
41
    public $pluginObject;
42
    public $myts;
43
    public $modConfig;
44
    public $xoopsConfig;
45
    public $cached        = '';
46
    public $charset       = _CHARSET;
47
    public $feedkey       = 'feed';
48
    public $pluginFile    = '%s.php';
49
    public $substrRemove  = [',', '/', ';', ':', '(', '{', '[', ' '];
50
    public $substrAdd     = ['.', '!', '?', '}', ']', ')', '%'];
51
    public $substrEndwith = '...';
52
    public $specUrl       = 'http://blogs.law.harvard.edu/tech/rss';
53
    public $specs         = [
54
        'req'   => 'requiredChannelElements',
55
        'opt'   => 'optionalChannelElements',
56
        'cloud' => 'ltcloudgtSubelementOfLtchannelgt',
57
        'img'   => 'ltimagegtSubelementOfLtchannelgt',
58
    ];
59
    public $escaped       = [
60
        128 => '&#8364;',
61
        130 => '&#8218;',
62
        131 => '&#402;',
63
        132 => '&#8222;',
64
        133 => '&#8230;',
65
        134 => '&#8224;',
66
        135 => '&#8225;',
67
        136 => '&#710;',
68
        137 => '&#8240;',
69
        138 => '&#352;',
70
        139 => '&#8249;',
71
        140 => '&#338;',
72
        142 => '&#381;',
73
        145 => '&#8216;',
74
        146 => '&#8217;',
75
        147 => '&#8220;',
76
        148 => '&#8221;',
77
        149 => '&#8226;',
78
        150 => '&#8211;',
79
        151 => '&#8212;',
80
        152 => '&#732;',
81
        153 => '&#8482;',
82
        154 => '&#353;',
83
        155 => '&#8250;',
84
        156 => '&#339;',
85
        158 => '&#382;',
86
        159 => '&#376;',
87
    ];
88
89
    /**
90
     * FeedHandler constructor.
91
     */
92
    public function __construct(array $modConfig, array $xoopsConfig, \XoopsModule $xoopsModule)
93
    {
94
        $this->myts          = \MyTextSanitizer::getInstance();
95
        $this->rssmod        = $xoopsModule;
96
        $this->helper        = Helper::getInstance();
97
        $this->pluginHandler = Helper::getInstance()->getHandler('Plugin');
98
        $this->miscHandler   = Helper::getInstance()->getHandler('Misc');
99
        $this->modConfig     = $modConfig;
100
        $this->xoopsConfig   = $xoopsConfig;
101
        $this->channelreq    = [
102
            'title'       => $this->xoopsConfig['sitename'],
103
            'link'        => XOOPS_URL,
104
            'description' => $this->xoopsConfig['slogan'],
105
        ];
106
    }
107
108
    public function getChannel(array &$feed): void
109
    {
110
        $channel  = [];
111
        $elements = $this->miscHandler->getObjects2(new \Criteria('misc_category', 'channel'));
112
        if (\is_array($elements) && !empty($elements)) {
113
            foreach ($elements as $e) {
114
                if ('' !== $e->getVar('misc_content')) {
115
                    $channel[$e->getVar('misc_title')] = $e->getVar('misc_content', 'n');
116
                }
117
            }
118
            $channel['language']      = _LANGCODE;
119
            $channel['lastBuildDate'] = $this->rssTimeStamp(\time());
120
            if ($this->modConfig['cache']) {
121
                $channel['ttl'] = (string)$this->modConfig['cache'];
122
            }
123
        }
124
        if (!empty($feed['plugin'])) {
125
            if (\is_object($this->pluginObject) && \is_object($this->subHandler)) {
126
                $channel['title']       = $this->pluginObject->getVar('sub_title', 'n');
127
                $channel['link']        = $this->pluginObject->getVar('sub_link', 'n');
128
                $channel['description'] = $this->pluginObject->getVar('sub_desc', 'n');
129
                $image                  = [
130
                    'url'   => $this->pluginObject->getVar('img_url', 'n'),
131
                    'title' => $this->pluginObject->getVar('img_title', 'n'),
132
                    'link'  => $this->pluginObject->getVar('img_link', 'n'),
133
                ];
134
            }
135
        } else {
136
            $img = $this->miscHandler->getObjects2(new \Criteria('misc_category', 'channelimg'), '*', 'title');
137
            if ($img) {
138
                $image = [
139
                    'url'   => $img['url']->getVar('misc_content', 'n'),
140
                    'title' => $img['title']->getVar('misc_content', 'n'),
141
                    'link'  => $img['link']->getVar('misc_content', 'n'),
142
                ];
143
            }
144
        }
145
        if (empty($channel['title']) || empty($channel['link']) || empty($channel['description'])) {
146
            $channel = \array_merge($channel, $this->channelreq);
147
        }
148
        foreach ($channel as $k => $v) {
149
150
            $this->cleanupChars($channel[$k]);
151
        }
152
        if (!empty($image)) {
153
            foreach ($image as $k => $v) {
154
                $this->cleanupChars($image[$k]);
155
            }
156
            $feed['image'] = &$image;
157
        }
158
        $feed['channel'] = &$channel;
159
    }
160
161
    public function getSticky(array &$feed): bool
162
    {
163
        if (!$intr = $this->miscHandler->getObjects2(new \Criteria('misc_category', 'sticky'))) {
164
            return false;
165
        }
166
        $sticky = &$intr[0];
167
        unset($intr);
168
        $setting = $sticky->getVar('misc_setting');
169
        if (\in_array(0, $setting['feeds']) || '' === $sticky->getVar('misc_title') || '' === $sticky->getVar('misc_content')) {
170
            return false;
171
        }
172
        if ((\in_array(-1, $setting['feeds']) && empty($feed['plugin']))
173
            || (!empty($feed['plugin']) && \in_array($this->pluginObject->getVar('rssf_conf_id'), $setting['feeds']))) {
174
            $feed['sticky']['title'] = $sticky->getVar('misc_title', 'n');
175
            $feed['sticky']['link']  = $setting['link'];
176
            $sticky->setDoHtml((bool)$setting['dohtml']);
177
            $sticky->setDoBr((bool)$setting['dobr']);
178
            $feed['sticky']['description'] = $sticky->getVar('misc_content');
179
            $this->cleanupChars($feed['sticky']['title']);
180
            $this->cleanupChars($feed['sticky']['link']);
181
            $this->cleanupChars($feed['sticky']['description'], !$setting['dohtml'], false);
182
            $this->wrapCdata($feed['sticky']['description']);
183
            $feed['sticky']['pubdate'] = $this->rssTimeStamp(\time());
184
        }
185
186
        return true;
187
    }
188
189
    public function getItems(array &$feed): void
190
    {
191
        $entries = [];
192
        $db      = \XoopsDatabaseFactory::getDatabaseConnection();
193
        if (!empty($feed['plugin'])) {
194
            $this->pluginObject->setVar('rssf_grab', $this->pluginObject->getVar('sub_entries'));
195
            $this->subHandler->grab = $this->pluginObject->getVar('sub_entries');
196
            $grab                   = $this->subHandler->grabEntries($db);
197
            if (null !== $grab && \count($grab) > 0) {
198
                foreach ($grab as $g) {
199
                    $entries[] = $g;
200
                }
201
            }
202
        } elseif ($plugins = $this->pluginHandler->getObjects2(new \Criteria('rssf_activated', '1'))) {
203
            foreach ($plugins as $p) {
204
                $handler = $this->pluginHandler->checkPlugin($p);
205
                if ($handler) {
206
                    $handler->grab = $p->getVar('rssf_grab');
207
                    $grab          = $handler->grabEntries($db);
208
                    if (null !== $grab && \count($grab) > 0) {
209
                        foreach ($grab as $g) {
210
                            $entries[] = $g;
211
                        }
212
                    }
213
                }
214
            }
215
        }
216
        if (\count($entries) > 0) {
217
            foreach ($entries as $i => &$iValue) {
218
                $this->cleanupChars($iValue['title']);
219
                $strip = (bool)$this->modConfig['strip_html'];
220
                $this->cleanupChars($iValue['description'], $strip, false, true);
221
                $this->wrapCdata($iValue['description']);
222
                $entries[$i]['category'] = $this->myts->undoHtmlSpecialChars($iValue['category']);
223
                $this->cleanupChars($iValue['category']);
224
                if (!isset($iValue['timestamp'])) {
225
                    $entries[$i]['timestamp'] = $this->rssmod->getVar('last_update');
226
                }
227
                $entries[$i]['pubdate'] = $this->rssTimeStamp((int)$iValue['timestamp']);
228
            }
229
            unset($iValue);
230
231
            if (empty($feed['plugin']) && 'd' === $this->modConfig['sort']) {
232
                \uasort($entries, [$this, 'sortTimestamp']);
233
            }
234
            if (empty($feed['plugin']) && \count($entries) > $this->modConfig['overall_entries']) {
235
                $entries = \array_slice($entries, 0, $this->modConfig['overall_entries']);
236
            }
237
        }
238
239
        $feed['items'] = &$entries;
240
    }
241
242
    public function doSubstr(string $text): string
243
    {
244
        $ret      = $text;
245
        $len      = \function_exists('mb_strlen') ? mb_strlen($ret, $this->charset) : mb_strlen($ret);
246
        $maxChars = $this->helper->getConfig('max_char');
247
        if ($len > $maxChars && $maxChars > 0) {
248
            $ret = $this->substrDetect($ret, 0, $maxChars - 1);
249
            if (false === $this->strrposDetect($ret, ' ')) {
250
                if (false !== $this->strrposDetect($text, ' ')) {
251
                    $ret = $this->substrDetect($text, 0, (int)mb_strpos($text, ' '));
252
                }
253
            }
254
            if (\in_array($this->substrDetect($text, $maxChars - 1, 1), $this->substrAdd)) {
255
                $ret .= $this->substrDetect($text, $maxChars - 1, 1);
256
            } else {
257
                if (false !== $this->strrposDetect($ret, ' ')) {
258
                    $ret = $this->substrDetect($ret, 0, $this->strrposDetect($ret, ' '));
259
                }
260
                if (\in_array($this->substrDetect($ret, -1, 1), $this->substrRemove)) {
261
                    $ret = $this->substrDetect($ret, 0, -1);
262
                }
263
            }
264
            $ret .= $this->substrEndwith;
265
        }
266
267
        return $ret;
268
    }
269
270
    public function substrDetect(string $text, int $start, int $len): string
271
    {
272
        if (\function_exists('mb_strcut')) {
273
            return mb_strcut($text, $start, $len, _CHARSET);
274
        }
275
276
        return mb_substr($text, $start, $len);
277
    }
278
279
    /**
280
     * @return false|int
281
     */
282
    public function strrposDetect(string $text, string $find)
283
    {
284
        if (\function_exists('mb_strrpos')) {
285
            return mb_strrpos($text, $find, 0, _CHARSET);
286
        }
287
288
        return mb_strrpos($text, $find);
289
    }
290
291
    public function rssTimeStamp(int $time): ?string
292
    {
293
        return \date('D, j M Y H:i:s O', $time);
294
    }
295
296
    public function sortTimestamp(array $a, array $b): int
297
    {
298
        if ($a['timestamp'] === $b['timestamp']) {
299
            return 0;
300
        }
301
302
        return ($a['timestamp'] > $b['timestamp']) ? -1 : 1;
303
    }
304
305
    public function cleanupChars(string &$text, bool $strip = true, bool $dospec = true, bool $dosub = false): void
306
    {
307
        if ($strip) {
308
            $text = \strip_tags($text);
309
        }
310
        if ($dosub) {
311
            $text = $this->doSubstr($text);
312
        }
313
        if ($dospec) {
314
            $text = \htmlspecialchars($text, \ENT_QUOTES, $this->charset);
315
            $text = \preg_replace('/&amp;(#\d+);/i', '&$1;', $text);
316
        }
317
        if (XOOPS_USE_MULTIBYTES !== 1 || false === stripos($this->charset, "utf-8")) {
0 ignored issues
show
introduced by
The condition XoopsModules\Rssfit\XOOPS_USE_MULTIBYTES !== 1 is always true.
Loading history...
318
            $text = \str_replace(\array_map('\chr', \array_keys($this->escaped)), $this->escaped, $text);
319
        }
320
    }
321
322
    public function wrapCdata(string &$text): void
323
    {
324
        $text = '<![CDATA[' . \str_replace(['<![CDATA[', ']]>'], ['&lt;![CDATA[', ']]&gt;'], $text) . ']]>';
325
    }
326
327
    public function &getActivatedSubfeeds(string $fields = '', string $type = ''): ?array
328
    {
329
        $ret  = null;
330
        $subs = $this->pluginHandler->getObjects2(new \Criteria('subfeed', '1'), $fields);
331
        if (\is_array($subs) && !empty($subs)) {
332
            $ret = [];
333
            switch ($type) {
334
                default:
335
                    $ret = &$subs;
336
                    break;
337
                case 'list':
338
                    foreach ($subs as $s) {
339
                        $ret[$s->getVar('rssf_conf_id')] = $s->getVar('sub_title');
340
                    }
341
                    break;
342
            }
343
        }
344
345
        return $ret;
346
    }
347
348
    /**
349
     * @param null|string|array $selected
350
     */
351
    public function feedSelectBox(string $caption = '', $selected = null, int $size = 1, bool $multi = true, bool $none = true, bool $main = true, string $name = 'feeds', string $type = 'id'): \XoopsFormSelect
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

351
    public function feedSelectBox(string $caption = '', $selected = null, int $size = 1, bool $multi = true, bool $none = true, bool $main = true, string $name = 'feeds', /** @scrutinizer ignore-unused */ string $type = 'id'): \XoopsFormSelect

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
352
    {
353
        $select = new \XoopsFormSelect($caption, $name, $selected, $size, $multi);
354
        if ($none) {
355
            $select->addOption('0', '-------');
356
        }
357
        if ($main) {
358
            $select->addOption('-1', \_AM_RSSFIT_MAINFEED);
359
        }
360
        $subs = $this->getActivatedSubfeeds('sublist', 'list');
361
        if ($subs) {
362
            foreach ($subs as $k => $v) {
363
                $select->addOption($k, $v);
364
            }
365
        }
366
367
        return $select;
368
    }
369
370
    /**
371
     * @return string
372
     */
373
    public function specUrl(string $key = '0'): ?string
374
    {
375
        if (isset($this->specs[$key])) {
376
            return $this->specUrl . '#' . $this->specs[$key];
377
        }
378
379
        return null;
380
    }
381
382
    /**
383
     * @return string
384
     */
385
    public function subFeedUrl(string $filename = ''): ?string
386
    {
387
        if (!empty($filename)) {
388
            $filename = \str_replace(['rssfit.', '.php'], '', $filename);
389
390
            return \RSSFIT_URL_FEED . '?' . $this->feedkey . '=' . $filename;
0 ignored issues
show
Bug introduced by
The constant RSSFIT_URL_FEED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
391
        }
392
393
        return null;
394
    }
395
396
    public function checkSubFeed(array &$feed): void
397
    {
398
        if (!empty($feed['plugin'])) {
399
            $criteria = new \CriteriaCompo();
400
            $criteria->add(new \Criteria('rssf_filename', \sprintf($this->pluginFile, $feed['plugin'])));
401
            $criteria->add(new \Criteria('subfeed', '1'));
402
            $sub     = $this->pluginHandler->getObjects2($criteria);
403
            $handler = null;
404
            if (isset($sub[0])) {
405
                $handler = $this->pluginHandler->checkPlugin($sub[0]);
406
            }
407
            if ($handler) {
408
                $this->pluginObject = $sub[0];
409
                $this->subHandler   = $handler;
410
                $this->cached       = 'mod_' . $this->rssmod->getVar('dirname') . '|' . \md5(\str_replace(XOOPS_URL, '', $GLOBALS['xoopsRequestUri']));
411
            } else {
412
                $feed['plugin'] = '';
413
            }
414
        }
415
    }
416
417
    public function buildFeed(array &$feed): void
418
    {
419
        $this->getChannel($feed);
420
        $this->getItems($feed);
421
        $this->getSticky($feed);
422
    }
423
}
424