Completed
Branch master (c92e39)
by Michael
02:32
created

sfAnswer   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 373
Duplicated Lines 4.02 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
dl 15
loc 373
rs 7.9487
c 0
b 0
f 0
wmc 52
lcom 2
cbo 2

16 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 32 4
A getAttachment() 0 14 3
A incrementDownload() 0 9 2
A saveAttachment() 3 15 4
D deleteAttachment() 3 33 9
B setAttachment() 0 21 5
C displayAttachment() 0 85 11
A store() 0 11 2
A answerid() 0 4 1
A faqid() 0 4 1
A answer() 0 4 1
A uid() 0 4 1
A datesub() 9 9 2
A status() 0 4 1
A notLoaded() 0 4 1
B sendNotifications() 0 35 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like sfAnswer 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 sfAnswer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Module: SmartFAQ
5
 * Author: The SmartFactory <www.smartfactory.ca>
6
 * Licence: GNU
7
 */
8
9
// defined('XOOPS_ROOT_PATH') || exit('XOOPS root path not defined');
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
10
11
// Answers status
12
define('_SF_AN_STATUS_NOTSET', -1);
13
define('_SF_AN_STATUS_PROPOSED', 0);
14
define('_SF_AN_STATUS_APPROVED', 1);
15
define('_SF_AN_STATUS_REJECTED', 2);
16
17
// Notification Events
18
define('_SF_NOT_ANSWER_APPROVED', 3);
19
define('_SF_NOT_ANSWER_REJECTED', 4);
20
21
/**
22
 * Class sfAnswer
23
 */
