GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

General   F
last analyzed

Complexity

Total Complexity 238

Size/Duplication

Total Lines 1764
Duplicated Lines 0 %

Importance

Changes 16
Bugs 1 Features 0
Metric Value
eloc 490
c 16
b 1
f 0
dl 0
loc 1764
rs 2
wmc 238

54 Methods

Rating   Name   Duplication   Size   Complexity  
A reverse_sanitize() 0 3 1
A substrmax() 0 3 1
A array_remove_duplicates() 0 3 2
A substrmin() 0 3 1
A right() 0 4 1
A tabsToSpaces() 0 3 1
A left() 0 4 1
A unwrapCDATA() 0 3 1
A getExtension() 0 3 1
A sanitizeDouble() 0 5 1
A substr() 0 9 3
A realiseDirectory() 0 18 4
A flattenArray() 0 23 6
A sanitize() 0 5 1
A flattenArraySub() 0 9 3
A checkFileDeletable() 0 7 2
B processFilePostData() 0 25 7
A in_iarray() 0 8 3
F listStructure() 0 61 23
B array_to_xml() 0 27 10
A merge_file_post_data() 0 11 4
B ensureType() 0 20 7
B getPostData() 0 45 11
A checkFileWritable() 0 11 3
B checkFile() 0 16 7
A buildPaginationElement() 0 10 1
A array_find_available_index() 0 17 4
A hash() 0 12 4
B getMimeType() 0 51 5
A wrapInCDATA() 0 18 4
A validateURL() 0 19 5
C validateString() 0 26 12
A formatFilesize() 0 13 3
A createXMLDateObject() 0 26 5
A deleteFile() 0 16 5
A createHandle() 0 43 6
A convertHumanFileSizeToBytes() 0 22 4
A cleanArray() 0 7 3
A strlen() 0 6 2
C listDirStructure() 0 33 13
B in_array_multi() 0 19 8
B writeFile() 0 38 7
A strpos() 0 6 2
A intval() 0 7 3
A array_iunique() 0 11 3
A uploadFile() 0 23 5
B validateXML() 0 38 6
A array_map_recursive() 0 13 3
A checkFileReadable() 0 5 2
A limitWords() 0 28 5
A in_array_all() 0 9 3
B deleteDirectory() 0 28 9
A createFilename() 0 21 3
A countWords() 0 20 2

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
/**
3
 * @package toolkit
4
 */
5
/**
6
 * General is a utility class that offers a number miscellaneous of
7
 * functions that are used throughout Symphony.
8
 */
