Completed
Push — master ( c5b9c9...4f0041 )
by Goffy
14s queued 12s
created

SysUtility::enumerate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace XoopsModules\Tools\Common;
6
7
/*
8
 Utility Class Definition
9
10
 You may not change or alter any portion of this comment or credits of
11
 supporting developers from this source code or any supporting source code
12
 which is considered copyrighted (c) material of the original comment or credit
13
 authors.
14
15
 This program is distributed in the hope that it will be useful, but
16
 WITHOUT ANY WARRANTY; without even the implied warranty of
17
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18
 */
19
20
/**
21
 *
22
 * @copyright    {@link https://xoops.org/ XOOPS Project}
23
 * @license      GNU GPL2orlater(https://www.gnu.org/licenses/gpl-2.0.html)
24
 * @author       XOOPS Development Team <https://xoops.org>
25
 * @author       ZySpec <[email protected]>
26
 * @author       Mamba <[email protected]>
27
 */
28
29
use Xmf\Request;
30
use MyTextSanitizer;
31
use XoopsFormDhtmlTextArea;
32
use XoopsFormEditor;
33
use XoopsFormTextArea;
34
use XoopsModules\Tools\{
35
    Helper
36
};
37
38
39
40
/**
41
 * Class SysUtility
42
 */
43
class SysUtility
44
{
45
    use VersionChecks; //checkVerXoops, checkVerPhp Traits
46
    use ServerStats; // getServerStats Trait
47
    use FilesManagement; // Files Management Trait
48
49
    //--------------- Common module methods -----------------------------
50
51
    /**
52
     * Access the only instance of this class
53
 *
54
     * @return SysUtility
55
*
56
     */
57
    public static function getInstance(): SysUtility
58
    {
59
        static $instance;
60
        if (null === $instance) {
61
            $instance = new static();
62
        }
63
64
        return $instance;
65
    }
66
67
    /**
68
     * @param $text
69
     * @param $form_sort
70
     * @return string
71
     */
72
    public static function selectSorting($text, $form_sort): string
73
    {
74
        global $start, $order, $sort;
75
76
        $select_view   = '';
0 ignored issues
show
Unused Code introduced by
$select_view 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...
77
        $moduleDirName = \basename(\dirname(__DIR__));
0 ignored issues
show
Unused Code introduced by
$moduleDirName 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...
78
        $helper = Helper::getInstance();
79
80
        //$pathModIcon16 = XOOPS_URL . '/modules/' . $moduleDirName . '/' . $helper->getConfig('modicons16');
81
        $pathModIcon16 = $helper->url($helper->getModule()->getInfo('modicons16'));
82
83
        $select_view = '<form name="form_switch" id="form_switch" action="' . Request::getString('REQUEST_URI', '', 'SERVER') . '" method="post"><span style="font-weight: bold;">' . $text . '</span>';
84
        //$sorts =  $sort ==  'asc' ? 'desc' : 'asc';
85
        if ($form_sort == $sort) {
86
            $sel1 = 'asc' === $order ? 'selasc.png' : 'asc.png';
87
            $sel2 = 'desc' === $order ? 'seldesc.png' : 'desc.png';
88
        } else {
89
            $sel1 = 'asc.png';
90
            $sel2 = 'desc.png';
91
        }
92
        $select_view .= '  <a href="' . Request::getString('SCRIPT_NAME', '', 'SERVER') . '?start=' . $start . '&sort=' . $form_sort . '&order=asc"><img src="' . $pathModIcon16 . '/' . $sel1 . '" title="ASC" alt="ASC"></a>';
93
        $select_view .= '<a href="' . Request::getString('SCRIPT_NAME', '', 'SERVER') . '?start=' . $start . '&sort=' . $form_sort . '&order=desc"><img src="' . $pathModIcon16 . '/' . $sel2 . '" title="DESC" alt="DESC"></a>';
94
        $select_view .= '</form>';
95
96
        return $select_view;
97
    }
98
99
    /***************Blocks***************/
100
    /**
101
     * @param array $cats
102
     * @return string
103
     */
104
    public static function blockAddCatSelect(array $cats): string
105
    {
106
        $cat_sql = '';
107
        if (\is_array($cats) && !empty($cats)) {
108
            $cat_sql = '(' . \current($cats);
109
            \array_shift($cats);
110
            foreach ($cats as $cat) {
111
                $cat_sql .= ',' . $cat;
112
            }
113
            $cat_sql .= ')';
114
        }
115
116
        return $cat_sql;
117
    }
118
119
    /**
120
     * @param $content
121
     */
122 View Code Duplication
    public static function metaKeywords($content): void
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...
123
    {
124
        global $xoopsTpl, $xoTheme;
125
        $myts    = \MyTextSanitizer::getInstance();
126
        $content = $myts->undoHtmlSpecialChars($myts->displayTarea($content));
127
        if (\is_object($xoTheme)) {
128
            $xoTheme->addMeta('meta', 'keywords', \strip_tags($content));
129
        } else {    // Compatibility for old Xoops versions
130
            $xoopsTpl->assign('xoops_metaKeywords', \strip_tags($content));
131
        }
132
    }
133
134
    /**
135
     * @param $content
136
     */
137 View Code Duplication
    public static function metaDescription($content): void
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...
138
    {
139
        global $xoopsTpl, $xoTheme;
140
        $myts    = \MyTextSanitizer::getInstance();
141
        $content = $myts->undoHtmlSpecialChars($myts->displayTarea($content));
142
        if (\is_object($xoTheme)) {
143
            $xoTheme->addMeta('meta', 'description', \strip_tags($content));
144
        } else {    // Compatibility for old Xoops versions
145
            $xoopsTpl->assign('xoops_metaDescription', \strip_tags($content));
146
        }
147
    }
148
149
    /**
150
     * @param string $tableName
151
     * @param string $columnName
152
     *
153
     * @return array|false
154
     */
155
    public static function enumerate(string $tableName, string $columnName)
156
    {
157
        $table = $GLOBALS['xoopsDB']->prefix($tableName);
158
159
        //    $result = $GLOBALS['xoopsDB']->query("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
160
        //        WHERE TABLE_NAME = '" . $table . "' AND COLUMN_NAME = '" . $columnName . "'")
161
        //    || exit ($GLOBALS['xoopsDB']->error());
162
163
        $sql    = 'SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = "' . $table . '" AND COLUMN_NAME = "' . $columnName . '"';
164
        $result = $GLOBALS['xoopsDB']->query($sql);
165
        if (!$result) {
166
//            exit($GLOBALS['xoopsDB']->error());
167
            $logger     = \XoopsLogger::getInstance();
168
            $logger->handleError(\E_USER_WARNING, $sql, __FILE__, __LINE__);
169
            return false;
170
        }
171
172
        $row      = $GLOBALS['xoopsDB']->fetchBoth($result);
173
        $enumList = \explode(',', \str_replace("'", '', \mb_substr($row['COLUMN_TYPE'], 5, - 6)));
174
        return $enumList;
175
    }
176
177
178
    /**
179
     * @param array|string $tableName
180
     * @param int          $id_field
181
     * @param int          $id
182
     *
183
     * @return int|false
184
     */
185
    public static function cloneRecord($tableName, int $id_field, int $id)
186
    {
187
        $new_id = false;
0 ignored issues
show
Unused Code introduced by
$new_id 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...
188
        $logger     = \XoopsLogger::getInstance();
189
        $table  = $GLOBALS['xoopsDB']->prefix($tableName);
190
        // copy content of the record you wish to clone
191
        $sql       = "SELECT * FROM $table WHERE $id_field='$id' ";
192
        $tempTable = $GLOBALS['xoopsDB']->fetchArray($GLOBALS['xoopsDB']->query($sql), \MYSQLI_ASSOC);
193
        if (!$tempTable) {
194
//            exit($GLOBALS['xoopsDB']->error());
195
            $logger->handleError(\E_USER_WARNING, $sql, __FILE__, __LINE__);
196
            return false;
197
        }
198
        // set the auto-incremented id's value to blank.
199
        unset($tempTable[$id_field]);
200
        // insert cloned copy of the original  record
201
        $sql    = "INSERT INTO $table (" . \implode(', ', \array_keys($tempTable)) . ") VALUES ('" . \implode("', '", \array_values($tempTable)) . "')";
202
        $result = $GLOBALS['xoopsDB']->queryF($sql);
203
        if (!$result) {
204
//            exit($GLOBALS['xoopsDB']->error());
205
            $logger->handleError(\E_USER_WARNING, $sql, __FILE__, __LINE__);
206
            return false;
207
        }
208
        // Return the new id
209
        $new_id = $GLOBALS['xoopsDB']->getInsertId();
210
211
        return $new_id;
212
    }
213
214
    /**
215
     * truncateHtml can truncate a string up to a number of characters while preserving whole words and HTML tags
216
     * www.gsdesign.ro/blog/cut-html-string-without-breaking-the-tags
217
     * www.cakephp.org
218
     *
219
     * @param string $text         String to truncate.
220
     * @param int    $length       Length of returned string, including ellipsis.
221
     * @param string $ending       Ending to be appended to the trimmed string.
222
     * @param bool   $exact        If false, $text will not be cut mid-word
223
     * @param bool   $considerHtml If true, HTML tags would be handled correctly
224
     *
225
     * @return string Trimmed string.
226
     */
227
    public static function truncateHtml(string $text, int $length = 100, string $ending = '...', bool $exact = false, bool $considerHtml = true): string
228
    {
229
        $openTags = [];
230
        if ($considerHtml) {
231
            // if the plain text is shorter than the maximum length, return the whole text
232
            if (\mb_strlen(\preg_replace('/<.*?' . '>/', '', $text)) <= $length) {
233
                return $text;
234
            }
235
            // splits all html-tags to scanable lines
236
            \preg_match_all('/(<.+?' . '>)?([^<>]*)/s', $text, $lines, \PREG_SET_ORDER);
237
            $total_length = \mb_strlen($ending);
238
            //$openTags    = [];
239
            $truncate     = '';
240
            foreach ($lines as $line_matchings) {
0 ignored issues
show
Bug introduced by
The expression $lines of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
241
                // if there is any html-tag in this line, handle it and add it (uncounted) to the output
242
                if (!empty($line_matchings[1])) {
243
                    // if it's an "empty element" with or without xhtml-conform closing slash
244
                    if (\preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
245
                        // do nothing
246
                        // if tag is a closing tag
247
                    } elseif (\preg_match('/^<\s*\/(\S+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
248
                        // delete tag from $openTags list
249
                        $pos = \array_search($tag_matchings[1], $openTags, true);
250
                        if (false !== $pos) {
251
                            unset($openTags[$pos]);
252
                        }
253
                        // if tag is an opening tag
254
                    } elseif (\preg_match('/^<\s*([^\s>!]+).*?' . '>$/s', $line_matchings[1], $tag_matchings)) {
255
                        // add tag to the beginning of $openTags list
256
                        \array_unshift($openTags, \mb_strtolower($tag_matchings[1]));
257
                    }
258
                    // add html-tag to $truncate'd text
259
                    $truncate .= $line_matchings[1];
260
                }
261
                // calculate the length of the plain text part of the line; handle entities as one character
262
                $content_length = \mb_strlen(\preg_replace('/&[0-9a-z]{2,8};|&#\d{1,7};|[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
263
                if ($total_length + $content_length > $length) {
264
                    // the number of characters which are left
265
                    $left            = $length - $total_length;
266
                    $entities_length = 0;
267
                    // search for html entities
268
                    if (\preg_match_all('/&[0-9a-z]{2,8};|&#\d{1,7};|[0-9a-f]{1,6};/i', $line_matchings[2], $entities, \PREG_OFFSET_CAPTURE)) {
269
                        // calculate the real length of all entities in the legal range
270
                        foreach ($entities[0] as $entity) {
271
                            if ($left >= $entity[1] + 1 - $entities_length) {
272
                                $left--;
273
                                $entities_length += \mb_strlen($entity[0]);
274
                            } else {
275
                                // no more characters left
276
                                break;
277
                            }
278
                        }
279
                    }
280
                    $truncate .= \mb_substr($line_matchings[2], 0, $left + $entities_length);
281
                    // maximum lenght is reached, so get off the loop
282
                    break;
283
                }
284
                $truncate     .= $line_matchings[2];
285
                $total_length += $content_length;
286
287
                // if the maximum length is reached, get off the loop
288
                if ($total_length >= $length) {
289
                    break;
290
                }
291
            }
292
        } else {
293
            if (\mb_strlen($text) <= $length) {
294
                return $text;
295
            }
296
            $truncate = \mb_substr($text, 0, $length - mb_strlen($ending));
297
        }
298
        // if the words shouldn't be cut in the middle...
299
        if (!$exact) {
300
            // ...search the last occurance of a space...
301
            $spacepos = mb_strrpos($truncate, ' ');
302
            if (isset($spacepos)) {
303
                // ...and cut the text in this position
304
                $truncate = \mb_substr($truncate, 0, $spacepos);
305
            }
306
        }
307
        // add the defined ending to the text
308
        $truncate .= $ending;
309
        if ($considerHtml) {
310
            // close all unclosed html-tags
311
            foreach ($openTags as $tag) {
312
                $truncate .= '</' . $tag . '>';
313
            }
314
        }
315
316
        return $truncate;
317
    }
318
319
    /**
320
     * @param \Xmf\Module\Helper|null $helper
321
     * @param array|null              $options
322
     * @return \XoopsFormDhtmlTextArea|\XoopsFormEditor
323
     */
324
    public static function getEditor($helper = null, array $options = null)
325
    {
326
        /** @var Helper $helper */
327
        if (null === $options) {
328
            $options           = [];
329
            $options['name']   = 'Editor';
330
            $options['value']  = 'Editor';
331
            $options['rows']   = 10;
332
            $options['cols']   = '100%';
333
            $options['width']  = '100%';
334
            $options['height'] = '400px';
335
        }
336
337
        if (null === $helper) {
338
            $helper = Helper::getInstance();
339
        }
340
341
        $isAdmin = $helper->isUserAdmin();
342
343
        if (\class_exists('XoopsFormEditor')) {
344
            if ($isAdmin) {
345
                $descEditor = new \XoopsFormEditor(\ucfirst($options['name']), $helper->getConfig('editorAdmin'), $options, $nohtml = false, $onfailure = 'textarea');
346
            } else {
347
                $descEditor = new \XoopsFormEditor(\ucfirst($options['name']), $helper->getConfig('editorUser'), $options, $nohtml = false, $onfailure = 'textarea');
348
            }
349
        } else {
350
            $descEditor = new \XoopsFormDhtmlTextArea(\ucfirst($options['name']), $options['name'], $options['value'], '100%', '100%');
351
        }
352
353
        //        $form->addElement($descEditor);
354
355
        return $descEditor;
356
    }
357
358
    /**
359
     * @param string $fieldname
360
     * @param string $table
361
     *
362
     * @return bool
363
     */
364
    public static function fieldExists(string $fieldname, string $table): bool
365
    {
366
        global $xoopsDB;
367
        $result = $xoopsDB->queryF("SHOW COLUMNS FROM   $table LIKE '$fieldname'");
368
369
        return ($xoopsDB->getRowsNum($result) > 0);
370
    }
371
372
    /**
373
     * Function responsible for checking if a directory exists, we can also write in and create an index.html file
374
     *
375
     * @param string $folder The full path of the directory to check
376
     */
377
    public static function prepareFolder(string $folder): void
378
    {
379
        try {
380
            if (!@\mkdir($folder) && !\is_dir($folder)) {
381
                throw new \RuntimeException(\sprintf('Unable to create the %s directory', $folder));
382
            }
383
            file_put_contents($folder . '/index.html', '<script>history.go(-1);</script>');
384
        } catch (\Exception $e) {
385
            echo 'Caught exception: ', $e->getMessage(), "\n", '<br>';
386
        }
387
    }
388
389
    /**
390
     * @param string $tablename
391
     *
392
     * @return bool
393
     */
394
    public static function tableExists(string $tablename): bool
395
    {
396
        $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 1);
397
        \trigger_error(__FUNCTION__ . " is deprecated, called from {$trace[0]['file']} line {$trace[0]['line']}");
398
        $GLOBALS['xoopsLogger']->addDeprecated(
399
            \basename(\dirname(__DIR__, 2)) . ' Module: ' . __FUNCTION__ . ' function is deprecated, please use Xmf\Database\Tables method(s) instead.' . " Called from {$trace[0]['file']}line {$trace[0]['line']}"
400
        );
401
        $result = $GLOBALS['xoopsDB']->queryF("SHOW TABLES LIKE '$tablename'");
402
403
        return $GLOBALS['xoopsDB']->getRowsNum($result) > 0;
404
    }
405
406
    /**
407
     * Add a field to a mysql table
408
     *
409
     * @param $field
410
     * @param $table
411
     * @return bool|\mysqli_result
412
     */
413
    public static function addField($field, $table)
414
    {
415
        global $xoopsDB;
416
        return $xoopsDB->queryF('ALTER TABLE ' . $table . " ADD $field;");
417
    }
418
}
419