24
class sfAnswer extends XoopsObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
25
{
26
    public $attachment_array = array();
27
28
    /**
29
     * constructor
30
     * @param null $id
31
     */
32
    public function __construct($id = null)
33
    {
34
        $this->db = XoopsDatabaseFactory::getDatabaseConnection();
35
        $this->initVar('answerid', XOBJ_DTYPE_INT, null, false);
36
        $this->initVar('status', XOBJ_DTYPE_INT, -1, false);
37
        $this->initVar('faqid', XOBJ_DTYPE_INT, null, false);
38
        $this->initVar('answer', XOBJ_DTYPE_TXTAREA, null, true);
39
        $this->initVar('uid', XOBJ_DTYPE_INT, 0, false);
40
        $this->initVar('datesub', XOBJ_DTYPE_INT, null, false);
41
        $this->initVar('notifypub', XOBJ_DTYPE_INT, 0, false);
42
43
        $this->initVar('attachment', XOBJ_DTYPE_TXTAREA, '');
44
45
        $this->initVar('dohtml', XOBJ_DTYPE_INT, 1, false);
46
        $this->initVar('doxcode', XOBJ_DTYPE_INT, 1, false);
47
        $this->initVar('dosmiley', XOBJ_DTYPE_INT, 1, false);
48
        $this->initVar('doimage', XOBJ_DTYPE_INT, 0, false);
49
        $this->initVar('dobr', XOBJ_DTYPE_INT, 1, false);
50
51
        // for backward compatibility
52
        if (isset($id)) {
53
            if (is_array($id)) {
54
                $this->assignVars($id);
55
            } else {
56
                $answerHandler = new sfAnswerHandler($this->db);
57
                $answer        = $answerHandler->get($id);
58
                foreach ($answer->vars as $k => $v) {
59
                    $this->assignVar($k, $v['value']);
60
                }
61
            }
62
        }
63
    }
64
65
    // ////////////////////////////////////////////////////////////////////////////////////
66
    // attachment functions    TODO: there should be a file/attachment management class
67
    /**
68
     * @return array|mixed|null
69
     */
70
    public function getAttachment()
71
    {
72
        if (count($this->attachment_array)) {
73
            return $this->attachment_array;
74
        }
75
        $attachment = $this->getVar('attachment');
76
        if (empty($attachment)) {
77
            $this->attachment_array = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $attachment_array.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
78
        } else {
79
            $this->attachment_array = @unserialize(base64_decode($attachment));
80
        }
81
82
        return $this->attachment_array;
83
    }
84
85
    /**
86
     * @param $attach_key
87
     * @return bool
88
     */
89
    public function incrementDownload($attach_key)
90
    {
91
        if (!$attach_key) {
92
            return false;
93
        }
94
        $this->attachment_array[(string)$attach_key]['num_download']++;
95
96
        return $this->attachment_array[(string)$attach_key]['num_download'];
97
    }
98
99
    /**
100
     * @return bool
101
     */
102
    public function saveAttachment()
0 ignored issues
show
Coding Style introduced by
saveAttachment uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
103
    {
104
        $attachment_save = '';
105 View Code Duplication
        if (is_array($this->attachment_array) && count($this->attachment_array) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
            $attachment_save = base64_encode(serialize($this->attachment_array));
107
        }
108
        $this->setVar('attachment', $attachment_save);
109
        $sql = 'UPDATE ' . $GLOBALS['xoopsDB']->prefix('smartfaq_answers') . ' SET attachment=' . $GLOBALS['xoopsDB']->quoteString($attachment_save) . ' WHERE post_id = ' . $this->getVar('answerid');
110
        if (!$result = $GLOBALS['xoopsDB']->queryF($sql)) {
111
            //xoops_error($GLOBALS["xoopsDB"]->error());
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
112
            return false;
113
        }
114
115
        return true;
116
    }
117
118
    /**
119
     * @param  null $attach_array
120
     * @return bool
121
     */
122
    public function deleteAttachment($attach_array = null)
123
    {
124
        global $xoopsModuleConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
125
126
        $attach_old = $this->getAttachment();
127
        if (!is_array($attach_old) || count($attach_old) < 1) {
128
            return true;
129
        }
130
        $this->attachment_array = array();
131
132
        if ($attach_array === null) {
133
            $attach_array = array_keys($attach_old);
134
        } // to delete all!
135
        if (!is_array($attach_array)) {
136
            $attach_array = array($attach_array);
137
        }
138
139
        foreach ($attach_old as $key => $attach) {
140
            if (in_array($key, $attach_array)) {
141
                @unlink(XOOPS_ROOT_PATH . '/' . $xoopsModuleConfig['dir_attachments'] . '/' . $attach['name_saved']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
142
                @unlink(XOOPS_ROOT_PATH . '/' . $xoopsModuleConfig['dir_attachments'] . '/thumbs/' . $attach['name_saved']); // delete thumbnails
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
143
                continue;
144
            }
145
            $this->attachment_array[$key] = $attach;
146
        }
147
        $attachment_save = '';
148 View Code Duplication
        if (is_array($this->attachment_array) && count($this->attachment_array) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
149
            $attachment_save = base64_encode(serialize($this->attachment_array));
150
        }
151
        $this->setVar('attachment', $attachment_save);
152
153
        return true;
154
    }
155
156
    /**
157
     * @param  string $name_saved
158
     * @param  string $name_display
159
     * @param  string $mimetype
160
     * @param  int    $num_download
161
     * @return bool
162
     */
163
    public function setAttachment($name_saved = '', $name_display = '', $mimetype = '', $num_download = 0)
164
    {
165
        static $counter = 0;
166
        $this->attachment_array = $this->getAttachment();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getAttachment() of type * is incompatible with the declared type array of property $attachment_array.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
167
        if ($name_saved) {
168
            $key                          = (string)(time() + ($counter++));
169
            $this->attachment_array[$key] = array(
170
                'name_saved'   => $name_saved,
171
                'name_display' => isset($name_display) ? $name_display : $name_saved,
172
                'mimetype'     => $mimetype,
173
                'num_download' => isset($num_download) ? (int)$num_download : 0
174
            );
175
        }
176
        $attachment_save = null;
177
        if (is_array($this->attachment_array)) {
178
            $attachment_save = base64_encode(serialize($this->attachment_array));
179
        }
180
        $this->setVar('attachment', $attachment_save);
181
182
        return true;
183
    }
184
185
    /**
186
     * TODO: refactor
187
     * @param  bool $asSource
188
     * @return string
189
     */
190
    public function displayAttachment($asSource = false)
191
    {
192
        global $xoopsModule, $xoopsModuleConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
193
194
        $post_attachment = '';
195
        $attachments     = $this->getAttachment();
196
        if (is_array($attachments) && count($attachments) > 0) {
197
            $iconHandler = sf_getIconHandler();
198
            $mime_path   = $iconHandler->getPath('mime');
199
            include_once XOOPS_ROOT_PATH . '/modules/' . $xoopsModule->getVar('dirname', 'n') . '/include/functions.image.php';
200
            $image_extensions = array('jpg', 'jpeg', 'gif', 'png', 'bmp'); // need improve !!!
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
201
            $post_attachment  .= '<br><strong>' . _MD_ATTACHMENT . '</strong>:';
202
            $post_attachment  .= '<br><hr size="1" noshade="noshade" /><br>';
203
            foreach ($attachments as $key => $att) {
204
                $file_extension = ltrim(strrchr($att['name_saved'], '.'), '.');
205
                $filetype       = $file_extension;
206
                if (file_exists(XOOPS_ROOT_PATH . '/' . $mime_path . '/' . $filetype . '.gif')) {
207
                    $icon_filetype = XOOPS_URL . '/' . $mime_path . '/' . $filetype . '.gif';
208
                } else {
209
                    $icon_filetype = XOOPS_URL . '/' . $mime_path . '/unknown.gif';
210
                }
211
                $file_size = @filesize(XOOPS_ROOT_PATH . '/' . $xoopsModuleConfig['dir_attachments'] . '/' . $att['name_saved']);
212
                $file_size = number_format($file_size / 1024, 2) . ' KB';
213
                if ($xoopsModuleConfig['media_allowed'] && in_array(strtolower($file_extension), $image_extensions)) {
214
                    $post_attachment .= '<br><img src="' . $icon_filetype . '" alt="' . $filetype . '" /><strong>&nbsp; ' . $att['name_display'] . '</strong> <small>(' . $file_size . ')</small>';
215
                    $post_attachment .= '<br>' . sf_attachmentImage($att['name_saved']);
216
                    $isDisplayed     = true;
0 ignored issues
show
Unused Code introduced by
$isDisplayed is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
217
                } else {
218
                    global $xoopsUser;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
219
                    if (empty($xoopsModuleConfig['show_userattach'])) {
220
                        $post_attachment .= '<a href="'
221
                                            . XOOPS_URL
222
                                            . '/modules/'
223
                                            . $xoopsModule->getVar('dirname', 'n')
224
                                            . '/dl_attachment.php?attachid='
225
                                            . $key
226
                                            . '&amp;post_id='
227
                                            . $this->getVar('post_id')
228
                                            . '"> <img src="'
229
                                            . $icon_filetype
230
                                            . '" alt="'
231
                                            . $filetype
232
                                            . '" /> '
233
                                            . $att['name_display']
234
                                            . '</a> '
235
                                            . _MD_FILESIZE
236
                                            . ': '
237
                                            . $file_size
238
                                            . '; '
239
                                            . _MD_HITS
240
                                            . ': '
241
                                            . $att['num_download'];
242
                    } elseif ($xoopsUser && $xoopsUser->uid() > 0 && $xoopsUser->isactive()) {
243
                        $post_attachment .= '<a href="'
244
                                            . XOOPS_URL
245
                                            . '/modules/'
246
                                            . $xoopsModule->getVar('dirname', 'n')
247
                                            . '/dl_attachment.php?attachid='
248
                                            . $key
249
                                            . '&amp;post_id='
250
                                            . $this->getVar('post_id')
251
                                            . '"> <img src="'
252
                                            . $icon_filetype
253
                                            . '" alt="'
254
                                            . $filetype
255
                                            . '" /> '
256
                                            . $att['name_display']
257
                                            . '</a> '
258
                                            . _MD_FILESIZE
259
                                            . ': '
260
                                            . $file_size
261
                                            . '; '
262
                                            . _MD_HITS
263
                                            . ': '
264
                                            . $att['num_download'];
265
                    } else {
266
                        $post_attachment .= _MD_NEWBB_SEENOTGUEST;
267
                    }
268
                }
269
                $post_attachment .= '<br>';
270
            }
271
        }
272
273
        return $post_attachment;
274
    }
275
    // attachment functions
276
    // ////////////////////////////////////////////////////////////////////////////////////
277
278
    /**
279
     * @param  bool $force
280
     * @return bool
281
     */
282
    public function store($force = true)
283
    {
284
        $answerHandler = new sfAnswerHandler($this->db);
285
286
        if ($this->status() == _SF_AN_STATUS_APPROVED) {
287
            $criteria = new CriteriaCompo(new criteria('faqid', $this->faqid()));
288
            $answerHandler->updateAll('status', _SF_AN_STATUS_REJECTED, $criteria);
289
        }
290
291
        return $answerHandler->insert($this, $force);
292
    }
293
294
    /**
295
     * @return mixed
296
     */
297
    public function answerid()
298
    {
299
        return $this->getVar('answerid');
300
    }
301
302
    /**
303
     * @return mixed
304
     */
305
    public function faqid()
306
    {
307
        return $this->getVar('faqid');
308
    }
309
310
    /**
311
     * @param  string $format
312
     * @return mixed
313
     */
314
    public function answer($format = 'S')
315
    {
316
        return $this->getVar('answer', $format);
317
    }
318
319
    /**
320
     * @return mixed
321
     */
322
    public function uid()
323
    {
324
        return $this->getVar('uid');
325
    }
326
327
    /**
328
     * @param  string $dateFormat
329
     * @param  string $format
330
     * @return string
331
     */
332 View Code Duplication
    public function datesub($dateFormat = 'none', $format = 'S')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
333
    {
334
        if ($dateFormat === 'none') {
335
            $smartModuleConfig = sf_getModuleConfig();
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 ($this->getVar('answerid') == -1);
356
    }
357
358
    /**
359
     * @param array $notifications
360
     */
361
    public function sendNotifications($notifications = array())
362
    {
363
        $smartModule = sf_getModuleInfo();
364
365
        $myts                = MyTextSanitizer::getInstance();
366
        $notificationHandler = xoops_getHandler('notification');
367
368
        $faqObj = new sfFaq($this->faqid());
369
370
        $tags                  = array();
371
        $tags['MODULE_NAME']   = $myts->displayTarea($smartModule->getVar('name'));
372
        $tags['FAQ_NAME']      = $faqObj->question();
373
        $tags['FAQ_URL']       = XOOPS_URL . '/modules/' . $smartModule->getVar('dirname') . '/faq.php?faqid=' . $faqObj->faqid();
374
        $tags['CATEGORY_NAME'] = $faqObj->getCategoryName();
375
        $tags['CATEGORY_URL']  = XOOPS_URL . '/modules/' . $smartModule->getVar('dirname') . '/category.php?categoryid=' . $faqObj->categoryid();
376
        $tags['FAQ_QUESTION']  = $faqObj->question();
377
378
        // TODO : Not sure about the 'formpreview' ...
379
        $tags['FAQ_ANSWER'] = $this->answer('formpreview');
380
        $tags['DATESUB']    = $this->datesub();
381
382
        foreach ($notifications as $notification) {
383
            switch ($notification) {
384
                case _SF_NOT_ANSWER_APPROVED:
385
                    // This notification is not working for PM, but is for email... and I don't understand why???
386
                    $notificationHandler->triggerEvent('faq', $this->answerid(), 'answer_approved', $tags);
387
388
                    break;
389
390
                case -1:
391
                default:
392
                    break;
393
            }
394
        }
395
    }
396
}
397
398
/**
399
 * Answers handler class.
400
 * This class is responsible for providing data access mechanisms to the data source
401
 * of Answer class objects.
402
 *
403
 * @author  marcan <[email protected]>
404
 * @package SmartFAQ
405
 */
406
class sfAnswerHandler extends XoopsPersistableObjectHandler
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
407
{
408
409
    /**
410
     * create a new answer
411
     *
412
     * @param  bool $isNew flag the new objects as "new"?
413
     * @return object sfAnswer
414
     */
415
    public function create($isNew = true)
416
    {
417
        $answer = new sfAnswer();
418
        if ($isNew) {
419
            $answer->setNew();
420
        }
421
422
        return $answer;
423
    }
424
425
    /**
426
     * retrieve an answer
427
     *
428
     * @param  int  $id answerid of the answer
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
429
     * @param  null $fields
430
     * @return mixed reference to the <a href='psi_element://sfAnswer'>sfAnswer</a> object, FALSE if failed
431
     */
432 View Code Duplication
    public function get($id = null, $fields = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
433
    {
434
        if ((int)$id > 0) {
435
            $sql = 'SELECT * FROM ' . $this->db->prefix('smartfaq_answers') . ' WHERE answerid=' . $id;
436
            if (!$result = $this->db->query($sql)) {
437
                return false;
438
            }
439
440
            $numrows = $this->db->getRowsNum($result);
441
            if ($numrows == 1) {
442
                $answer = new sfAnswer();
443
                $answer->assignVars($this->db->fetchArray($result));
444
445
                return $answer;
446
            }
447
        }
448
449
        return false;
450
    }
451
452
    /**
453
     * insert a new answer in the database
454
     *
455
     * @param  XoopsObject $answerObj reference to the <a href='psi_element://sfAnswer'>sfAnswer</a> object
456
     * @param  bool        $force
457
     * @return bool        FALSE if failed, TRUE if already present and unchanged or successful
458
     */
459
    public function insert(XoopsObject $answerObj, $force = false)
460
    {
461
        if (strtolower(get_class($answerObj)) !== 'sfanswer') {
462
            return false;
463
        }
464
        if (!$answerObj->isDirty()) {
465
            return true;
466
        }
467
        if (!$answerObj->cleanVars()) {
468
            return false;
469
        }
470
471
        foreach ($answerObj->cleanVars as $k => $v) {
472
            ${$k} = $v;
473
        }
474
475
        if ($answerObj->isNew()) {
476
            $sql = sprintf('INSERT INTO %s (answerid, `status`, faqid, answer, uid, datesub, notifypub) VALUES (NULL, %u, %u, %s, %u, %u, %u)', $this->db->prefix('smartfaq_answers'), $status, $faqid,
0 ignored issues
show
Bug introduced by
The variable $status does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $faqid does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
477
                           $this->db->quoteString($answer), $uid, time(), $notifypub);
0 ignored issues
show
Bug introduced by
The variable $answer does not exist. Did you mean $answerObj?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $uid does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $notifypub does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
478
        } else {
479
            $sql = sprintf('UPDATE %s SET STATUS = %u, faqid = %s, answer = %s, uid = %u, datesub = %u, notifypub = %u WHERE answerid = %u', $this->db->prefix('smartfaq_answers'), $status, $faqid,
480
                           $this->db->quoteString($answer), $uid, $datesub, $notifypub, $answerid);
0 ignored issues
show
Bug introduced by
The variable $answer does not exist. Did you mean $answerObj?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $datesub does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $answerid does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
481
        }
482
483 View Code Duplication
        if (false != $force) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
484
            $result = $this->db->queryF($sql);
485
        } else {
486
            $result = $this->db->query($sql);
487
        }
488
489
        if (!$result) {
490
            return false;
491
        }
492
493
        if ($answerObj->isNew()) {
494
            $answerObj->assignVar('answerid', $this->db->getInsertId());
495
        } else {
496
            $answerObj->assignVar('answerid', $answerid);
497
        }
498
499
        return true;
500
    }
501
502
    /**
503
     * delete an answer from the database
504
     *
505
     * @param  XoopsObject $answer reference to the answer to delete
506
     * @param  bool        $force
507
     * @return bool        FALSE if failed.
508
     */
509
    public function delete(XoopsObject $answer, $force = false)
510
    {
511
        if (strtolower(get_class($answer)) !== 'sfanswer') {
512
            return false;
513
        }
514
        $sql = sprintf('DELETE FROM %s WHERE answerid = %u', $this->db->prefix('smartfaq_answers'), $answer->getVar('answerid'));
515
516
        //echo "<br>" . $sql . "<br>";
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
517
518 View Code Duplication
        if (false != $force) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
519
            $result = $this->db->queryF($sql);
520
        } else {
521
            $result = $this->db->query($sql);
522
        }
523
        if (!$result) {
524
            return false;
525
        }
526
527
        return true;
528
    }
529
530
    /**
531
     * delete an answer from the database
532
     *
533
     * @param  object $faqObj reference to the answer to delete
534
     * @return bool   FALSE if failed.
535
     * @internal param bool $force
536
     */
537
    public function deleteFaqAnswers($faqObj)
538
    {
539
        if (strtolower(get_class($faqObj)) !== 'sffaq') {
540
            return false;
541
        }
542
        $answers = $this->getAllAnswers($faqObj->faqid());
543
        $result  = true;
544
        foreach ($answers as $answer) {
545
            if (!$this->delete($answer)) {
546
                $result = false;
547
            }
548
        }
549
550
        return $result;
551
    }
552
553
    /**
554
     * retrieve answers from the database
555
     *
556
     * @param  CriteriaElement $criteria  {@link CriteriaElement} conditions to be met
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be null|CriteriaElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
557
     * @param  bool            $id_as_key use the answerid as key for the array?
558
     * @param  bool            $as_object
559
     * @return array           array of <a href='psi_element://sfAnswer'>sfAnswer</a> objects
560
     */
561
    public function &getObjects(CriteriaElement $criteria = null, $id_as_key = false, $as_object = true)
562
    {
563
        $ret   = array();
564
        $limit = $start = 0;
565
        $sql   = 'SELECT * FROM ' . $this->db->prefix('smartfaq_answers');
566 View Code Duplication
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
567
            $sql .= ' ' . $criteria->renderWhere();
568
            if ($criteria->getSort() != '') {
569
                $sql .= ' ORDER BY ' . $criteria->getSort() . ' ' . $criteria->getOrder();
570
            }
571
            $limit = $criteria->getLimit();
572
            $start = $criteria->getStart();
573
        }
574
        //echo "<br>" . $sql . "<br>";
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
575
        $result = $this->db->query($sql, $limit, $start);
576
        if (!$result) {
577
            return $ret;
578
        }
579 View Code Duplication
        while ($myrow = $this->db->fetchArray($result)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
580
            $answer = new sfAnswer();
581
            $answer->assignVars($myrow);
582
            if (!$id_as_key) {
583
                $ret[] =& $answer;
584
            } else {
585
                $ret[$myrow['answerid']] =& $answer;
586
            }
587
            unset($answer);
588
        }
589
590
        return $ret;
591
    }
592
593
    /**
594
     * retrieve 1 official answer (for now SmartFAQ only allow 1 official answer...)
595
     *
596
     * @param  int $faqid
597
     * @return mixed reference to the <a href='psi_element://sfAnswer'>sfAnswer</a> object, FALSE if failed
598
     */
599
    public function getOfficialAnswer($faqid = 0)
600
    {
601
        $theaAnswers = $this->getAllAnswers($faqid, _SF_AN_STATUS_APPROVED, 1, 0);
602
        $ret         = false;
603
        if (count($theaAnswers) == 1) {
604
            $ret = $theaAnswers[0];
605
        }
606
607
        return $ret;
608
    }
609
610
    /**
611
     * retrieve all answers
612
     *
613
     * @param  int    $faqid
614
     * @param  int    $status
615
     * @param  int    $limit
616
     * @param  int    $start
617
     * @param  string $sort
618
     * @param  string $order
619
     * @return array  array of <a href='psi_element://sfAnswer'>sfAnswer</a> objects
620
     */
621
    public function getAllAnswers(
622
        $faqid = 0,
623
        $status = -1,
624
        $limit = 0,
625
        $start = 0,
626
        $sort = 'datesub',
627
        $order = 'DESC'
628
    ) {
629
        $hasStatusCriteria = false;
630
        $criteriaStatus    = new CriteriaCompo();
631
        if (is_array($status)) {
632
            $hasStatusCriteria = true;
633
            foreach ($status as $v) {
634
                $criteriaStatus->add(new Criteria('status', $v), 'OR');
635
            }
636
        } elseif ($status != -1) {
637
            $hasStatusCriteria = true;
638
            $criteriaStatus->add(new Criteria('status', $status), 'OR');
639
        }
640
        $criteriaFaqid = new Criteria('faqid', $faqid);
641
642
        $criteria = new CriteriaCompo();
643
        $criteria->add($criteriaFaqid);
644
645
        if ($hasStatusCriteria) {
646
            $criteria->add($criteriaStatus);
647
        }
648
649
        $criteria->setSort($sort);
650
        $criteria->setOrder($order);
651
        $criteria->setLimit($limit);
652
        $criteria->setStart($start);
653
        $ret =& $this->getObjects($criteria);
654
655
        return $ret;
656
    }
657
658
    /**
659
     * count answers matching a condition
660
     *
661
     * @param  CriteriaElement $criteria {@link CriteriaElement} to match
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be null|CriteriaElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
662
     * @return int             count of answers
663
     */
664 View Code Duplication
    public function getCount(CriteriaElement $criteria = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
665
    {
666
        $sql = 'SELECT COUNT(*) FROM ' . $this->db->prefix('smartfaq_answers');
667
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
668
            $sql .= ' ' . $criteria->renderWhere();
669
        }
670
        $result = $this->db->query($sql);
671
        if (!$result) {
672
            return 0;
673
        }
674
        list($count) = $this->db->fetchRow($result);
675
676
        return $count;
677
    }
678
679
    /**
680
     * count answers matching a condition and group by faq ID
681
     *
682
     * @param  object $criteria {@link CriteriaElement} to match
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be object|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
683
     * @return array
684
     */
685
    public function getCountByFAQ($criteria = null)
686
    {
687
        $sql = 'SELECT faqid, COUNT(*) FROM ' . $this->db->prefix('smartfaq_answers');
688
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
689
            $sql .= ' ' . $criteria->renderWhere();
690
            $sql .= ' ' . $criteria->getGroupby();
691
        }
692
693
        //echo "<br>$sql<br>";
694
695
        $result = $this->db->query($sql);
696
        if (!$result) {
697
            return array();
698
        }
699
        $ret = array();
700
        while (list($id, $count) = $this->db->fetchRow($result)) {
701
            $ret[$id] = $count;
702
        }
703
704
        return $ret;
705
    }
