SysUtility::truncateHtml()   F
last analyzed

Complexity

Conditions 19
Paths 194

Size

Total Lines 90
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 19
eloc 48
c 1
b 0
f 0
nc 194
nop 5
dl 0
loc 90
rs 3.7333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Xhttperror\Common;
4
5
/*
6
 Utility Class Definition
7
8
 You may not change or alter any portion of this comment or credits of
9
 supporting developers from this source code or any supporting source code
10
 which is considered copyrighted (c) material of the original comment or credit
11
 authors.
12
13
 This program is distributed in the hope that it will be useful, but
14
 WITHOUT ANY WARRANTY; without even the implied warranty of
15
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
 */
17
18
/**
19
 * @license      https://www.fsf.org/copyleft/gpl.html GNU public license
20
 * @copyright    https://xoops.org 2000-2020 &copy; XOOPS Project
21
 * @author       ZySpec <[email protected]>
22
 * @author       Mamba <[email protected]>
23
 */
24
25
use Xmf\Request;
26
use MyTextSanitizer;
27
use XoopsFormDhtmlTextArea;
28
use XoopsFormTextArea;
29
use XoopsModules\Xhttperror\{
30
    Helper
31
};
32
33
34
/**
35
 * Class SysUtility
36
 */
37
class SysUtility
38
{
39
    use VersionChecks; //checkVerXoops, checkVerPhp Traits
0 ignored issues
show
introduced by
The trait XoopsModules\Xhttperror\Common\VersionChecks requires some properties which are not provided by XoopsModules\Xhttperror\Common\SysUtility: $tag_name, $prerelease
Loading history...
40
    use ServerStats; // getServerStats Trait
41
    use FilesManagement; // Files Management Trait
42
43
    //--------------- Common module methods -----------------------------
44
45
    /**
46
     * Access the only instance of this class
47
     *
48
     * @return SysUtility
49
     *
50
     */
51
    public static function getInstance()
52
    {
53
        static $instance;
54
        if (null === $instance) {
55
            $instance = new static();
56
        }
57
58
        return $instance;
59
    }
60
61
    /**
62
     * @param $text
63
     * @param $form_sort
64
     * @return string
65
     */
66
    public static function selectSorting($text, $form_sort)
67
    {
68
        global $start, $order, $file_cat, $sort, $xoopsModule;
69
70
        $select_view   = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $select_view is dead and can be removed.
Loading history...
71
        $moduleDirName = basename(dirname(__DIR__));
0 ignored issues
show
Unused Code introduced by
The assignment to $moduleDirName is dead and can be removed.
Loading history...
72
        /** @var Helper $helper */
73
        $helper = Helper::getInstance();
74
75
        //$pathModIcon16 = XOOPS_URL . '/modules/' . $moduleDirName . '/' . $helper->getConfig('modicons16');
76
        $pathModIcon16 = $helper->url($helper->getModule()->getInfo('modicons16'));
0 ignored issues
show
Bug introduced by
It seems like $helper->getModule()->getInfo('modicons16') can also be of type array; however, parameter $url of Xmf\Module\Helper\GenericHelper::url() 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

76
        $pathModIcon16 = $helper->url(/** @scrutinizer ignore-type */ $helper->getModule()->getInfo('modicons16'));
Loading history...
77
78
        $select_view = '<form name="form_switch" id="form_switch" action="' . Request::getString('REQUEST_URI', '', 'SERVER') . '" method="post"><span style="font-weight: bold;">' . $text . '</span>';
79
        //$sorts =  $sort ==  'asc' ? 'desc' : 'asc';
80
        if ($form_sort == $sort) {
81
            $sel1 = 'asc' === $order ? 'selasc.png' : 'asc.png';
82
            $sel2 = 'desc' === $order ? 'seldesc.png' : 'desc.png';
83
        } else {
84
            $sel1 = 'asc.png';
85
            $sel2 = 'desc.png';
86
        }
87
        $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>';
88
        $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>';
89
        $select_view .= '</form>';
90
91
        return $select_view;
92
    }
93
94
    /***************Blocks***************/
95
    /**
96
     * @param array $cats
97
     * @return string
98
     */
99
    public static function blockAddCatSelect($cats)
100
    {
101
        $cat_sql = '';
102
        if (is_array($cats) && !empty($cats)) {
103
            $cat_sql = '(' . current($cats);
104
            array_shift($cats);
105
            foreach ($cats as $cat) {
106
                $cat_sql .= ',' . $cat;
107
            }
108
            $cat_sql .= ')';
109
        }
110
111
        return $cat_sql;
112
    }
113
114
    /**
115
     * @param $content
116
     */
117
    public static function metaKeywords($content)
118
    {
119
        global $xoopsTpl, $xoTheme;
120
        $myts    = \MyTextSanitizer::getInstance();
121
        $content = $myts->undoHtmlSpecialChars($myts->displayTarea($content));
122
        if (null !== $xoTheme && is_object($xoTheme)) {
123
            $xoTheme->addMeta('meta', 'keywords', strip_tags($content));
124
        } else {    // Compatibility for old Xoops versions
125
            $xoopsTpl->assign('xoops_metaKeywords', strip_tags($content));
126
        }
127
    }
128
129
    /**
130
     * @param $content
131
     */
132
    public static function metaDescription($content)
133
    {
134
        global $xoopsTpl, $xoTheme;
135
        $myts    = \MyTextSanitizer::getInstance();
136
        $content = $myts->undoHtmlSpecialChars($myts->displayTarea($content));
137
        if (null !== $xoTheme && is_object($xoTheme)) {
138
            $xoTheme->addMeta('meta', 'description', strip_tags($content));
139
        } else {    // Compatibility for old Xoops versions
140
            $xoopsTpl->assign('xoops_metaDescription', strip_tags($content));
141
        }
142
    }
143
144
    /**
145
     * @param $tableName
146
     * @param $columnName
147
     *
148
     * @return array
149
     */
150
    public static function enumerate($tableName, $columnName)
151
    {
152
        $table = $GLOBALS['xoopsDB']->prefix($tableName);
153
154
        //    $result = $GLOBALS['xoopsDB']->query("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
155
        //        WHERE TABLE_NAME = '" . $table . "' AND COLUMN_NAME = '" . $columnName . "'")
156
        //    || exit ($GLOBALS['xoopsDB']->error());
157
158
        $sql    = 'SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = "' . $table . '" AND COLUMN_NAME = "' . $columnName . '"';
159
        $result = $GLOBALS['xoopsDB']->query($sql);
160
        if (!$result) {
161
            exit($GLOBALS['xoopsDB']->error());
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
162
        }
163
164
        $row      = $GLOBALS['xoopsDB']->fetchBoth($result);
165
        $enumList = explode(',', str_replace("'", '', substr($row['COLUMN_TYPE'], 5, - 6)));
166
        return $enumList;
167
    }
168
169
170
    /**
171
     * @param array|string $tableName
172
     * @param int          $id_field
173
     * @param int          $id
174
     *
175
     * @return mixed
176
     */
177
    public static function cloneRecord($tableName, $id_field, $id)
178
    {
179
        $new_id = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $new_id is dead and can be removed.
Loading history...
180
        $table  = $GLOBALS['xoopsDB']->prefix($tableName);
181
        // copy content of the record you wish to clone
182
        $sql = "SELECT * FROM $table WHERE $id_field='$id' ";
183
        $tempTable = $GLOBALS['xoopsDB']->fetchArray($GLOBALS['xoopsDB']->query($sql), MYSQLI_ASSOC);
184
        if (!$tempTable) {
185
            exit($GLOBALS['xoopsDB']->error());
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
186
        }
187
        // set the auto-incremented id's value to blank.
188
        unset($tempTable[$id_field]);
189
        // insert cloned copy of the original  record
190
        $sql    = "INSERT INTO $table (" . implode(', ', array_keys($tempTable)) . ") VALUES ('" . implode("', '", array_values($tempTable)) . "')";
191
        $result = $GLOBALS['xoopsDB']->queryF($sql);
192
        if (!$result) {
193
            exit($GLOBALS['xoopsDB']->error());
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
194
        }
195
        // Return the new id
196
        $new_id = $GLOBALS['xoopsDB']->getInsertId();
197
198
        return $new_id;
199
    }
200
    /**
201
     * truncateHtml can truncate a string up to a number of characters while preserving whole words and HTML tags
202
     * www.gsdesign.ro/blog/cut-html-string-without-breaking-the-tags
203
     * www.cakephp.org
204
     *
205
     * @param string $text         String to truncate.
206
     * @param int    $length       Length of returned string, including ellipsis.
207
     * @param string $ending       Ending to be appended to the trimmed string.
208
     * @param bool   $exact        If false, $text will not be cut mid-word
209
     * @param bool   $considerHtml If true, HTML tags would be handled correctly
210
     *
211
     * @return string Trimmed string.
212
     */
213
    public static function truncateHtml($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true)
214
    {
215
        $openTags = [];
216
        if ($considerHtml) {
217
            // if the plain text is shorter than the maximum length, return the whole text
218
            if (strlen(preg_replace('/<.*?' . '>/', '', $text)) <= $length) {
219
                return $text;
220
            }
221
            // splits all html-tags to scanable lines
222
            preg_match_all('/(<.+?' . '>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
223
            $total_length = strlen($ending);
224
            //$openTags    = [];
225
            $truncate     = '';
226
            foreach ($lines as $line_matchings) {
227
                // if there is any html-tag in this line, handle it and add it (uncounted) to the output
228
                if (!empty($line_matchings[1])) {
229
                    // if it's an "empty element" with or without xhtml-conform closing slash
230
                    if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
231
                        // do nothing
232
                        // if tag is a closing tag
233
                    } elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
234
                        // delete tag from $openTags list
235
                        $pos = array_search($tag_matchings[1], $openTags);
236
                        if (false !== $pos) {
237
                            unset($openTags[$pos]);
238
                        }
239
                        // if tag is an opening tag
240
                    } elseif (preg_match('/^<\s*([^\s>!]+).*?' . '>$/s', $line_matchings[1], $tag_matchings)) {
241
                        // add tag to the beginning of $openTags list
242
                        array_unshift($openTags, strtolower($tag_matchings[1]));
243
                    }
244
                    // add html-tag to $truncate'd text
245
                    $truncate .= $line_matchings[1];
246
                }
247
                // calculate the length of the plain text part of the line; handle entities as one character
248
                $content_length = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
249
                if ($total_length + $content_length > $length) {
250
                    // the number of characters which are left
251
                    $left            = $length - $total_length;
252
                    $entities_length = 0;
253
                    // search for html entities
254
                    if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) {
255
                        // calculate the real length of all entities in the legal range
256
                        foreach ($entities[0] as $entity) {
257
                            if ($entity[1] + 1 - $entities_length <= $left) {
258
                                $left--;
259
                                $entities_length += strlen($entity[0]);
260
                            } else {
261
                                // no more characters left
262
                                break;
263
                            }
264
                        }
265
                    }
266
                    $truncate .= substr($line_matchings[2], 0, $left + $entities_length);
267
                    // maximum lenght is reached, so get off the loop
268
                    break;
269
                } else {
270
                $truncate     .= $line_matchings[2];
271
                $total_length += $content_length;
272
                }
273
                // if the maximum length is reached, get off the loop
274
                if ($total_length >= $length) {
275
                    break;
276
                }
277
            }
278
        } elseif (strlen($text) <= $length) {
279
                return $text;
280
            } else {
281
                $truncate = substr($text, 0, $length - strlen($ending));
282
            }