9
10
class General
11
{
12
    /**
13
     * Convert any special characters into their entity equivalents. Since
14
     * Symphony 2.3, this function assumes UTF-8 and will not double
15
     * encode strings.
16
     *
17
     * @uses htmlspecialchars()
18
     * @param string $source
19
     *  a string to operate on.
20
     * @return string
21
     *  the encoded version of the string.
22
     */
23
    public static function sanitize($source)
24
    {
25
        $source = htmlspecialchars($source, ENT_COMPAT, 'UTF-8', false);
26
27
        return $source;
28
    }
29
30
    /**
31
     * Convert any special characters into their entity equivalents.
32
     * Contrary to `sanitize()`, this version does double encode existing entities.
33
     *
34
     * @since Symphony 2.7.5
35
     * @uses htmlspecialchars()
36
     * @param string $source
37
     *  a string to operate on.
38
     * @return string
39
     *  the fully encoded version of the string.
40
     */
41
    public static function sanitizeDouble($source)
42
    {
43
        $source = htmlspecialchars($source, ENT_COMPAT, 'UTF-8', true);
44
45
        return $source;
46
    }
47
48
    /**
49
     * Revert any html entities to their character equivalents.
50
     *
51
     * @param string $str
52
     *  a string to operate on
53
     * @return string
54
     *  the decoded version of the string
55
     */
56
    public static function reverse_sanitize($str)
57
    {
58
        return htmlspecialchars_decode($str, ENT_COMPAT);
59
    }
60
61
    /**
62
     * Validate a string against a set of regular expressions.
63
     *
64
     * @param array|string $string
65
     *  string to operate on
66
     * @param array|string $rule
67
     *  a single rule or array of rules
68
     * @return boolean
69
     *  false if any of the rules in $rule do not match any of the strings in
70
     *  `$string`, return true otherwise.
71
     */
72
    public static function validateString($string, $rule)
73
    {
74
        if (!is_array($rule) && ($rule == '' || $rule == null)) {
75
            return true;
76
        }
77
78
        if (!is_array($string) && ($string == '' || $rule == null)) {
79
            return true;
80
        }
81
82
        if (!is_array($rule)) {
83
            $rule = array($rule);
84
        }
85
86
        if (!is_array($string)) {
87
            $string = array($string);
88
        }
89
90
        foreach ($rule as $r) {
91
            foreach ($string as $s) {
92
                if (!preg_match($r, $s)) {
93
                    return false;
94
                }
95
            }
96
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
97
        return true;
98
    }
99
100
    /**
101
     * Replace the tabs with spaces in the input string.
102
     *
103
     * @param string $string
104
     *  the string in which to replace the tabs with spaces.
105
     * @param integer $spaces (optional)
106
     *  the number of spaces to replace each tab with. This argument is optional
107
     *  with a default of 4.
108
     * @return string
109
     *  the resulting string.
110
     */
111
    public static function tabsToSpaces($string, $spaces = 4)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$spaces" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$spaces"; expected 0 but found 1
Loading history...
112
    {
113
        return str_replace("\t", str_pad(null, $spaces), $string);
114
    }
115
116
    /**
117
     * Checks an xml document for well-formedness.
118
     *
119
     * @param string $data
120
     *  filename, xml document as a string, or arbitrary string
121
     * @param pointer &$errors
0 ignored issues
show
Bug introduced by
The type pointer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
122
     *  pointer to an array which will contain any validation errors
123
     * @param boolean $isFile (optional)
124
     *  if this is true, the method will attempt to read from a file, `$data`
125
     *  instead.
126
     * @param XsltProcess $xsltProcessor (optional)
127
     *  if set, the validation will be done using this XSLT processor rather
128
     *  than the built in XML parser. the default is null.
129
     * @param string $encoding (optional)
130
     *  if no XML header is expected, than this should be set to match the
131
     *  encoding of the XML
132
     * @return boolean
133
     *  true if there are no errors in validating the XML, false otherwise.
134
     */
135
    public static function validateXML($data, &$errors, $isFile = true, $xsltProcessor = null, $encoding = 'UTF-8')
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$isFile" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$isFile"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$xsltProcessor" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$xsltProcessor"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$encoding" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$encoding"; expected 0 but found 1
Loading history...
136
    {
137
        $_data = ($isFile) ? file_get_contents($data) : $data;
138
        $_data = preg_replace('/<!DOCTYPE[-.:"\'\/\\w\\s]+>/', null, $_data);
139
140
        if (strpos($_data, '<?xml') === false) {
141
            $_data = '<?xml version="1.0" encoding="'.$encoding.'"?><rootelement>'.$_data.'</rootelement>';
142
        }
143
144
        if (is_object($xsltProcessor)) {
145
            $xsl = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
146
147
            <xsl:template match="/"></xsl:template>
148
149
            </xsl:stylesheet>';
150
151
            $xsltProcessor->process($_data, $xsl, array());
152
153
            if ($xsltProcessor->isErrors()) {
154
                $errors = $xsltProcessor->getError(true);
155
                return false;
156
            }
157
        } else {
158
            $_parser = xml_parser_create();
159
            xml_parser_set_option($_parser, XML_OPTION_SKIP_WHITE, 0);
160
            xml_parser_set_option($_parser, XML_OPTION_CASE_FOLDING, 0);
161
162
            if (!xml_parse($_parser, $_data)) {
163
                $errors = array('error' => xml_get_error_code($_parser) . ': ' . xml_error_string(xml_get_error_code($_parser)),
164
                                'col' => xml_get_current_column_number($_parser),
165
                                'line' => (xml_get_current_line_number($_parser) - 2));
166
                return false;
167
            }
168
169
            xml_parser_free($_parser);
170
        }
171
172
        return true;
173
    }
174
175
    /**
176
     * Check that a string is a valid URL.
177
     *
178
     * @param string $url
179
     *  string to operate on
180
     * @return string
181
     *  a blank string or a valid URL
182
     */
183
    public static function validateURL($url = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$url" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$url"; expected 0 but found 1
Loading history...
184
    {
185
        $url = trim($url);
186
187
        if (is_null($url) || $url == '') {
188
            return $url;
189
        }
190
191
        if (!preg_match('#^http[s]?:\/\/#i', $url)) {
192
            $url = 'http://' . $url;
193
        }
194
195
        include TOOLKIT . '/util.validators.php';
0 ignored issues
show
Bug introduced by
The constant TOOLKIT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
196
197
        if (!preg_match($validators['URI'], $url)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $validators seems to be never defined.
Loading history...
198
            $url = '';
199
        }
200
201
        return $url;
202
    }
203
204
    /**
205
     * Strip any slashes from all array values.
206
     *
207
     * @param array &$arr
208
     *  Pointer to an array to operate on. Can be multi-dimensional.
209
     */
210
    public static function cleanArray(array &$arr)
211
    {
212
        foreach ($arr as $k => $v) {
213
            if (is_array($v)) {
214
                self::cleanArray($arr[$k]);
215
            } else {
216
                $arr[$k] = stripslashes($v);
217
            }
218
        }
219
    }
220
221
    /**
222
     * Flatten the input array. Any elements of the input array that are
223
     * themselves arrays will be removed and the contents of the removed array
224
     * inserted in its place. The keys for the inserted values will be the
225
     * concatenation of the keys in the original arrays in which it was embedded.
226
     * The elements of the path are separated by periods (.). For example,
227
     * given the following nested array structure:
228
     * `
229
     * array(1 =>
230
     *          array('key' => 'value'),
231
     *      2 =>
232
     *          array('key2' => 'value2', 'key3' => 'value3')
233
     *      )
234
     * `
235
     * will flatten to:
236
     * `array('1.key' => 'value', '2.key2' => 'value2', '2.key3' => 'value3')`
237
     *
238
     * @param array &$source
239
     *  The array to flatten, passed by reference
240
     * @param array &$output (optional)
241
     *  The array in which to store the flattened input, passed by reference.
242
     *  if this is not provided then a new array will be created.
243
     * @param string $path (optional)
244
     *  the current prefix of the keys to insert into the output array. this
245
     *  defaults to null.
246
     */
247
    public static function flattenArray(array &$source, &$output = null, $path = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$output" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$output"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$path" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$path"; expected 0 but found 1
Loading history...
248
    {
249
        if (is_null($output)) {
250
            $output = array();
251
        }
252
253
        foreach ($source as $key => $value) {
254
            if (is_int($key)) {
255
                $key = (string)($key + 1);
256
            }
257
258
            if (!is_null($path)) {
259
                $key = $path . '.' . (string)$key;
260
            }
261
262
            if (is_array($value)) {
263
                self::flattenArray($value, $output, $key);
264
            } else {
265
                $output[$key] = $value;
266
            }
267
        }
268
269
        $source = $output;
270
    }
271
272
    /**
273
     * Flatten the input array. Any elements of the input array that are
274
     * themselves arrays will be removed and the contents of the removed array
275
     * inserted in its place. The keys for the inserted values will be the
276
     * concatenation of the keys in the original arrays in which it was embedded.
277
     * The elements of the path are separated by colons (:). For example, given
278
     * the following nested array structure:
279
     * `
280
     * array(1 =>
281
     *          array('key' => 'value'),
282
     *      2 =>
283
     *          array('key2' => 'value2', 'key3' => 'value3')
284
     *      )
285
     * `
286
     * will flatten to:
287
     * `array('1:key' => 'value', '2:key2' => 'value2', '2:key3' => 'value3')`
288
     *
289
     *
290
     * @param array &$output
291
     *  The array in which to store the flattened input, passed by reference.
292
     * @param array &$source
293
     *  The array to flatten, passed by reference
294
     * @param string $path
295
     *  the current prefix of the keys to insert into the output array.
296
     */
297
    protected static function flattenArraySub(array &$output, array &$source, $path)
298
    {
299
        foreach ($source as $key => $value) {
300
            $key = $path . ':' . $key;
301
302
            if (is_array($value)) {
303
                self::flattenArraySub($output, $value, $key);
304
            } else {
305
                $output[$key] = $value;
306
            }
307
        }
308
    }
309
310
    /**
311
     * Given a string, this will clean it for use as a Symphony handle. Preserves multi-byte characters.
312
     *
313
     * @since Symphony 2.2.1
314
     * @param string $string
315
     *  String to be cleaned up
316
     * @param integer $max_length
317
     *  The maximum number of characters in the handle
318
     * @param string $delim
319
     *  All non-valid characters will be replaced with this
320
     * @param boolean $uriencode
321
     *  Force the resultant string to be uri encoded making it safe for URLs
322
     * @param array $additional_rule_set
323
     *  An array of REGEX patterns that should be applied to the `$string`. This
324
     *  occurs after the string has been trimmed and joined with the `$delim`
325
     * @return string
326
     *  Returns resultant handle
327
     */
328
    public static function createHandle($string, $max_length = 255, $delim = '-', $uriencode = false, $additional_rule_set = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$max_length" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$max_length"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$delim" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$delim"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$uriencode" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$uriencode"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$additional_rule_set" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$additional_rule_set"; expected 0 but found 1
Loading history...
329
    {
330
        $max_length = intval($max_length);
331
332
        // Strip out any tag
333
        $string = strip_tags($string);
334
335
        // Remove punctuation
336
        $string = preg_replace('/[\\.\'"]+/', null, $string);
337
338
        // Trim it
339
        if ($max_length > 0) {
340
            $string = General::limitWords($string, $max_length);
341
        }
342
343
        // Replace spaces (tab, newline etc) with the delimiter
344
        $string = preg_replace('/[\s]+/', $delim, $string);
345
346
        // Find all legal characters
347
        preg_match_all('/[^<>?@:!\-\/\[-`;‘’…]+/u', $string, $matches);
348
349
        // Join only legal character with the $delim
350
        $string = implode($delim, $matches[0]);
351
352
        // Allow for custom rules
353
        if (is_array($additional_rule_set) && !empty($additional_rule_set)) {
354
            foreach ($additional_rule_set as $rule => $replacement) {
355
                $string = preg_replace($rule, $replacement, $string);
356
            }
357
        }
358
359
        // Remove leading or trailing delim characters
360
        $string = trim($string, $delim);
361
362
        // Encode it for URI use
363
        if ($uriencode) {
364
            $string = urlencode($string);
365
        }
366
367
        // Make it lowercase
368
        $string = strtolower($string);
369
370
        return $string;
371
    }
372
373
    /**
374
     * Given a string, this will clean it for use as a filename. Preserves multi-byte characters.
375
     *
376
     * @since Symphony 2.2.1
377
     * @param string $string
378
     *  String to be cleaned up
379
     * @param string $delim
380
     *  All non-valid characters will be replaced with this
381
     * @return string
382
     *  Returns created filename
383
     */
384
    public static function createFilename($string, $delim = '-')
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$delim" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$delim"; expected 0 but found 1
Loading history...
385
    {
386
        // Strip out any tag
387
        $string = strip_tags($string);
388
389
        // Find all legal characters
390
        $count = preg_match_all('/[\p{L}\w:;.,+=~]+/u', $string, $matches);
391
        if ($count <= 0 || $count == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $count of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
392
            preg_match_all('/[\w:;.,+=~]+/', $string, $matches);
393
        }
394
395
        // Join only legal character with the $delim
396
        $string = implode($delim, $matches[0]);
397
398
        // Remove leading or trailing delim characters
399
        $string = trim($string, $delim);
400
401
        // Make it lowercase
402
        $string = strtolower($string);
403
404
        return $string;
405
    }
406
407
    /**
408
     * Computes the length of the string.
409
     * This function will attempt to use PHP's `mbstring` functions if they are available.
410
     * This function also forces utf-8 encoding.
411
     *
412
     * @since Symphony 2.5.0
413
     * @param string $str
414
     *  the string to operate on
415
     * @return int
416
     *  the string's length
417
     */
418
    public static function strlen($str)
419
    {
420
        if (function_exists('mb_strlen')) {
421
            return mb_strlen($str, 'utf-8');
422
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
423
        return strlen($str);
424
    }
425
426
    /**
427
     * Finds position of the first occurrence of a string in a string.
428
     * This function will attempt to use PHP's `mbstring` functions if they are available.
429
     * This function also forces utf-8 encoding for mbstring.
430
     *
431
     * @since Symphony 2.7.0
432
     * @param string $haystack
433
     *  the string to look into
434
     * @param string $needle
435
     *  the string to look for
436
     * @param int $offset
437
     *  the search offset. If it is not specified, 0 is used.
438
     *  A negative offset counts from the end of the string.
439
     * @return int
440
     *  the numeric position of the first occurrence of needle in the haystack
441
     */
442
    public static function strpos($haystack, $needle, $offset = 0)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$offset" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$offset"; expected 0 but found 1
Loading history...
443
    {
444
        if (function_exists('mb_strpos')) {
445
            return mb_strpos($haystack, $needle, $offset, 'utf-8');
446
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
447
        return strpos($haystack, $needle, $offset);
448
    }
449
450
    /**
451
     * Creates a sub string.
452
     * This function will attempt to use PHP's `mbstring` functions if they are available.
453
     * This function also forces utf-8 encoding.
454
     *
455
     * @since Symphony 2.5.0
456
     * @param string $str
457
     *  the string to operate on
458
     * @param int $start
459
     *  the starting offset
460
     * @param int $length
461
     *  the length of the substring
462
     * @return string
463
     *  the resulting substring
464
     */
465
    public static function substr($str, $start, $length = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$length" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$length"; expected 0 but found 1
Loading history...
466
    {
467
        if (function_exists('mb_substr')) {
468
            return mb_substr($str, $start, $length, 'utf-8');
469
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
470
        if ($length === null) {
471
            return substr($str, $start);
472
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
473
        return substr($str, $start, $length);
474
    }
475
476
    /**
477
     * Extract the first `$val` characters of the input string. If `$val`
478
     * is larger than the length of the input string then the original
479
     * input string is returned.
480
     *
481
     * @param string $str
482
     *  the string to operate on
483
     * @param integer $val
484
     *  the number to compare lengths with
485
     * @return string|boolean
486
     *  the resulting string or false on failure.
487
     */
488
    public static function substrmin($str, $val)
489
    {
490
        return self::substr($str, 0, min(self::strlen($str), $val));
491
    }
492
493
    /**
494
     * Extract the first `$val` characters of the input string. If
495
     * `$val` is larger than the length of the input string then
496
     * the original input string is returned
497
     *
498
     * @param string $str
499
     *  the string to operate on
500
     * @param integer $val
501
     *  the number to compare lengths with
502
     * @return string|boolean
503
     *  the resulting string or false on failure.
504
     */
505
    public static function substrmax($str, $val)
506
    {
507
        return self::substr($str, 0, max(self::strlen($str), $val));
508
    }
509
510
    /**
511
     * Extract the last `$num` characters from a string.
512
     *
513
     * @param string $str
514
     *  the string to extract the characters from.
515
     * @param integer $num
516
     *  the number of characters to extract.
517
     * @return string|boolean
518
     *  a string containing the last `$num` characters of the
519
     *  input string, or false on failure.
520
     */
521
    public static function right($str, $num)
522
    {
523
        $str = self::substr($str, self::strlen($str)-$num, $num);
524
        return $str;
525
    }
526
527
    /**
528
     * Extract the first `$num` characters from a string.
529
     *
530
     * @param string $str
531
     *  the string to extract the characters from.
532
     * @param integer $num
533
     *  the number of characters to extract.
534
     * @return string|boolean
535
     *  a string containing the last `$num` characters of the
536
     *  input string, or false on failure.
537
     */
538
    public static function left($str, $num)
539
    {
540
        $str = self::substr($str, 0, $num);
541
        return $str;
542
    }
543
544
    /**
545
     * Create all the directories as specified by the input path. If the current
546
     * directory already exists, this function will return true.
547
     *
548
     * @param string $path
549
     *  the path containing the directories to create.
550
     * @param string|integer $mode (optional)
551
     *  the permissions (in octal) of the directories to create. Defaults to 0755
552
     * @param boolean $silent (optional)
553
     *  true if an exception should be raised if an error occurs, false
554
     *  otherwise. this defaults to true.
555
     * @throws Exception
556
     * @return boolean
557
     */
558
    public static function realiseDirectory($path, $mode = 0755, $silent = true)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$mode" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$mode"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$silent" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$silent"; expected 0 but found 1
Loading history...
559
    {
560
        if (is_dir($path)) {
561
            return true;
562
        }
563
564
        try {
565
            $current_umask = umask(0);
566
            $success = @mkdir($path, intval($mode, 8), true);
567
            umask($current_umask);
568
569
            return $success;
570
        } catch (Exception $ex) {
571
            if ($silent === false) {
572
                throw new Exception(__('Unable to create path - %s', array($path)));
573
            }
574
575
            return false;
576
        }
577
    }
578
579
    /**
580
     * Recursively deletes all files and directories given a directory. This
581
     * function has two path. This function optionally takes a `$silent` parameter,
582
     * which when `false` will throw an `Exception` if there is an error deleting a file
583
     * or folder.
584
     *
585
     * @since Symphony 2.3
586
     * @param string $dir
587
     *  the path of the directory to delete
588
     * @param boolean $silent (optional)
589
     *  true if an exception should be raised if an error occurs, false
590
     *  otherwise. this defaults to true.
591
     * @throws Exception
592
     * @return boolean
593
     */
594
    public static function deleteDirectory($dir, $silent = true)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$silent" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$silent"; expected 0 but found 1
Loading history...
595
    {
596
        try {
597
            if (!@file_exists($dir)) {
598
                return true;
599
            }
600
601
            if (!@is_dir($dir)) {
602
                return @unlink($dir);
603
            }
604
605
            foreach (scandir($dir) as $item) {
606
                if ($item == '.' || $item == '..') {
607
                    continue;
608
                }
609
610
                if (!self::deleteDirectory($dir.DIRECTORY_SEPARATOR.$item)) {
611
                    return false;
612
                }
613
            }
614
615
            return rmdir($dir);
616
        } catch (Exception $ex) {
617
            if ($silent === false) {
618
                throw new Exception(__('Unable to remove - %s', array($dir)));
619
            }
620
621
            return false;
622
        }
623
    }
624
625
    /**
626
     * Search a multi-dimensional array for a value.
627
     *
628
     * @param mixed $needle
629
     *  the value to search for.
630
     * @param array $haystack
631
     *  the multi-dimensional array to search.
632
     * @return boolean
633
     *  true if `$needle` is found in `$haystack`.
634
     *  true if `$needle` == `$haystack`.
635
     *  true if `$needle` is found in any of the arrays contained within `$haystack`.
636
     *  false otherwise.
637
     */
638
    public static function in_array_multi($needle, $haystack)
639
    {
640
        if ($needle == $haystack) {
641
            return true;
642
        }
643
644
        if (is_array($haystack)) {
0 ignored issues
show
introduced by
The condition is_array($haystack) is always true.
Loading history...
645
            foreach ($haystack as $key => $val) {
646
                if (is_array($val)) {
647
                    if (self::in_array_multi($needle, $val)) {
648
                        return true;
649
                    }
650
                } elseif (!strcmp($needle, $key) || !strcmp($needle, $val)) {
651
                    return true;
652
                }
653
            }
654
        }
655
656
        return false;
657
    }
658
659
    /**
660
     * Search an array for multiple values.
661
     *
662
     * @param array $needles
663
     *  the values to search the `$haystack` for.
664
     * @param array $haystack
665
     *  the in which to search for the `$needles`
666
     * @return boolean
667
     *  true if any of the `$needles` are in `$haystack`,
668
     *  false otherwise.
669
     */
670
    public static function in_array_all($needles, $haystack)
671
    {
672
        foreach ($needles as $n) {
673
            if (!in_array($n, $haystack)) {
674
                return false;
675
            }
676
        }
677
678
        return true;
679
    }
680
681
    /**
682
     * Transform a multi-dimensional array to a flat array. The input array
683
     * is expected to conform to the structure of the `$_FILES` variable.
684
     *
685
     * @param array $filedata
686
     *  the raw `$_FILES` data structured array
687
     * @return array
688
     *  the flattened array.
689
     */
690
    public static function processFilePostData($filedata)
691
    {
692
        $result = array();
693
694
        foreach ($filedata as $key => $data) {
695
            foreach ($data as $handle => $value) {
696
                if (is_array($value)) {
697
                    foreach ($value as $index => $pair) {
698
                        if (!is_array($result[$handle][$index])) {
699
                            $result[$handle][$index] = array();
700
                        }
701
702
                        if (!is_array($pair)) {
703
                            $result[$handle][$index][$key] = $pair;
704
                        } else {
705
                            $result[$handle][$index][array_pop(array_keys($pair))][$key] = array_pop(array_values($pair));
0 ignored issues
show
Bug introduced by
array_values($pair) cannot be passed to array_pop() as the parameter $array expects a reference. ( Ignorable by Annotation )

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

705
                            $result[$handle][$index][array_pop(array_keys($pair))][$key] = array_pop(/** @scrutinizer ignore-type */ array_values($pair));
Loading history...
Bug introduced by
array_keys($pair) cannot be passed to array_pop() as the parameter $array expects a reference. ( Ignorable by Annotation )

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

705
                            $result[$handle][$index][array_pop(/** @scrutinizer ignore-type */ array_keys($pair))][$key] = array_pop(array_values($pair));
Loading history...
706
                        }
707
                    }
708
                } else {
709
                    $result[$handle][$key] = $value;
710
                }
711
            }
712
        }
713
714
        return $result;
715
    }
716
717
    /**
718
     * Merge `$_POST` with `$_FILES` to produce a flat array of the contents
719
     * of both. If there is no merge_file_post_data function defined then
720
     * such a function is created. This is necessary to overcome PHP's ability
721
     * to handle forms. This overcomes PHP's convoluted `$_FILES` structure
722
     * to make it simpler to access `multi-part/formdata`.
723
     *
724
     * @return array
725
     *  a flat array containing the flattened contents of both `$_POST` and
726
     *  `$_FILES`.
727
     */
728
    public static function getPostData()
729
    {
730
        if (!function_exists('merge_file_post_data')) {
731
            function merge_file_post_data($type, array $file, &$post)
732
            {
733
                foreach ($file as $key => $value) {
734
                    if (!isset($post[$key])) {
735
                        $post[$key] = array();
736
                    }
737
738
                    if (is_array($value)) {
739
                        merge_file_post_data($type, $value, $post[$key]);
740
                    } else {
741
                        $post[$key][$type] = $value;
742
                    }
743
                }
744
            }
745
        }
746
747
        $files = array(
748
            'name'      => array(),
749
            'type'      => array(),
750
            'tmp_name'  => array(),
751
            'error'     => array(),
752
            'size'      => array()
753
        );
754
        $post = $_POST;
755
756
        if (is_array($_FILES) && !empty($_FILES)) {
757
            foreach ($_FILES as $key_a => $data_a) {
758
                if (!is_array($data_a)) {
759
                    continue;
760
                }
761
762
                foreach ($data_a as $key_b => $data_b) {
763
                    $files[$key_b][$key_a] = $data_b;
764
                }
765
            }
766
        }
767
768
        foreach ($files as $type => $data) {
769
            merge_file_post_data($type, $data, $post);
770
        }
771
772
        return $post;
773
    }
774
775
    /**
776
     * Find the next available index in an array. Works best with numeric keys.
777
     * The next available index is the minimum integer such that the array does
778
     * not have a mapping for that index. Uses the increment operator on the
779
     * index type of the input array, whatever that may do.
780
     *
781
     * @param array $array
782
     *  the array to find the next index for.
783
     * @param mixed $seed (optional)
784
     *  the object with which the search for an empty index is initialized. this
785
     *  defaults to null.
786
     * @return integer
787
     *  the minimum empty index into the input array.
788
     */
789
    public static function array_find_available_index($array, $seed = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$seed" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$seed"; expected 0 but found 1
Loading history...
790
    {
791
        if (!is_null($seed)) {
792
            $index = $seed;
793
        } else {
794
            $keys = array_keys($array);
795
            sort($keys);
796
            $index = array_pop($keys);
797
        }
798
799
        if (isset($array[$index])) {
800
            do {
801
                $index++;
802
            } while (isset($array[$index]));
803
        }
804
805
        return $index;
806
    }
807
808
    /**
809
     * Filter the duplicate values from an array into a new array, optionally
810
     * ignoring the case of the values (assuming they are strings?). A new array
811
     * is returned, the input array is left unchanged.
812
     *
813
     * @param array $array
814
     *  the array to filter.
815
     * @param boolean $ignore_case
816
     *  true if the case of the values in the array should be ignored, false otherwise.
817
     * @return array
818
     *  a new array containing only the unique elements of the input array.
819
     */
820
    public static function array_remove_duplicates(array $array, $ignore_case = false)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$ignore_case" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$ignore_case"; expected 0 but found 1
Loading history...
821
    {
822
        return ($ignore_case === true ? self::array_iunique($array) : array_unique($array));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
823
    }
824
825
    /**
826
     * Test whether a value is in an array based on string comparison, ignoring
827
     * the case of the values.
828
     *
829
     * @param mixed $needle
830
     *  the object to search the array for.
831
     * @param array $haystack
832
     *  the array to search for the `$needle`.
833
     * @return boolean
834
     *  true if the `$needle` is in the `$haystack`, false otherwise.
835
     */
836
    public static function in_iarray($needle, array $haystack)
837
    {
838
        foreach ($haystack as $key => $value) {
839
            if (strcasecmp($value, $needle) == 0) {
840
                return true;
841
            }
842
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
843
        return false;
844
    }
845
846
    /**
847
     * Filter the input array for duplicates, treating each element in the array
848
     * as a string and comparing them using a case insensitive comparison function.
849
     *
850
     * @param array $array
851
     *  the array to filter.
852
     * @return array
853
     *  a new array containing only the unique elements of the input array.
854
     */
855
    public static function array_iunique(array $array)
856
    {
857
        $tmp = array();
858
859
        foreach ($array as $key => $value) {
860
            if (!self::in_iarray($value, $tmp)) {
861
                $tmp[$key] = $value;
862
            }
863
        }
864
865
        return $tmp;
866
    }
867
868
    /**
869
     * Function recursively apply a function to an array's values.
870
     * This will not touch the keys, just the values.
871
     *
872
     * @since Symphony 2.2
873
     * @param string $function
874
     * @param array $array
875
     * @return array
876
     *  a new array with all the values passed through the given `$function`
877
     */
878
    public static function array_map_recursive($function, array $array)
879
    {
880
        $tmp = array();
881
882
        foreach ($array as $key => $value) {
883
            if (is_array($value)) {
884
                $tmp[$key] = self::array_map_recursive($function, $value);
885
            } else {
886
                $tmp[$key] = call_user_func($function, $value);
887
            }
888
        }
889
890
        return $tmp;
891
    }
892
893
    /**
894
     * Convert an array into an XML fragment and append it to an existing
895
     * XML element. Any arrays contained as elements in the input array will
896
     * also be recursively formatted and appended to the input XML fragment.
897
     * The input XML element will be modified as a result of calling this.
898
     *
899
     * @param XMLElement $parent
900
     *  the XML element to append the formatted array data to.
901
     * @param array $data
902
     *  the array to format and append to the XML fragment.
903
     * @param boolean $validate
904
     *  true if the formatted array data should be validated as it is
905
     *  constructed, false otherwise.
906
     */
907
    public static function array_to_xml(XMLElement $parent, array $data, $validate = false)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$validate" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$validate"; expected 0 but found 1
Loading history...
908
    {
909
        foreach ($data as $element_name => $value) {
910
            if (!is_numeric($value) && empty($value)) {
911
                continue;
912
            }
913
914
            if (is_int($element_name)) {
915
                $child = new XMLElement('item');
916
                $child->setAttribute('index', $element_name + 1);
917
            } else {
918
                $child = new XMLElement($element_name, null, array(), true);
919
            }
920
921
            if (is_array($value) || is_object($value)) {
922
                self::array_to_xml($child, (array)$value);
923
924
                if ($child->getNumberOfChildren() == 0) {
925
                    continue;
926
                }
927
            } elseif ($validate === true && !self::validateXML(self::sanitize($value), $errors, false, new XSLTProcess)) {
928
                continue;
929
            } else {
930
                $child->setValue(self::sanitize($value));
931
            }
932
933
            $parent->appendChild($child);
934
        }
935
    }
936
937
    /**
938
     * Create a file at the input path with the (optional) input permissions
939
     * with the input content. This function will ignore errors in opening,
940
     * writing, closing and changing the permissions of the resulting file.
941
     * If opening or writing the file fail then this will return false.
942
     * This method calls `General::checkFileWritable()` which properly checks
943
     * for permissions.
944
     *
945
     * @uses General::checkFileWritable()
946
     * @param string $file
947
     *  the path of the file to write.
948
     * @param mixed $data
949
     *  the data to write to the file.
950
     * @param integer|string $perm (optional)
951
     *  the permissions as an octal number to set set on the resulting file.
952
     *  this defaults to 0644 (if omitted or set to null)
953
     * @param string $mode (optional)
954
     *  the mode that the file should be opened with, defaults to 'w'. See modes
955
     *  at http://php.net/manual/en/function.fopen.php
956
     * @param boolean $trim (optional)
957
     *  removes tripple linebreaks
958
     * @return boolean
959
     *  true if the file is successfully opened, written to, closed and has the
960
     *  required permissions set. false, otherwise.
961
     */
962
    public static function writeFile($file, $data, $perm = 0644, $mode = 'w', $trim = false)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$perm" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$perm"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$mode" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$mode"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$trim" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$trim"; expected 0 but found 1
Loading history...
963
    {
964
        if (static::checkFileWritable($file) === false) {
965
            return false;
966
        }
967
968
        if (!$handle = fopen($file, $mode)) {
969
            return false;
970
        }
971
972
        if ($trim === true) {
973
            $data = preg_replace("/(" . PHP_EOL . "([ |\t]+)?){2,}" . PHP_EOL . "/", PHP_EOL . PHP_EOL, trim($data));
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /( does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal / does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
974
        }
975
976
        if (fwrite($handle, $data, strlen($data)) === false) {
977
            return false;
978
        }
979
980
        fclose($handle);
981
982
        try {
983
            if (is_null($perm)) {
0 ignored issues
show
introduced by
The condition is_null($perm) is always false.
Loading history...
984
                $perm = 0644;
985
            }
986
987
            @chmod($file, intval($perm, 8));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

987
            /** @scrutinizer ignore-unhandled */ @chmod($file, intval($perm, 8));

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...
988
        } catch (Exception $ex) {
989
            // If we can't chmod the file, this is probably because our host is
990
            // running PHP with a different user to that of the file. Although we
991
            // can delete the file, create a new one and then chmod it, we run the
992
            // risk of losing the file as we aren't saving it anywhere. For the immediate
993
            // future, atomic saving isn't needed by Symphony and it's recommended that
994
            // if your extension require this logic, it uses it's own function rather
995
            // than this 'General' one.
996
            return true;
997
        }
998
999
        return true;
1000
    }
1001
1002
    /**
1003
     * Checks that the file and its folder are readable and writable.
1004
     *
1005
     * @deprecated @since Symphony 2.7.0
1006
     * @since Symphony 2.6.3
1007
     * @return boolean
1008
     */
1009
    public static function checkFile($file)
1010
    {
1011
        if (Symphony::Log()) {
1012
            Symphony::Log()->pushDeprecateWarningToLog('General::checkFile()', '`General::checkFileWritable()');
1013
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1014
        clearstatcache();
1015
        $dir = dirname($file);
1016
1017
        if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
1018
            (!is_writable($dir) || !is_readable($dir)) // Folder
1019
            || (file_exists($file) && (!is_readable($file) || !is_writable($file))) // File
1020
        ) {
1021
            return false;
1022
        }
1023
1024
        return true;
1025
    }
1026
1027
    /**
1028
     * Checks that the file is readable.
1029
     * It first checks to see if the $file path exists
1030
     * and if it does, checks that it is readable.
1031
     *
1032
     * @uses clearstatcache()
1033
     * @since Symphony 2.7.0
1034
     * @param string $file
1035
     *  The path of the file
1036
     * @return boolean
1037
     */
1038
    public static function checkFileReadable($file)
1039
    {
1040
        clearstatcache();
1041
        // Reading a file requires that is exists and can be read
1042
        return @file_exists($file) && @is_readable($file);
1043
    }
1044
1045
    /**
1046
     * Checks that the file is writable.
1047
     * It first checks to see if the $file path exists
1048
     * and if it does, checks that is it writable. If the file
1049
     * does not exits, it checks that the directory exists and if it does,
1050
     * checks that it is writable.
1051
     *
1052
     * @uses clearstatcache()
1053
     * @since Symphony 2.7.0
1054
     * @param string $file
1055
     *  The path of the file
1056
     * @return boolean
1057
     */
1058
    public static function checkFileWritable($file)
1059
    {
1060
        clearstatcache();
1061
        if (@file_exists($file)) {
1062
            // Writing to an existing file does not require write
1063
            // permissions on the directory.
1064
            return @is_writable($file);
1065
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1066
        $dir = dirname($file);
1067
        // Creating a file requires write permissions on the directory.
1068
        return @file_exists($dir) && @is_writable($dir);
1069
    }
1070
1071
    /**
1072
     * Checks that the file is deletable.
1073
     * It first checks to see if the $file path exists
1074
     * and if it does, checks that is it writable.
1075
     *
1076
     * @uses clearstatcache()
1077
     * @since Symphony 2.7.0
1078
     * @param string $file
1079
     *  The path of the file
1080
     * @return boolean
1081
     */
1082
    public static function checkFileDeletable($file)
1083
    {
1084
        clearstatcache();
1085
        $dir = dirname($file);
1086
        // Deleting a file requires write permissions on the directory.
1087
        // It does not require write permissions on the file
1088
        return @file_exists($dir) && @is_writable($dir);
1089
    }
1090
1091
    /**
1092
     * Delete a file at a given path, silently ignoring errors depending
1093
     * on the value of the input variable $silent.
1094
     *
1095
     * @uses General::checkFileDeletable()
1096
     * @param string $file
1097
     *  the path of the file to delete
1098
     * @param boolean $silent (optional)
1099
     *  true if an exception should be raised if an error occurs, false
1100
     *  otherwise. this defaults to true.
1101
     * @throws Exception
1102
     * @return boolean
1103
     *  true if the file is successfully unlinked, if the unlink fails and
1104
     *  silent is set to true then an exception is thrown. if the unlink
1105
     *  fails and silent is set to false then this returns false.
1106
     */
1107
    public static function deleteFile($file, $silent = true)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$silent" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$silent"; expected 0 but found 1
Loading history...
1108
    {
1109
        try {
1110
            if (static::checkFileDeletable($file) === false) {
1111
                throw new Exception(__('Denied by permission'));
1112
            }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1113
            if (!@file_exists($file)) {
1114
                return true;
1115
            }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1116
            return @unlink($file);
1117
        } catch (Exception $ex) {
1118
            if ($silent === false) {
1119
                throw new Exception(__('Unable to remove file - %s', array($file)), 0, $ex);
1120
            }
1121
1122
            return false;
1123
        }
1124
    }
1125
1126
    /**
1127
     * Extract the file extension from the input file path.
1128
     *
1129
     * @param string $file
1130
     *  the path of the file to extract the extension of.
1131
     * @return array
1132
     *  an array with a single key 'extension' and a value of the extension
1133
     *  of the input path.
1134
     */
1135
    public static function getExtension($file)
1136
    {
1137
        return pathinfo($file, PATHINFO_EXTENSION);
1138
    }
1139
1140
    /**
1141
     * Gets mime type of a file.
1142
     *
1143
     * For email attachments, the mime type is very important.
1144
     * Uses the PHP 5.3 function `finfo_open` when available, otherwise falls
1145
     * back to using a mapping of known of common mimetypes. If no matches
1146
     * are found `application/octet-stream` will be returned.
1147
     *
1148
     * @author Michael Eichelsdoerfer
1149
     * @author Huib Keemink
1150
     * @param string $file
1151
     * @return string|boolean
1152
     *  the mime type of the file, or false is none found
1153
     */
1154
    public function getMimeType($file)
1155
    {
1156
        if (!empty($file)) {
1157
            // in PHP 5.3 we can use 'finfo'
1158
            if (PHP_VERSION_ID >= 50300 && function_exists('finfo_open')) {
1159
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
1160
                $mime_type = finfo_file($finfo, $file);
1161
                finfo_close($finfo);
1162
            } else {
1163
                // A few mimetypes to "guess" using the file extension.
1164
                $mimetypes = array(
1165
                    'txt'   => 'text/plain',
1166
                    'csv'   => 'text/csv',
1167
                    'pdf'   => 'application/pdf',
1168
                    'doc'   => 'application/msword',
1169
                    'docx'  => 'application/msword',
1170
                    'xls'   => 'application/vnd.ms-excel',
1171
                    'ppt'   => 'application/vnd.ms-powerpoint',
1172
                    'eps'   => 'application/postscript',
1173
                    'zip'   => 'application/zip',
1174
                    'gif'   => 'image/gif',
1175
                    'jpg'   => 'image/jpeg',
1176
                    'jpeg'  => 'image/jpeg',
1177
                    'png'   => 'image/png',
1178
                    'mp3'   => 'audio/mpeg',
1179
                    'mp4a'  => 'audio/mp4',
1180
                    'aac'   => 'audio/x-aac',
1181
                    'aif'   => 'audio/x-aiff',
1182
                    'aiff'  => 'audio/x-aiff',
1183
                    'wav'   => 'audio/x-wav',
1184
                    'wma'   => 'audio/x-ms-wma',
1185
                    'mpeg'  => 'video/mpeg',
1186
                    'mpg'   => 'video/mpeg',
1187
                    'mp4'   => 'video/mp4',
1188
                    'mov'   => 'video/quicktime',
1189
                    'avi'   => 'video/x-msvideo',
1190
                    'wmv'   => 'video/x-ms-wmv',
1191
                );
1192
1193
                $extension = substr(strrchr($file, '.'), 1);
1194
1195
                if ($mimetypes[strtolower($extension)] !== null) {
0 ignored issues
show
introduced by
The condition $mimetypes[strtolower($extension)] !== null is always true.
Loading history...
1196
                    $mime_type = $mimetypes[$extension];
1197
                } else {
1198
                    $mime_type = 'application/octet-stream';
1199
                }
1200
            }
1201
1202
            return $mime_type;
1203
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1204
        return false;
1205
    }
1206
1207
    /**
1208
     * Construct a multi-dimensional array that reflects the directory
1209
     * structure of a given path.
1210
     *
1211
     * @param string $dir (optional)
1212
     *  the path of the directory to construct the multi-dimensional array
1213
     *  for. this defaults to '.'.
1214
     * @param string $filter (optional)
1215
     *  A regular expression to filter the directories. This is positive filter, ie.
1216
     * if the filter matches, the directory is included. Defaults to null.
1217
     * @param boolean $recurse (optional)
1218
     *  true if sub-directories should be traversed and reflected in the
1219
     *  resulting array, false otherwise.
1220
     * @param mixed $strip_root (optional)
1221
     *  If null, the full path to the file will be returned, otherwise the value
1222
     *  of `strip_root` will be removed from the file path.
1223
     * @param array $exclude (optional)
1224
     *  ignore directories listed in this array. this defaults to an empty array.
1225
     * @param boolean $ignore_hidden (optional)
1226
     *  ignore hidden directory (i.e.directories that begin with a period). this defaults
1227
     *  to true.
1228
     * @return null|array
1229
     *  return the array structure reflecting the input directory or null if
1230
     * the input directory is not actually a directory.
1231
     */
1232
    public static function listDirStructure($dir = '.', $filter = null, $recurse = true, $strip_root = null, $exclude = array(), $ignore_hidden = true)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$dir" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$dir"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$filter" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$filter"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$recurse" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$recurse"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$strip_root" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$strip_root"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$exclude" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$exclude"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$ignore_hidden" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$ignore_hidden"; expected 0 but found 1
Loading history...
1233
    {
1234
        if (!is_dir($dir)) {
1235
            return null;
1236
        }
1237
1238
        $files = array();
1239
1240
        foreach (scandir($dir) as $file) {
1241
            if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
1242
                ($file == '.' || $file == '..')
1243
                || ($ignore_hidden && $file{0} == '.')
1244
                || !is_dir("$dir/$file")
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $dir instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1245
                || in_array($file, $exclude)
1246
                || in_array("$dir/$file", $exclude)
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $dir instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1247
            ) {
1248
                continue;
1249
            }
1250
1251
            if (!is_null($filter)) {
1252
                if (!preg_match($filter, $file)) {
1253
                    continue;
1254
                }
1255
            }
1256
1257
            $files[] = rtrim(str_replace($strip_root, '', $dir), '/') ."/$file/";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1258
1259
            if ($recurse) {
1260
                $files = @array_merge($files, self::listDirStructure("$dir/$file", $filter, $recurse, $strip_root, $exclude, $ignore_hidden));
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $dir instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Bug introduced by
Are you sure the usage of self::listDirStructure($...xclude, $ignore_hidden) targeting General::listDirStructure() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1261
            }
1262
        }
1263
1264
        return $files;
1265
    }
1266
1267
    /**
1268
     * Construct a multi-dimensional array that reflects the directory
1269
     * structure of a given path grouped into directory and file keys
1270
     * matching any input constraints.
1271
     *
1272
     * @param string $dir (optional)
1273
     *  the path of the directory to construct the multi-dimensional array
1274
     *  for. this defaults to '.'.
1275
     * @param array|string $filters (optional)
1276
     *  either a regular expression to filter the files by or an array of
1277
     *  files to include.
1278
     * @param boolean $recurse (optional)
1279
     *  true if sub-directories should be traversed and reflected in the
1280
     *  resulting array, false otherwise.
1281
     * @param string $sort (optional)
1282
     *  'asc' if the resulting filelist array should be sorted, anything else otherwise.
1283
     *  this defaults to 'asc'.
1284
     * @param mixed $strip_root (optional)
1285
     *  If null, the full path to the file will be returned, otherwise the value
1286
     *  of `strip_root` will be removed from the file path.
1287
     * @param array $exclude (optional)
1288
     *  ignore files listed in this array. this defaults to an empty array.
1289
     * @param boolean $ignore_hidden (optional)
1290
     *  ignore hidden files (i.e. files that begin with a period). this defaults
1291
     *  to true.
1292
     * @return null|array
1293
     *  return the array structure reflecting the input directory or null if
1294
     * the input directory is not actually a directory.
1295
     */
1296
    public static function listStructure($dir = ".", $filters = array(), $recurse = true, $sort = "asc", $strip_root = null, $exclude = array(), $ignore_hidden = true)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$dir" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$dir"; expected 0 but found 1
Loading history...
Coding Style Comprehensibility introduced by
The string literal . does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style introduced by
Incorrect spacing between argument "$filters" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$filters"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$recurse" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$recurse"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$sort" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$sort"; expected 0 but found 1
Loading history...
Coding Style Comprehensibility introduced by
The string literal asc does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style introduced by
Incorrect spacing between argument "$strip_root" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$strip_root"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$exclude" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$exclude"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$ignore_hidden" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$ignore_hidden"; expected 0 but found 1
Loading history...
1297
    {
1298
        if (!is_dir($dir)) {
1299
            return null;
1300
        }
1301
1302
        // Check to see if $filters is a string containing a regex, or an array of file types
1303
        if (is_array($filters) && !empty($filters)) {
1304
            $filter_type = 'file';
1305
        } elseif (is_string($filters)) {
1306
            $filter_type = 'regex';
1307
        } else {
1308
            $filter_type = null;
1309
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1310
        $files = array();
1311
1312
        $prefix = str_replace($strip_root, '', $dir);
1313
1314
        if ($prefix !== "" && substr($prefix, -1) !== "/") {
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal / does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1315
            $prefix .= "/";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal / does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1316
        }
1317
1318
        $files['dirlist'] = array();
1319
        $files['filelist'] = array();
1320
1321
        foreach (scandir($dir) as $file) {
1322
            if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
1323
                ($file == '.' || $file === '..')
1324
                || ($ignore_hidden && $file{0} === '.')
1325
                || in_array($file, $exclude)
1326
                || in_array("$dir/$file", $exclude)
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $dir instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1327
            ) {
1328
                continue;
1329
            }
1330
1331
            $dir = rtrim($dir, '/');
1332
1333
            if (is_dir("$dir/$file")) {
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $dir instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1334
                if ($recurse) {
1335
                    $files["$prefix$file/"] = self::listStructure("$dir/$file", $filters, $recurse, $sort, $strip_root, $exclude, $ignore_hidden);
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $prefix instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $dir instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Bug introduced by
Are you sure the assignment to $files[$prefix.$file.'/'] is correct as self::listStructure($dir...xclude, $ignore_hidden) targeting General::listStructure() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1336
                }
1337
1338
                $files['dirlist'][] = "$prefix$file/";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $prefix instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1339
            } elseif ($filter_type === 'regex') {
1340
                if (preg_match($filters, $file)) {
1341
                    $files['filelist'][] = "$prefix$file";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $prefix instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1342
                }
1343
            } elseif ($filter_type === 'file') {
1344
                if (in_array(self::getExtension($file), $filters)) {
1345
                    $files['filelist'][] = "$prefix$file";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $prefix instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1346
                }
1347
            } elseif (is_null($filter_type)) {
1348
                $files['filelist'][] = "$prefix$file";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $prefix instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1349
            }
1350
        }
1351
1352
        if (is_array($files['filelist'])) {
1353
            ($sort == 'desc') ? rsort($files['filelist']) : sort($files['filelist']);
1354
        }
1355
1356
        return $files;
1357
    }
1358
1359
    /**
1360
     * Count the number of words in a string. Words are delimited by "spaces".
1361
     * The characters included in the set of "spaces" are:
1362
     *  '&#x2002;', '&#x2003;', '&#x2004;', '&#x2005;',
1363
     *  '&#x2006;', '&#x2007;', '&#x2009;', '&#x200a;',
1364
     *  '&#x200b;', '&#x2002f;', '&#x205f;'
1365
     * Any html/xml tags are first removed by strip_tags() and any included html
1366
     * entities are decoded. The resulting string is then split by the above set
1367
     * of spaces and the resulting size of the resulting array returned.
1368
     *
1369
     * @param string $string
1370
     *  the string from which to count the contained words.
1371
     * @return integer
1372
     *  the number of words contained in the input string.
1373
     */
1374
    public static function countWords($string)
1375
    {
1376
        $string = strip_tags($string);
1377
1378
        // Strip spaces:
1379
        $string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
1380
        $spaces = array(
1381
            '&#x2002;', '&#x2003;', '&#x2004;', '&#x2005;',
1382
            '&#x2006;', '&#x2007;', '&#x2009;', '&#x200a;',
1383
            '&#x200b;', '&#x2002f;', '&#x205f;'
1384
        );
1385
1386
        foreach ($spaces as &$space) {
1387
            $space = html_entity_decode($space, ENT_NOQUOTES, 'UTF-8');
1388
        }
1389
1390
        $string = str_replace($spaces, ' ', $string);
1391
        $string = preg_replace('/[^\w\s]/i', '', $string);
1392
1393
        return str_word_count($string);
1394
    }
1395
1396
    /**
1397
     * Truncate a string to a given length, respecting word boundaries. The returned
1398
     * string will always be less than `$maxChars`. Newlines, HTML elements and
1399
     * leading or trailing spaces are removed from the string.
1400
     *
1401
     * @param string $string
1402
     *  the string to truncate.
1403
     * @param integer $maxChars (optional)
1404
     *  the maximum length of the string to truncate the input string to. this
1405
     *  defaults to 200 characters.
1406
     * @param boolean $appendHellip (optional)
1407
     *  true if the ellipses should be appended to the result in circumstances
1408
     *  where the result is shorter than the input string. false otherwise. this
1409
     *  defaults to false.
1410
     * @return null|string
1411
     *  if the resulting string contains only spaces then null is returned. otherwise
1412
     *  a string that satisfies the input constraints.
1413
     */
1414
    public static function limitWords($string, $maxChars = 200, $appendHellip = false)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$maxChars" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$maxChars"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$appendHellip" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$appendHellip"; expected 0 but found 1
Loading history...
1415
    {
1416
        if ($appendHellip) {
1417
            $maxChars -= 1;
1418
        }
1419
1420
        $string = trim(strip_tags(nl2br($string)));
1421
        $original_length = strlen($string);
1422
1423
        if ($original_length == 0) {
1424
            return null;
1425
        } elseif ($original_length <= $maxChars) {
1426
            return $string;
1427
        }
1428
1429
        // Compute the negative offset
1430
        $offset = $maxChars - $original_length;
1431
        // Find the first word break char before the maxChars limit is hit.
1432
        $last_word_break = max(array_filter(array_map(function ($wb) use ($string, $offset) {
1433
            return strrpos($string, $wb, $offset);
1434
        }, array(' ', '-', ',', '.', '!', '?', PHP_EOL))));
1435
        $result = substr($string, 0, $last_word_break);
1436
1437
        if ($appendHellip) {
1438
            $result .= "&#8230;";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1439
        }
1440
1441
        return $result;
1442
    }
1443
1444
    /**
1445
     * Move a file from the source path to the destination path and name and
1446
     * set its permissions to the input permissions. This will ignore errors
1447
     * in the `is_uploaded_file()`, `move_uploaded_file()` and `chmod()` functions.
1448
     *
1449
     * @uses General::checkFileWritable()
1450
     * @param string $dest_path
1451
     *  the file path to which the source file is to be moved.
1452
     * @param string $dest_name
1453
     *  the file name within the file path to which the source file is to be moved.
1454
     * @param string $tmp_name
1455
     *  the full path name of the source file to move.
1456
     * @param integer|string $perm (optional)
1457
     *  the permissions to apply to the moved file. this defaults to 0644 @since
1458
     *  Symphony 2.7.0. It was 0777 in 2.6.x and less.
1459
     * @return boolean
1460
     *  true if the file was moved and its permissions set as required. false otherwise.
1461
     */
1462
    public static function uploadFile($dest_path, $dest_name, $tmp_name, $perm = 0644)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$perm" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$perm"; expected 0 but found 1
Loading history...
1463
    {
1464
        // Upload the file
1465
        if (@is_uploaded_file($tmp_name)) {
1466
            $dest_path = rtrim($dest_path, '/') . '/';
1467
            $dest = $dest_path . $dest_name;
1468
1469
            // Check that destination is writable
1470
            if (!static::checkFileWritable($dest)) {
1471
                return false;
1472
            }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1473
            // Try place the file in the correction location
1474
            if (@move_uploaded_file($tmp_name, $dest)) {
1475
                if (is_null($perm)) {
0 ignored issues
show
introduced by
The condition is_null($perm) is always false.
Loading history...
1476
                    $perm = 0644;
1477
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1478
                @chmod($dest, intval($perm, 8));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1478
                /** @scrutinizer ignore-unhandled */ @chmod($dest, intval($perm, 8));

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...
1479
                return true;
1480
            }
1481
        }
1482
1483
        // Could not move the file
1484
        return false;
1485
    }
1486
1487
    /**
1488
     * Format a number of bytes in human readable format. This will append MB as
1489
     * appropriate for values greater than 1,024*1,024, KB for values between
1490
     * 1,024 and 1,024*1,024-1 and bytes for values between 0 and 1,024.
1491
     *
1492
     * @param integer $file_size
1493
     *  the number to format.
1494
     * @return string
1495
     *  the formatted number.
1496
     */
1497
    public static function formatFilesize($file_size)
1498
    {
1499
        $file_size = intval($file_size);
1500
1501
        if ($file_size >= (1024 * 1024)) {
1502
            $file_size = number_format($file_size * (1 / (1024 * 1024)), 2) . ' MB';
1503
        } elseif ($file_size >= 1024) {
1504
            $file_size = intval($file_size * (1/1024)) . ' KB';
1505
        } else {
1506
            $file_size = intval($file_size) . ' bytes';
1507
        }
1508
1509
        return $file_size;
1510
    }
1511
1512
    /**
1513
     * Gets the number of bytes from 'human readable' size value. Supports
1514
     * the output of `General::formatFilesize` as well as reading values
1515
     * from the PHP configuration. eg. 1 MB or 1M
1516
     *
1517
     * @since Symphony 2.5.2
1518
     * @param string $file_size
1519
     * @return integer
1520
     */
1521
    public static function convertHumanFileSizeToBytes($file_size)
1522
    {
1523
        $file_size = str_replace(
1524
            array(' MB', ' KB', ' bytes'),
1525
            array('M', 'K', 'B'),
1526
            trim($file_size)
1527
        );
1528
1529
        $last = strtolower($file_size[strlen($file_size)-1]);
1530
1531
        $file_size = (int) $file_size;
1532
1533
        switch ($last) {
1534
            case 'g':
1535
                $file_size *= 1024;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
1536
            case 'm':
1537
                $file_size *= 1024;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
1538
            case 'k':
1539
                $file_size *= 1024;
1540
        }
1541
1542
        return $file_size;
1543
    }
1544
1545
    /**
1546
     * Construct an XML fragment that reflects the structure of the input timestamp.
1547
     *
1548
     * @param integer $timestamp
1549
     *  the timestamp to construct the XML element from.
1550
     * @param string $element (optional)
1551
     *  the name of the element to append to the namespace of the constructed XML.
1552
     *  this defaults to "date".
1553
     * @param string $date_format (optional)
1554
     *  the format to apply to the date, defaults to `Y-m-d`.
1555
     *  if empty, uses DateTimeObj settings.
1556
     * @param string $time_format (optional)
1557
     *  the format to apply to the date, defaults to `H:i`.
1558
     *  if empty, uses DateTimeObj settings.
1559
     * @param string $namespace (optional)
1560
     *  the namespace in which the resulting XML entity will reside. this defaults
1561
     *  to null.
1562
     * @return boolean|XMLElement
1563
     *  false if there is no XMLElement class on the system, the constructed XML element
1564
     *  otherwise.
1565
     */
1566
    public static function createXMLDateObject($timestamp, $element = 'date', $date_format = 'Y-m-d', $time_format = 'H:i', $namespace = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$element" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$element"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$date_format" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$date_format"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$time_format" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$time_format"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$namespace" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$namespace"; expected 0 but found 1
Loading history...
1567
    {
1568
        if (!class_exists('XMLElement')) {
1569
            return false;
1570
        }
1571
1572
        if (empty($date_format)) {
1573
            $date_format = DateTimeObj::getSetting('date_format');
1574
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1575
        if (empty($time_format)) {
1576
            $time_format = DateTimeObj::getSetting('time_format');
1577
        }
1578
1579
        $xDate = new XMLElement(
1580
            (!is_null($namespace) ? $namespace . ':' : '') . $element,
1581
            DateTimeObj::get($date_format, $timestamp),
0 ignored issues
show
Bug introduced by
It seems like $date_format can also be of type array; however, parameter $format of DateTimeObj::get() 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

1581
            DateTimeObj::get(/** @scrutinizer ignore-type */ $date_format, $timestamp),
Loading history...
1582
            array(
1583
                'iso' => DateTimeObj::get('c', $timestamp),
1584
                'timestamp' => DateTimeObj::get('U', $timestamp),
1585
                'time' => DateTimeObj::get($time_format, $timestamp),
1586
                'weekday' => DateTimeObj::get('N', $timestamp),
1587
                'offset' => DateTimeObj::get('O', $timestamp)
1588
            )
1589
        );
1590
1591
        return $xDate;
1592
    }
1593
1594
    /**
1595
     * Construct an XML fragment that describes a pagination structure.
1596
     *
1597
     * @param integer $total_entries (optional)
1598
     *  the total number of entries that this structure is paginating. this
1599
     *  defaults to 0.
1600
     * @param integer $total_pages (optional)
1601
     *  the total number of pages within the pagination structure. this defaults
1602
     *  to 0.
1603
     * @param integer $entries_per_page (optional)
1604
     *  the number of entries per page. this defaults to 1.
1605
     * @param integer $current_page (optional)
1606
     *  the current page within the total number of pages within this pagination
1607
     *  structure. this defaults to 1.
1608
     * @return XMLElement
1609
     *  the constructed XML fragment.
1610
     */
1611
    public static function buildPaginationElement($total_entries = 0, $total_pages = 0, $entries_per_page = 1, $current_page = 1)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$total_entries" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$total_entries"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$total_pages" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$total_pages"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$entries_per_page" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$entries_per_page"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$current_page" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$current_page"; expected 0 but found 1
Loading history...
1612
    {
1613
        $pageinfo = new XMLElement('pagination');
1614
1615
        $pageinfo->setAttribute('total-entries', $total_entries);
1616
        $pageinfo->setAttribute('total-pages', $total_pages);
1617
        $pageinfo->setAttribute('entries-per-page', $entries_per_page);
1618
        $pageinfo->setAttribute('current-page', $current_page);
1619
1620
        return $pageinfo;
1621
    }
1622
1623
    /**
1624
     * Uses `SHA1` or `MD5` to create a hash based on some input
1625
     * This function is currently very basic, but would allow
1626
     * future expansion. Salting the hash comes to mind.
1627
     *
1628
     * @param string $input
1629
     *  the string to be hashed
1630
     * @param string $algorithm
1631
     *  This function supports 'md5', 'sha1' and 'pbkdf2'. Any
1632
     *  other algorithm will default to 'pbkdf2'.
1633
     * @return string
1634
     *  the hashed string
1635
     */
1636
    public static function hash($input, $algorithm = 'sha1')
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$algorithm" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$algorithm"; expected 0 but found 1
Loading history...
1637
    {
1638
        switch ($algorithm) {
1639
            case 'sha1':
1640
                return SHA1::hash($input);
1641
1642
            case 'md5':
1643
                return MD5::hash($input);
1644
1645
            case 'pbkdf2':
1646
            default:
1647
                return Crytography::hash($input, $algorithm);
0 ignored issues
show
Bug introduced by
The type Crytography was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1648
        }
1649
    }
1650
1651
    /**
1652
     * Helper to cut down on variables' type check.
1653
     * Currently known types are the PHP defaults.
1654
     * Uses `is_XXX()` functions internally.
1655
     *
1656
     * @since Symphony 2.3
1657
     *
1658
     * @param array $params - an array of arrays containing variables info
1659
     *
1660
     *  Array[
1661
     *      $key1 => $value1
1662
     *      $key2 => $value2
1663
     *      ...
1664
     *  ]
1665
     *
1666
     *  $key = the name of the variable
1667
     *  $value = Array[
1668
     *      'var' => the variable to check
1669
     *      'type' => enforced type. Must match the XXX part from an `is_XXX()` function
1670
     *      'optional' => boolean. If this is set, the default value of the variable must be null
1671
     *  ]
1672
     *
1673
     * @throws InvalidArgumentException if validator doesn't exist.
1674
     * @throws InvalidArgumentException if variable type validation fails.
1675
     *
1676
     * @example
1677
     *  $color = 'red';
1678
     *  $foo = null;
1679
     *  $bar = 21;
1680
     *
1681
     *  General::ensureType(array(
1682
     *      'color' => array('var' => $color, 'type'=> 'string'),               // success
1683
     *      'foo' => array('var' => $foo, 'type'=> 'int',  'optional' => true), // success
1684
     *      'bar' => array('var' => $bar, 'type'=> 'string')                    // fail
1685
     *  ));
1686
     */
1687
    public static function ensureType(array $params)
1688
    {
1689
        foreach ($params as $name => $param) {
1690
            if (isset($param['optional']) && ($param['optional'] === true)) {
1691
                if (is_null($param['var'])) {
1692
                    continue;
1693
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1694
                // if not null, check it's type
1695
            }
1696
1697
            // validate the validator
1698
            $validator = 'is_'.$param['type'];
1699
1700
            if (!function_exists($validator)) {
1701
                throw new InvalidArgumentException(__('Enforced type `%1$s` for argument `$%2$s` does not match any known variable types.', array($param['type'], $name)));
1702
            }
1703
1704
            // validate variable type
1705
            if (!call_user_func($validator, $param['var'])) {
1706
                throw new InvalidArgumentException(__('Argument `$%1$s` is not of type `%2$s`, given `%3$s`.', array($name, $param['type'], gettype($param['var']))));
1707
            }
1708
        }
1709
    }
1710
1711
1712
    /**
1713
     * Wrap a value in CDATA tags for XSL output of non encoded data, only
1714
     * if not already wrapped.
1715
     *
1716
     * @since Symphony 2.3.2
1717
     *
1718
     * @param string $value
1719
     *  The string to wrap in CDATA
1720
     * @return string
1721
     *  The wrapped string
1722
     */
1723
    public static function wrapInCDATA($value)
1724
    {
1725
        if (empty($value)) {
1726
            return $value;
1727
        }
1728
1729
        $startRegExp = '/^' . preg_quote(CDATA_BEGIN) . '/';
0 ignored issues
show
Bug introduced by
The constant CDATA_BEGIN was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1730
        $endRegExp = '/' . preg_quote(CDATA_END) . '$/';
0 ignored issues
show
Bug introduced by
The constant CDATA_END was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1731
1732
        if (!preg_match($startRegExp, $value)) {
1733
            $value = CDATA_BEGIN . $value;
1734
        }
1735
1736
        if (!preg_match($endRegExp, $value)) {
1737
            $value .= CDATA_END;
1738
        }
1739
1740
        return $value;
1741
    }
1742
1743
    /**
1744
     * Unwrap a value from CDATA tags to return the raw string
1745
     *
1746
     * @since Symphony 2.3.4
1747
     * @param string $value
1748
     *  The string to unwrap from CDATA
1749
     * @return string
1750
     *  The unwrapped string
1751
     */
1752
    public static function unwrapCDATA($value)
1753
    {
1754
        return str_replace(array(CDATA_BEGIN, CDATA_END), '', $value);
0 ignored issues
show
Bug introduced by
The constant CDATA_END was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant CDATA_BEGIN was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1755
    }
1756
1757
    /**
1758
     * Converts a value to a positive integer. This method makes sure that the
1759
     * value is a valid positive integer representation before doing the cast.
1760
     *
1761
     * @since Symphony 2.5
1762
     * @param mixed $value
1763
     *  The value to cast to an integer
1764
     * @return int
1765
     *  The casted integer value if the input is valid, -1 otherwise.
1766
     */
1767
    public static function intval($value)
1768
    {
1769
        if (is_numeric($value) && preg_match('/^[0-9]+$/i', $value) === 1) {
1770
            return intval($value);
1771
        }
1772
1773
        return -1;
1774
    }
1775
}
1776