706
707
    /**
708
     * delete answers matching a set of conditions
709
     *
710
     * @param  CriteriaElement $criteria {@link CriteriaElement}
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be null|CriteriaElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
711
     * @param  bool            $force
712
     * @param  bool            $asObject
713
     * @return bool            FALSE if deletion failed
714
     */
715
    public function deleteAll(CriteriaElement $criteria = null, $force = true, $asObject = false)
716
    {
717
        $sql = 'DELETE FROM ' . $this->db->prefix('smartfaq_answers');
718
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
719
            $sql .= ' ' . $criteria->renderWhere();
720
        }
721
        if (!$this->db->query($sql)) {
722
            return false;
723
        }
724
725
        return true;
726
    }
727
728
    /**
729
     * Change a value for answers with a certain criteria
730
     *
731
     * @param  string          $fieldname  Name of the field
732
     * @param  string          $fieldvalue Value to write
733
     * @param  CriteriaElement $criteria   {@link CriteriaElement}
0 ignored issues
show
Documentation introduced by
Should the type for parameter $criteria not be null|CriteriaElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
734
     * @param  bool            $force
735
     * @return bool
736
     */
737 View Code Duplication
    public function updateAll($fieldname, $fieldvalue, CriteriaElement $criteria = null, $force = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
738
    {
739
        $set_clause = is_numeric($fieldvalue) ? $fieldname . ' = ' . $fieldvalue : $fieldname . ' = ' . $this->db->quoteString($fieldvalue);
740
        $sql        = 'UPDATE ' . $this->db->prefix('smartfaq_answers') . ' SET ' . $set_clause;
741
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
742
            $sql .= ' ' . $criteria->renderWhere();
743
        }
744
        //echo "<br>" . $sql . "<br>";
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
745
        if (!$this->db->queryF($sql)) {
746
            return false;
747
        }
748
749
        return true;
750
    }
751
752
    /**
753
     * @param $faqids
754
     * @return array
755
     */
756
    public function getLastPublishedByFaq($faqids)
757
    {
758
        $ret    = array();
759
        $sql    = 'SELECT faqid, answer, uid, datesub FROM ' . $this->db->prefix('smartfaq_answers') . '
760
               WHERE faqid IN (' . implode(',', $faqids) . ') AND status = ' . _SF_AN_STATUS_APPROVED . ' GROUP BY faqid';
761
        $result = $this->db->query($sql);
762
        if (!$result) {
763
            return $ret;
764
        }
765 View Code Duplication
        while ($row = $this->db->fetchArray($result)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
766
            $answer = new sfAnswer();
767
            $answer->assignVars($row);
768
            $ret[$row['faqid']] =& $answer;
769
            unset($answer);
770
        }
771
772
        return $ret;
773
    }
774
}
775