XoopsFileHandler   F
last analyzed

Complexity

Total Complexity 79

Size/Duplication

Total Lines 527
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 79
eloc 134
dl 0
loc 527
rs 2.08
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 3 1
A executable() 0 3 1
A writable() 0 3 1
A folder() 0 3 1
A pwd() 0 3 1
A readable() 0 3 1
A append() 0 3 1
A size() 0 7 2
A delete() 0 7 2
A prepare() 0 11 2
A perms() 0 7 2
A lastChange() 0 7 2
A close() 0 7 2
A __construct() 0 17 6
B read() 0 26 8
A create() 0 10 5
A lastAccess() 0 7 2
A open() 0 16 6
A name() 0 12 4
A exists() 0 5 2
A group() 0 7 2
A offset() 0 11 4
A write() 0 18 6
A owner() 0 7 2
A md5() 0 12 4
A safe() 0 10 3
A ext() 0 10 3
A info() 0 10 3

How to fix   Complexity   

Complex Class

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

1
<?php
2
/**
3
 * File engine For XOOPS
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       (c) 2005-2016 XOOPS Project (www.xoops.org)
13
 * @license             GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             class
15
 * @subpackage          file
16
 * @since               2.3.0
17
 * @author              Taiwen Jiang <[email protected]>
18
 */
19
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
20
21
/**
22
 * Convenience class for reading, writing and appending to files.
23
 *
24
 * PHP versions 4 and 5
25
 *
26
 * CakePHP(tm) :  Rapid Development Framework <https://www.cakephp.org/>
27
 * Copyright 2005-2008, Cake Software Foundation, Inc.
28
 *                                     1785 E. Sahara Avenue, Suite 490-204
29
 *                                     Las Vegas, Nevada 89104
30
 *
31
 * Licensed under The MIT License
32
 * Redistributions of files must retain the above copyright notice.
33
 *
34
 * @filesource
35
 * @copyright  Copyright 2005-2008, Cake Software Foundation, Inc.
36
 * @link       https://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
37
 * @package    cake
38
 * @subpackage cake.cake.libs
39
 * @since      CakePHP(tm) v 0.2.9
40
 * @modifiedby $LastChangedBy: beckmi $
41
 * @lastmodified $Date: 2015-06-06 17:59:41 -0400 (Sat, 06 Jun 2015) $
42
 * @license    https://www.opensource.org/licenses/mit-license.php The MIT License
43
 */
44
45
/**
46
 * Convenience class for reading, writing and appending to files.
47
 *
48
 * @package    cake
49
 * @subpackage cake.cake.libs
50
 */
