Answer   B
last analyzed

Complexity

Total Complexity 51

Size/Duplication

Total Lines 376
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 51
eloc 192
c 1
b 0
f 0
dl 0
loc 376
rs 7.92

16 Methods

Rating   Name   Duplication   Size   Complexity  
A status() 0 3 1
A saveAttachment() 0 14 4
A __construct() 0 28 4
A datesub() 0 10 2
A faqid() 0 3 1
A answer() 0 3 1
C displayAttachment() 0 86 11
A notLoaded() 0 3 1
B deleteAttachment() 0 33 9
A sendNotifications() 0 31 4
A uid() 0 3 1
A incrementDownload() 0 8 2
A answerid() 0 3 1
A store() 0 10 2
A getAttachment() 0 13 3
A setAttachment() 0 20 4

How to fix   Complexity   

Complex Class

Complex classes like Answer 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 Answer, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Smartfaq;
4
5
/**
6
 * Module: SmartFAQ
7
 * Author: The SmartFactory <www.smartfactory.ca>
8
 * Licence: GNU
9
 */
10
11
use XoopsModules\Smartfaq;
12
13
/**
14
 * Class Answer
15
 */
16
class Answer extends \XoopsObject
17
{
18
    public $attachment_array = [];
19
    public $db;
20
21
    /**
22
     * constructor
23
     * @param int|null|array $id
24
     */
25
    public function __construct($id = null)
26
    {
27
        $this->db = \XoopsDatabaseFactory::getDatabaseConnection();
28
        $this->initVar('answerid', \XOBJ_DTYPE_INT, null, false);
29
        $this->initVar('status', \XOBJ_DTYPE_INT, -1, false);
30
        $this->initVar('faqid', \XOBJ_DTYPE_INT, null, false);
31
        $this->initVar('answer', \XOBJ_DTYPE_TXTAREA, null, true);
32
        $this->initVar('uid', \XOBJ_DTYPE_INT, 0, false);
33
        $this->initVar('datesub', \XOBJ_DTYPE_INT, null, false);
34
        $this->initVar('notifypub', \XOBJ_DTYPE_INT, 0, false);
35
36
        $this->initVar('attachment', \XOBJ_DTYPE_TXTAREA, '');
37
38
        $this->initVar('dohtml', \XOBJ_DTYPE_INT, 1, false);
39
        $this->initVar('doxcode', \XOBJ_DTYPE_INT, 1, false);
40
        $this->initVar('dosmiley', \XOBJ_DTYPE_INT, 1, false);
41
        $this->initVar('doimage', \XOBJ_DTYPE_INT, 0, false);
42
        $this->initVar('dobr', \XOBJ_DTYPE_INT, 1, false);
43
44
        // for backward compatibility
45
        if (isset($id)) {
46
            if (\is_array($id)) {
47
                $this->assignVars($id);
48
            } else {
49
                $answerHandler = new AnswerHandler($this->db);
50
                $answer        = $answerHandler->get($id);
51
                foreach ($answer->vars as $k => $v) {
52
                    $this->assignVar($k, $v['value']);
53
                }
54
            }
55
        }
56
    }
57
58
    // ////////////////////////////////////////////////////////////////////////////////////
59
    // attachment functions    TODO: there should be a file/attachment management class
60
61
    /**
62
     * @return array|mixed|null
63
     */
64
    public function getAttachment()
65
    {
66
        if (\count($this->attachment_array)) {
67
            return $this->attachment_array;
68
        }
69
        $attachment = $this->getVar('attachment');
70
        if (empty($attachment)) {
71
            $this->attachment_array = null;
72
        } else {
73
            $this->attachment_array = @\unserialize(\base64_decode($attachment, true));
74
        }
75
76
        return $this->attachment_array;
77
    }
78
79
    /**
80
     * @param $attach_key
81
     * @return bool
82
     */
83
    public function incrementDownload($attach_key)
84
    {
85
        if (!$attach_key) {
86
            return false;
87
        }
88
        $this->attachment_array[(string)$attach_key]['num_download']++;
89
90
        return $this->attachment_array[(string)$attach_key]['num_download'];
91
    }
92
93
    /**
94
     * @return bool
95
     */
96
    public function saveAttachment()
97
    {
98
        $attachment_save = '';
99
        if ($this->attachment_array && \is_array($this->attachment_array)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->attachment_array 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...
100
            $attachment_save = \base64_encode(\serialize($this->attachment_array));
101
        }
102
        $this->setVar('attachment', $attachment_save);
103
        $sql = 'UPDATE ' . $GLOBALS['xoopsDB']->prefix('smartfaq_answers') . ' SET attachment=' . $GLOBALS['xoopsDB']->quoteString($attachment_save) . ' WHERE post_id = ' . $this->getVar('answerid');
104
        if (!$result = $GLOBALS['xoopsDB']->queryF($sql)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
105
            //xoops_error($GLOBALS["xoopsDB"]->error());
106
            return false;
107
        }
108
109
        return true;
110
    }
111
112
    /**
113
     * @param null $attach_array
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $attach_array is correct as it would always require null to be passed?
Loading history...
114
     * @return bool
115
     */
116
    public function deleteAttachment($attach_array = null)
117
    {
118
        /** @var Smartfaq\Helper $helper */
119
        $helper = Smartfaq\Helper::getInstance();
120
121
        $attach_old = $this->getAttachment();
122
        if (!\is_array($attach_old) || \count($attach_old) < 1) {
123
            return true;
124
        }
125
        $this->attachment_array = [];
126
127
        if (null === $attach_array) {
0 ignored issues
show
introduced by
The condition null === $attach_array is always true.
Loading history...
128
            $attach_array = \array_keys($attach_old);
129
        } // to delete all!
130
        if (!\is_array($attach_array)) {
0 ignored issues
show
introduced by
The condition is_array($attach_array) is always true.
Loading history...
131
            $attach_array = [$attach_array];
132
        }
133
134
        foreach ($attach_old as $key => $attach) {
135
            if (\in_array($key, $attach_array, true)) {
136
                @\unlink(XOOPS_ROOT_PATH . '/' . $helper->getConfig('dir_attachments') . '/' . $attach['name_saved']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

136
                /** @scrutinizer ignore-unhandled */ @\unlink(XOOPS_ROOT_PATH . '/' . $helper->getConfig('dir_attachments') . '/' . $attach['name_saved']);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
137
                @\unlink(XOOPS_ROOT_PATH . '/' . $helper->getConfig('dir_attachments') . '/thumbs/' . $attach['name_saved']); // delete thumbnails
138
                continue;
139
            }
140
            $this->attachment_array[$key] = $attach;
141
        }
142
        $attachment_save = '';
143
        if ($this->attachment_array && \is_array($this->attachment_array)) {
144
            $attachment_save = \base64_encode(\serialize($this->attachment_array));
145
        }
146
        $this->setVar('attachment', $attachment_save);
147
148
        return true;
149
    }
150
151
    /**
152
     * @param string $name_saved
153
     * @param string $name_display
154
     * @param string $mimetype
155
     * @param int    $num_download
156
     * @return bool
157
     */
158
    public function setAttachment($name_saved = '', $name_display = '', $mimetype = '', $num_download = 0)
159
    {
160
        static $counter = 0;
161
        $this->attachment_array = $this->getAttachment();
162
        if ($name_saved) {
163
            $key                          = (string)(\time() + ($counter++));
164
            $this->attachment_array[$key] = [
165
                'name_saved'   => $name_saved,
166
                'name_display' => $name_display ?? $name_saved,
167
                'mimetype'     => $mimetype,
168
                'num_download' => isset($num_download) ? (int)$num_download : 0,
169
            ];
170
        }
171
        $attachment_save = null;
172
        if (\is_array($this->attachment_array)) {
173
            $attachment_save = \base64_encode(\serialize($this->attachment_array));
174
        }
175
        $this->setVar('attachment', $attachment_save);
176
177
        return true;
178
    }
179
180
    /**
181
     * TODO: refactor
182
     * @param bool $asSource
183
     * @return string
184
     */
185
    public function displayAttachment($asSource = false)
0 ignored issues
show
Unused Code introduced by
The parameter $asSource 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

185
    public function displayAttachment(/** @scrutinizer ignore-unused */ $asSource = false)

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...
186
    {
187
        global $xoopsModule;
188
        /** @var Smartfaq\Helper $helper */
189
        $helper = Smartfaq\Helper::getInstance();
190
191
        $post_attachment = '';
192
        $attachments     = $this->getAttachment();
193
        if ($attachments && \is_array($attachments)) {
194
            $iconHandler = \sf_getIconHandler();
195
            $mime_path   = $iconHandler->getPath('mime');
196
            require_once XOOPS_ROOT_PATH . '/modules/' . $xoopsModule->getVar('dirname', 'n') . '/include/functions.image.php';
197
            $image_extensions = ['jpg', 'jpeg', 'gif', 'png', 'bmp']; // need improve !!!
198
            $post_attachment  .= '<br><strong>' . _MD_ATTACHMENT . '</strong>:';
199
            $post_attachment  .= '<br><hr size="1" noshade="noshade"><br>';
200
            foreach ($attachments as $key => $att) {
201
                $file_extension = \ltrim(mb_strrchr($att['name_saved'], '.'), '.');
202
                $filetype       = $file_extension;
203
                if (\file_exists(XOOPS_ROOT_PATH . '/' . $mime_path . '/' . $filetype . '.gif')) {
204
                    $icon_filetype = XOOPS_URL . '/' . $mime_path . '/' . $filetype . '.gif';
205
                } else {
206
                    $icon_filetype = XOOPS_URL . '/' . $mime_path . '/unknown.gif';
207
                }
208
                $file_size = @\filesize(XOOPS_ROOT_PATH . '/' . $helper->getConfig('dir_attachments') . '/' . $att['name_saved']);
209
                $file_size = \number_format($file_size / 1024, 2) . ' KB';
210
                if ($helper->getConfig('media_allowed') && \in_array(mb_strtolower($file_extension), $image_extensions, true)) {
211
                    $post_attachment .= '<br><img src="' . $icon_filetype . '" alt="' . $filetype . '"><strong>&nbsp; ' . $att['name_display'] . '</strong> <small>(' . $file_size . ')</small>';
212
                    $post_attachment .= '<br>' . \sf_attachmentImage($att['name_saved']);
213
                    $isDisplayed     = true;
0 ignored issues
show
Unused Code introduced by
The assignment to $isDisplayed is dead and can be removed.
Loading history...
214
                } else {
215
                    global $xoopsUser;
216
                    if (empty($helper->getConfig('show_userattach'))) {
217
                        $post_attachment .= '<a href="'
218
                                            . XOOPS_URL
219
                                            . '/modules/'
220
                                            . $xoopsModule->getVar('dirname', 'n')
221
                                            . '/dl_attachment.php?attachid='
222
                                            . $key
223
                                            . '&amp;post_id='
224
                                            . $this->getVar('post_id')
225
                                            . '"> <img src="'
226
                                            . $icon_filetype
227
                                            . '" alt="'
228
                                            . $filetype
229
                                            . '"> '
230
                                            . $att['name_display']
231
                                            . '</a> '
232
                                            . _MD_FILESIZE
233
                                            . ': '
234
                                            . $file_size
235
                                            . '; '
236
                                            . _MD_HITS
237
                                            . ': '
238
                                            . $att['num_download'];
239
                    } elseif ($xoopsUser && $xoopsUser->uid() > 0 && $xoopsUser->isActive()) {
240
                        $post_attachment .= '<a href="'
241
                                            . XOOPS_URL
242
                                            . '/modules/'
243
                                            . $xoopsModule->getVar('dirname', 'n')
244
                                            . '/dl_attachment.php?attachid='
245
                                            . $key
246
                                            . '&amp;post_id='
247
                                            . $this->getVar('post_id')
248
                                            . '"> <img src="'
249
                                            . $icon_filetype
250
                                            . '" alt="'
251
                                            . $filetype
252
                                            . '"> '
253
                                            . $att['name_display']
254
                                            . '</a> '
255
                                            . _MD_FILESIZE
256
                                            . ': '
257
                                            . $file_size
258
                                            . '; '
259
                                            . _MD_HITS
260
                                            . ': '
261
                                            . $att['num_download'];
262
                    } else {
263
                        $post_attachment .= \_MD_NEWBB_SEENOTGUEST;
0 ignored issues
show
Bug introduced by
The constant _MD_NEWBB_SEENOTGUEST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
264
                    }
265
                }
266
                $post_attachment .= '<br>';
267
            }
268
        }
269
270
        return $post_attachment;
271
    }
272
273
    // attachment functions
274
    // ////////////////////////////////////////////////////////////////////////////////////
275
276
    /**
277
     * @param bool $force
278
     * @return bool
279
     */
280
    public function store($force = true)
281
    {
282
        $answerHandler = new AnswerHandler($this->db);
283
284
        if (Constants::SF_AN_STATUS_APPROVED == $this->status()) {
285
            $criteria = new \CriteriaCompo(new \Criteria('faqid', $this->faqid()));
0 ignored issues
show
Bug introduced by
It seems like $this->faqid() can also be of type array and array; however, parameter $value of Criteria::__construct() does only seem to accept string, 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

285
            $criteria = new \CriteriaCompo(new \Criteria('faqid', /** @scrutinizer ignore-type */ $this->faqid()));
Loading history...
286
            $answerHandler->updateAll('status', Constants::SF_AN_STATUS_REJECTED, $criteria);
287
        }
288
289
        return $answerHandler->insert($this, $force);
290
    }
291
292
    /**
293
     * @return mixed
294
     */
295
    public function answerid()
296
    {
297
        return $this->getVar('answerid');
298
    }
299
300
    /**
301
     * @return mixed
302
     */
303
    public function faqid()
304
    {
305
        return $this->getVar('faqid');
306
    }
307
308
    /**
309
     * @param string $format
310
     * @return mixed
311
     */
312
    public function answer($format = 'S')
313
    {
314
        return $this->getVar('answer', $format);
315
    }
316
317
    /**
318
     * @return mixed
319
     */
320
    public function uid()
321
    {
322
        return $this->getVar('uid');
323
    }
324
325
    /**
326
     * @param string $dateFormat
327
     * @param string $format
328
     * @return string
329
     */
330
    public function datesub($dateFormat = 'none', $format = 'S')
331
    {
332
        if ('none' === $dateFormat) {
333
            /** @var Smartfaq\Helper $helper */
334
            $helper            = Smartfaq\Helper::getInstance();
335
            $smartModuleConfig = $helper->getConfig();
336
            $dateFormat        = $smartModuleConfig['dateformat'];
337
        }
338
339
        return \formatTimestamp($this->getVar('datesub', $format), $dateFormat);
340
    }
341
342
    /**
343
     * @return mixed
344
     */
345
    public function status()
346
    {
347
        return $this->getVar('status');
348
    }
349
350
    /**
351
     * @return bool
352
     */
353
    public function notLoaded()
354
    {
355
        return (-1 == $this->getVar('answerid'));
356
    }
357
358
    /**
359
     * @param array $notifications
360
     */
361
    public function sendNotifications($notifications = []): void
362
    {
363
        $smartModule = Smartfaq\Utility::getModuleInfo();
364
365
        $myts = \MyTextSanitizer::getInstance();
366
        /** @var \XoopsNotificationHandler $notificationHandler */
367
        $notificationHandler = \xoops_getHandler('notification');
368
369
        $faqObj = new Smartfaq\Faq($this->faqid());
370
371
        $tags                  = [];
372
        $tags['MODULE_NAME']   = $myts->displayTarea($smartModule->getVar('name'));
373
        $tags['FAQ_NAME']      = $faqObj->question();
374
        $tags['FAQ_URL']       = XOOPS_URL . '/modules/' . $smartModule->getVar('dirname') . '/faq.php?faqid=' . $faqObj->faqid();
375
        $tags['CATEGORY_NAME'] = $faqObj->getCategoryName();
376
        $tags['CATEGORY_URL']  = XOOPS_URL . '/modules/' . $smartModule->getVar('dirname') . '/category.php?categoryid=' . $faqObj->categoryid();
377
        $tags['FAQ_QUESTION']  = $faqObj->question();
378
379
        // TODO : Not sure about the 'formpreview' ...
380
        $tags['FAQ_ANSWER'] = $this->answer('formpreview');
381
        $tags['DATESUB']    = $this->datesub();
382
383
        foreach ($notifications as $notification) {
384
            switch ($notification) {
385
                case Constants::SF_NOT_ANSWER_APPROVED:
386
                    // This notification is not working for PM, but is for email... and I don't understand why???
387
                    $notificationHandler->triggerEvent('faq', $this->answerid(), 'answer_approved', $tags);
388
                    break;
389
                case -1:
390
                default:
391
                    break;
392
            }
393
        }
394
    }
395
}
396