283
284
        // if the words shouldn't be cut in the middle...
285
        if (!$exact) {
286
            // ...search the last occurance of a space...
287
            $spacepos = mb_strrpos($truncate, ' ');
288
            if (isset($spacepos)) {
289
                // ...and cut the text in this position
290
                $truncate = substr($truncate, 0, $spacepos);
291
            }
292
        }
293
        // add the defined ending to the text
294
        $truncate .= $ending;
295
        if ($considerHtml) {
296
            // close all unclosed html-tags
297
            foreach ($openTags as $tag) {
298
                $truncate .= '</' . $tag . '>';
299
            }
300
        }
301
302
        return $truncate;
303
    }
304
305
    /**
306
     * @param \Xmf\Module\Helper $helper
307
     * @param array|null         $options
308
     * @return \XoopsFormDhtmlTextArea|\XoopsFormEditor
309
     */
310
    public static function getEditor($helper = null, $options = null)
311
    {
312
        /** @var Helper $helper */
313
        if (null === $options) {
314
            $options           = [];
315
            $options['name']   = 'Editor';
316
            $options['value']  = 'Editor';
317
            $options['rows']   = 10;
318
            $options['cols']   = '100%';
319
            $options['width']  = '100%';
320
            $options['height'] = '400px';
321
        }
322
323
        if (null === $helper) {
324
            $helper = Helper::getInstance();
325
        }
326
327
        $isAdmin = $helper->isUserAdmin();
328
329
        if (class_exists('XoopsFormEditor')) {
330
            if ($isAdmin) {
331
                $descEditor = new \XoopsFormEditor(ucfirst($options['name']), $helper->getConfig('editorAdmin'), $options, $nohtml = false, $onfailure = 'textarea');
332
            } else {
333
                $descEditor = new \XoopsFormEditor(ucfirst($options['name']), $helper->getConfig('editorUser'), $options, $nohtml = false, $onfailure = 'textarea');
334
            }
335
        } else {
336
            $descEditor = new \XoopsFormDhtmlTextArea(ucfirst($options['name']), $options['name'], $options['value'], '100%', '100%');
0 ignored issues
show
Bug introduced by
'100%' of type string is incompatible with the type integer expected by parameter $cols of XoopsFormDhtmlTextArea::__construct(). ( Ignorable by Annotation )

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

336
            $descEditor = new \XoopsFormDhtmlTextArea(ucfirst($options['name']), $options['name'], $options['value'], '100%', /** @scrutinizer ignore-type */ '100%');
Loading history...
Bug introduced by
'100%' of type string is incompatible with the type integer expected by parameter $rows of XoopsFormDhtmlTextArea::__construct(). ( Ignorable by Annotation )

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

336
            $descEditor = new \XoopsFormDhtmlTextArea(ucfirst($options['name']), $options['name'], $options['value'], /** @scrutinizer ignore-type */ '100%', '100%');
Loading history...
337
        }
338
339
        //        $form->addElement($descEditor);
340
341
        return $descEditor;
342
    }