51
class XoopsFileHandler
52
{
53
    /**
54
     * folder object of the File
55
     *
56
     * @var object
57
     * @access public
58
     */
59
    public $folder;
60
61
    /**
62
     * Filename
63
     *
64
     * @var string
65
     * @access public
66
     */
67
    public $name;
68
69
    /**
70
     * file info
71
     *
72
     * @var string
73
     * @access public
74
     */
75
    public $info = array();
76
77
    /**
78
     * Holds the file handler resource if the file is opened
79
     *
80
     * @var resource
81
     * @access public
82
     */
83
    public $handle;
84
85
    /**
86
     * enable locking for file reading and writing
87
     *
88
     * @var boolean
89
     * @access public
90
     */
91
    public $lock;
92
93
    /**
94
     * Constructor
95
     *
96
     * @param string  $path   Path to file
97
     * @param boolean $create Create file if it does not exist (if true)
98
     * @param integer $mode   Mode to apply to the folder holding the file
99
     * @access private
100
     */
101
    public function __construct($path, $create = false, $mode = 0755)
102
    {
103
        XoopsLoad::load('XoopsFile');
104
        $this->folder = XoopsFile::getHandler('folder', dirname($path), $create, $mode);
105
        if (!is_dir($path)) {
106
            $this->name = basename($path);
107
        }
108
        if (!$this->exists()) {
109
            if ($create === true) {
110
                if ($this->safe($path) && $this->create() === false) {
111
                    return false;
112
                }
113
            } else {
114
                return false;
115
            }
116
        }
117
        return null;
118
    }
119
120
    /**
121
     * Closes the current file if it is opened
122
     *
123
     * @access private
124
     */
125
    public function __destruct()
126
    {
127
        $this->close();
128
    }
129
130
    /**
131
     * Creates the File.
132
     *
133
     * @return boolean Success
134
     * @access public
135
     */
136
    public function create()
137
    {
138
        $dir = $this->folder->pwd();
139
        if (is_dir($dir) && is_writable($dir) && !$this->exists()) {
140
            if (touch($this->pwd())) {
141
                return true;
142
            }
143
        }
144
145
        return false;
146
    }
147
148
    /**
149
     * Opens the current file with a given $mode
150
     *
151
     * @param  string  $mode  A valid 'fopen' mode string (r|w|a ...)
152
     * @param  boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
153
     * @return boolean True on success, false on failure
154
     * @access public
155
     */
156
    public function open($mode = 'r', $force = false)
157
    {
158
        if (!$force && is_resource($this->handle)) {
159
            return true;
160
        }
161
        if ($this->exists() === false) {
162
            if ($this->create() === false) {
163
                return false;
164
            }
165
        }
166
        $this->handle = fopen($this->pwd(), $mode);
167
        if (is_resource($this->handle)) {
168
            return true;
169
        }
170
171
        return false;
172
    }
173
174
    /**
175
     * Return the contents of this File as a string.
176
     *
177
     * @param bool|string|int $bytes where to start
178
     * @param string      $mode
179
     * @param boolean     $force If true then the file will be re-opened even if its already opened, otherwise it won't
180
     *
181
     * @return mixed string on success, false on failure
182
     * @access public
183
     */
184
    public function read($bytes = false, $mode = 'rb', $force = false)
185
    {
186
        $success = false;
187
        if ($this->lock !== null) {
188
            if (flock($this->handle, LOCK_SH) === false) {
189
                return false;
190
            }
191
        }
192
        if ($bytes === false) {
193
            $success = file_get_contents($this->pwd());
194
        } elseif ($this->open($mode, $force) === true) {
195
            if (is_int($bytes)) {
196
                $success = fread($this->handle, $bytes);
197
            } else {
198
                $data = '';
199
                while (!feof($this->handle)) {
200
                    $data .= fgets($this->handle, 4096);
201
                }
202
                $success = trim($data);
203
            }
204
        }
205
        if ($this->lock !== null) {
206
            flock($this->handle, LOCK_UN);
207
        }
208
209
        return $success;
210
    }
211
212
    /**
213
     * Sets or gets the offset for the currently opened file.
214
     *
215
     * @param  mixed   $offset The $offset in bytes to seek. If set to false then the current offset is returned.
216
     * @param  integer $seek   PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to
217
     * @return mixed   True on success, false on failure (set mode), false on failure or integer offset on success (get mode)
218
     * @access public
219
     */
220
    public function offset($offset = false, $seek = SEEK_SET)
221
    {
222
        if ($offset === false) {
223
            if (is_resource($this->handle)) {
224
                return ftell($this->handle);
225
            }
226
        } elseif ($this->open() === true) {
227
            return fseek($this->handle, $offset, $seek) === 0;
228
        }
229
230
        return false;
231
    }
232
233
    /**
234
     * Prepares a ascii string for writing
235
     * fixes line endings
236
     *
237
     * @param  string $data Data to prepare for writing.
238
     * @return string
239
     * @access public
240
     */
241
    public function prepare($data)
242
    {
243
        $lineBreak = "\n";
244
        if (substr(PHP_OS, 0, 3) === 'WIN') {
245
            $lineBreak = "\r\n";
246
        }
247
248
        return strtr($data, array(
249
            "\r\n" => $lineBreak,
250
            "\n"   => $lineBreak,
251
            "\r"   => $lineBreak));
252
    }
253
254
    /**
255
     * Write given data to this File.
256
     *
257
     * @param  string      $data  Data to write to this File.
258
     * @param  string      $mode  Mode of writing. {@link https://php.net/fwrite See fwrite()}.
259
     * @param  bool|string $force force the file to open
260
     * @return boolean     Success
261
     * @access public
262
     */
263
    public function write($data, $mode = 'w', $force = false)
264
    {
265
        $success = false;
266
        if ($this->open($mode, $force) === true) {
0 ignored issues
show
Bug introduced by
It seems like $force can also be of type string; however, parameter $force of XoopsFileHandler::open() does only seem to accept boolean, 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

266
        if ($this->open($mode, /** @scrutinizer ignore-type */ $force) === true) {
Loading history...
267
            if ($this->lock !== null) {
268
                if (flock($this->handle, LOCK_EX) === false) {
269
                    return false;
270
                }
271
            }
272
            if (fwrite($this->handle, $data) !== false) {
273
                $success = true;
274
            }
275
            if ($this->lock !== null) {
276
                flock($this->handle, LOCK_UN);
277
            }
278
        }
279
280
        return $success;
281
    }
282
283
    /**
284
     * Append given data string to this File.
285
     *
286
     * @param  string      $data  Data to write
287
     * @param  bool|string $force force the file to open
288
     * @return boolean     Success
289
     * @access public
290
     */
291
    public function append($data, $force = false)
292
    {
293
        return $this->write($data, 'a', $force);
294
    }
295
296
    /**
297
     * Closes the current file if it is opened.
298
     *
299
     * @return boolean True if closing was successful or file was already closed, otherwise false
300
     * @access public
301
     */
302
    public function close()
303
    {
304
        if (!is_resource($this->handle)) {
305
            return true;
306
        }
307
308
        return fclose($this->handle);
309
    }
310
311
    /**
312
     * Deletes the File.
313
     *
314
     * @return boolean Success
315
     * @access public
316
     */
317
    public function delete()
318
    {
319
        if ($this->exists()) {
320
            return unlink($this->pwd());
321
        }
322
323
        return false;
324
    }
325
326
    /**
327
     * Returns the File extension.
328
     *
329
     * @return string The File extension
330
     * @access public
331
     */
332
    public function info()
333
    {
334
        if ($this->info == null) {
335
            $this->info = pathinfo($this->pwd());
0 ignored issues
show
Documentation Bug introduced by
It seems like pathinfo($this->pwd()) can also be of type array. However, the property $info is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
336
        }
337
        if (!isset($this->info['filename'])) {
338
            $this->info['filename'] = $this->name();
339
        }
340
341
        return $this->info;
342
    }
343
344
    /**
345
     * Returns the File extension.
346
     *
347
     * @return string|false The File extension
348
     * @access public
349
     */
350
    public function ext()
351
    {
352
        if ($this->info == null) {
353
            $this->info();
354
        }
355
        if (isset($this->info['extension'])) {
356
            return $this->info['extension'];
357
        }
358
359
        return false;
360
    }
361
362
    /**
363
     * Returns the File name without extension.
364
     *
365
     * @return string|false The File name without extension.
366
     * @access public
367
     */
368
    public function name()
369
    {
370
        if ($this->info == null) {
371
            $this->info();
372
        }
373
        if (isset($this->info['extension'])) {
374
            return basename($this->name, '.' . $this->info['extension']);
375
        } elseif ($this->name) {
376
            return $this->name;
377
        }
378
379
        return false;
380
    }
381
382
    /**
383
     * makes filename safe for saving
384
     *
385
     * @param  string $name the name of the file to make safe if different from $this->name
386
     * @param  null|string   $ext
387
     * @return string $ext the extension of the file
388
     * @access public
389
     */
390
    public function safe($name = null, $ext = null)
391
    {
392
        if (!$name) {
393
            $name = $this->name;
394
        }
395
        if (!$ext) {
396
            $ext = $this->ext();
397
        }
398
399
        return preg_replace('/[^\w\.-]+/', '_', basename($name, $ext));
0 ignored issues
show
Bug introduced by
It seems like $ext can also be of type false; however, parameter $suffix of basename() 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

399
        return preg_replace('/[^\w\.-]+/', '_', basename($name, /** @scrutinizer ignore-type */ $ext));
Loading history...
400
    }
401
402
    /**
403
     * Get md5 Checksum of file with previous check of Filesize
404
     *
405
     * @param  mixed $maxsize in MB or true to force
406
     * @return string|false md5 Checksum {@link https://php.net/md5_file See md5_file()}
407
     * @access public
408
     */
409
    public function md5($maxsize = 5)
410
    {
411
        if ($maxsize === true) {
412
            return md5_file($this->pwd());
413
        } else {
414
            $size = $this->size();
415
            if ($size && $size < ($maxsize * 1024) * 1024) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $size of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
416
                return md5_file($this->pwd());
417
            }
418
        }
419
420
        return false;
421
    }
422
423
    /**
424
     * Returns the full path of the File.
425
     *
426
     * @return string Full path to file
427
     * @access public
428
     */
429
    public function pwd()
430
    {
431
        return $this->folder->slashTerm($this->folder->pwd()) . $this->name;
432
    }
433
434
    /**
435
     * Returns true if the File exists.
436
     *
437
     * @return boolean true if it exists, false otherwise
438
     * @access public
439
     */
440
    public function exists()
441
    {
442
        $exists = (file_exists($this->pwd()) && is_file($this->pwd()));
443
444
        return $exists;
445
    }
446
447
    /**
448
     * Returns the "chmod" (permissions) of the File.
449
     *
450
     * @return string|false Permissions for the file
451
     * @access public
452
     */
453
    public function perms()
454
    {
455
        if ($this->exists()) {
456
            return substr(sprintf('%o', fileperms($this->pwd())), -4);
457
        }
458
459
        return false;
460
    }
461
462
    /**
463
     * Returns the Filesize, either in bytes or in human-readable format.
464
     *
465
     * @return string|false |int filesize as int or as a human-readable string
466
     * @access   public
467
     */
468
    public function size()
469
    {
470
        if ($this->exists()) {
471
            return filesize($this->pwd());
472
        }
473
474
        return false;
475
    }
476
477
    /**
478
     * Returns true if the File is writable.
479
     *
480
     * @return boolean true if its writable, false otherwise
481
     * @access public
482
     */
483
    public function writable()
484
    {
485
        return is_writable($this->pwd());
486
    }
487
488
    /**
489
     * Returns true if the File is executable.
490
     *
491
     * @return boolean true if its executable, false otherwise
492
     * @access public
493
     */
494
    public function executable()
495
    {
496
        return is_executable($this->pwd());
497
    }
498
499
    /**
500
     * Returns true if the File is readable.
501
     *
502
     * @return boolean true if file is readable, false otherwise
503
     * @access public
504
     */
505
    public function readable()
506
    {
507
        return is_readable($this->pwd());
508
    }
509
510
    /**
511
     * Returns the File's owner.
512
     *
513
     * @return integer|false the Fileowner
514
     */
515
    public function owner()
516
    {
517
        if ($this->exists()) {
518
            return fileowner($this->pwd());
519
        }
520
521
        return false;
522
    }
523
524
    /**
525
     * Returns the File group.
526
     *
527
     * @return integer|false the Filegroup
528
     * @access public
529
     */
530
    public function group()
531
    {
532
        if ($this->exists()) {
533
            return filegroup($this->pwd());
534
        }
535
536
        return false;
537
    }
538
539
    /**
540
     * Returns last access time.
541
     *
542
     * @return integer|false timestamp Timestamp of last access time
543
     * @access public
544
     */
545
    public function lastAccess()
546
    {
547
        if ($this->exists()) {
548
            return fileatime($this->pwd());
549
        }
550
551
        return false;
552
    }
553
554
    /**
555
     * Returns last modified time.
556
     *
557
     * @return integer|false timestamp Timestamp of last modification
558
     * @access public
559
     */
560
    public function lastChange()
561
    {
562
        if ($this->exists()) {
563
            return filemtime($this->pwd());
564
        }
565
566
        return false;
567
    }
568
569
    /**
570
     * Returns the current folder.
571
     *
572
     * @return Folder Current folder
0 ignored issues
show
Bug introduced by
The type Folder 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...
573
     * @access public
574
     */
575
    public function &folder()
576
    {
577
        return $this->folder;
578
    }
579
}
580