343
344
    /**
345
     * @param string $fieldname
346
     * @param string $table
347
     *
348
     * @return bool
349
     */
350
    public static function fieldExists($fieldname, $table)
351
    {
352
        global $xoopsDB;
353
        $result = $xoopsDB->queryF("SHOW COLUMNS FROM   $table LIKE '$fieldname'");
354
355
        return ($xoopsDB->getRowsNum($result) > 0);
356
    }
357
358
    /**
359
     * Function responsible for checking if a directory exists, we can also write in and create an index.html file
360
     *
361
     * @param string $folder The full path of the directory to check
362
     */
363
    public static function prepareFolder($folder)
364
    {
365
        try {
366
            if (!@mkdir($folder) && !is_dir($folder)) {
367
                throw new \RuntimeException(sprintf('Unable to create the %s directory', $folder));
368
            }
369
            file_put_contents($folder . '/index.html', '<script>history.go(-1);</script>');
370
        } catch (\Exception $e) {
371
            echo 'Caught exception: ', $e->getMessage(), "\n", '<br>';
372
        }
373
    }
374
375
    /**
376
     * @param string $tablename
377
     *
378
     * @return bool
379
     */
380
   public static function tableExists($tablename)
381
    {
382
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
383
        trigger_error(__FUNCTION__ . " is deprecated, called from {$trace[0]['file']} line {$trace[0]['line']}");
384
        $GLOBALS['xoopsLogger']->addDeprecated(
385
            basename(dirname(dirname(__DIR__))) .  ' Module: ' . __FUNCTION__ . " function is deprecated, please use Xmf\Database\Tables method(s) instead." . " Called from {$trace[0]['file']}line {$trace[0]['line']}"
386
        );
387
        $result = $GLOBALS['xoopsDB']->queryF("SHOW TABLES LIKE '$tablename'");
388
389
        return ($GLOBALS['xoopsDB']->getRowsNum($result) > 0) ? true : false;
390
    }
391
392
    /**
393
     * Add a field to a mysql table
394
     *
395
     * @param $field
396
     * @param $table
397
     * @return bool|\mysqli_result
398
     */
399
    public static function addField($field, $table)
400
    {
401
        global $xoopsDB;
402
        $result = $xoopsDB->queryF('ALTER TABLE ' . $table . " ADD $field;");
403
404
        return $result;
405
    }
406
}
407