references_Cache_Lite::_cleanDir()   F
last analyzed

Complexity

Conditions 30
Paths 444

Size

Total Lines 64

Duplication

Lines 10
Ratio 15.63 %

Importance

Changes 0
Metric Value
cc 30
nc 444
nop 3
dl 10
loc 64
rs 0.7722
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Fast, light and safe Cache Class
5
 *
6
 * Cache_Lite is a fast, light and safe cache system. It's optimized
7
 * for file containers. It is fast and safe (because it uses file
8
 * locking and/or anti-corruption tests).
9
 *
10
 * There are some examples in the 'docs/examples' file
11
 * Technical choices are described in the 'docs/technical' file
12
 *
13
 * Memory Caching is from an original idea of
14
 * Mike BENOIT <[email protected]>
15
 *
16
 * Nota : A chinese documentation (thanks to RainX <[email protected]>) is
17
 * available at :
18
 * http://rainx.phpmore.com/manual/cache_lite.html
19
 *
20
 * @package  Cache_Lite
21
 * @category Caching
22
 * @author   Fabien MARTY <[email protected]>
23
 */
24
25
define('REFERENCES_CACHE_LITE_ERROR_RETURN', 1);
26
define('REFERENCES_CACHE_LITE_ERROR_DIE', 8);
27
28
class references_Cache_Lite
29
{
30
31
    // --- Private properties ---
32
33
    /**
34
     * Directory where to put the cache files
35
     * (make sure to add a trailing slash)
36
     *
37
     * @var string $_cacheDir
38
     */
39
    public $_cacheDir = '/tmp/';
40
41
    /**
42
     * Enable / disable caching
43
     *
44
     * (can be very usefull for the debug of cached scripts)
45
     *
46
     * @var boolean $_caching
47
     */
48
    public $_caching = true;
49
50
    /**
51
     * Cache lifetime (in seconds)
52
     *
53
     * If null, the cache is valid forever.
54
     *
55
     * @var int $_lifeTime
56
     */
57
    public $_lifeTime = 3600;
58
59
    /**
60
     * Enable / disable fileLocking
61
     *
62
     * (can avoid cache corruption under bad circumstances)
63
     *
64
     * @var boolean $_fileLocking
65
     */
66
    public $_fileLocking = true;
67
68
    /**
69
     * Timestamp of the last valid cache
70
     *
71
     * @var int $_refreshTime
72
     */
73
    public $_refreshTime;
74
75
    /**
76
     * File name (with path)
77
     *
78
     * @var string $_file
79
     */
80
    public $_file;
81
82
    /**
83
     * File name (without path)
84
     *
85
     * @var string $_fileName
86
     */
87
    public $_fileName;
88
89
    /**
90
     * Enable / disable write control (the cache is read just after writing to detect corrupt entries)
91
     *
92
     * Enable write control will lightly slow the cache writing but not the cache reading
93
     * Write control can detect some corrupt cache files but maybe it's not a perfect control
94
     *
95
     * @var boolean $_writeControl
96
     */
97
    public $_writeControl = true;
98
99
    /**
100
     * Enable / disable read control
101
     *
102
     * If enabled, a control key is embeded in cache file and this key is compared with the one
103
     * calculated after the reading.
104
     *
105
     * @var boolean $_writeControl
106
     */
107
    public $_readControl = true;
108
109
    /**
110
     * Type of read control (only if read control is enabled)
111
     *
112
     * Available values are :
113
     * 'md5' for a md5 hash control (best but slowest)
114
     * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice)
115
     * 'strlen' for a length only test (fastest)
116
     *
117
     * @var boolean $_readControlType
118
     */
119
    public $_readControlType = 'crc32';
120
121
    /**
122
     * Pear error mode (when raiseError is called)
123
     *
124
     * (see PEAR doc)
125
     *
126
     * @see setToDebug()
127
     * @var int $_pearErrorMode
128
     */
129
    public $_pearErrorMode = REFERENCES_CACHE_LITE_ERROR_RETURN;
130
131
    /**
132
     * Current cache id
133
     *
134
     * @var string $_id
135
     */
136
    public $_id;
137
138
    /**
139
     * Current cache group
140
     *
141
     * @var string $_group
142
     */
143
    public $_group;
144
145
    /**
146
     * Enable / Disable "Memory Caching"
147
     *
148
     * NB : There is no lifetime for memory caching !
149
     *
150
     * @var boolean $_memoryCaching
151
     */
152
    public $_memoryCaching = false;
153
154
    /**
155
     * Enable / Disable "Only Memory Caching"
156
     * (be carefull, memory caching is "beta quality")
157
     *
158
     * @var boolean $_onlyMemoryCaching
159
     */
160
    public $_onlyMemoryCaching = false;
161
162
    /**
163
     * Memory caching array
164
     *
165
     * @var array $_memoryCachingArray
166
     */
167
    public $_memoryCachingArray = array();
168
169
    /**
170
     * Memory caching counter
171
     *
172
     * @var int $memoryCachingCounter
173
     */
174
    public $_memoryCachingCounter = 0;
175
176
    /**
177
     * Memory caching limit
178
     *
179
     * @var int $memoryCachingLimit
180
     */
181
    public $_memoryCachingLimit = 1000;
182
183
    /**
184
     * File Name protection
185
     *
186
     * if set to true, you can use any cache id or group name
187
     * if set to false, it can be faster but cache ids and group names
188
     * will be used directly in cache file names so be carefull with
189
     * special characters...
190
     *
191
     * @var boolean $fileNameProtection
192
     */
193
    public $_fileNameProtection = true;
194
195
    /**
196
     * Enable / disable automatic serialization
197
     *
198
     * it can be used to save directly datas which aren't strings
199
     * (but it's slower)
200
     *
201
     * @var boolean $_serialize
202
     */
203
    public $_automaticSerialization = false;
204
205
    /**
206
     * Disable / Tune the automatic cleaning process
207
     *
208
     * The automatic cleaning process destroy too old (for the given life time)
209
     * cache files when a new cache file is written.
210
     * 0               => no automatic cache cleaning
211
     * 1               => systematic cache cleaning
212
     * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
213
     *
214
     * @var int $_automaticCleaning
215
     */
216
    public $_automaticCleaningFactor = 0;
217
218
    /**
219
     * Nested directory level
220
     *
221
     * Set the hashed directory structure level. 0 means "no hashed directory
222
     * structure", 1 means "one level of directory", 2 means "two levels"...
223
     * This option can speed up Cache_Lite only when you have many thousands of
224
     * cache file. Only specific benchs can help you to choose the perfect value
225
     * for you. Maybe, 1 or 2 is a good start.
226
     *
227
     * @var int $_hashedDirectoryLevel
228
     */
229
    public $_hashedDirectoryLevel = 0;
230
231
    /**
232
     * Umask for hashed directory structure
233
     *
234
     * @var int $_hashedDirectoryUmask
235
     */
236
    public $_hashedDirectoryUmask = 0700;
237
238
    /**
239
     * API break for error handling in REFERENCES_CACHE_LITE_ERROR_RETURN mode
240
     *
241
     * In REFERENCES_CACHE_LITE_ERROR_RETURN mode, error handling was not good because
242
     * for example save() method always returned a boolean (a PEAR_Error object
243
     * would be better in REFERENCES_CACHE_LITE_ERROR_RETURN mode). To correct this without
244
     * breaking the API, this option (false by default) can change this handling.
245
     *
246
     * @var boolean
247
     */
248
    public $_errorHandlingAPIBreak = false;
249
250
    // --- Public methods ---
251
252
    /**
253
     * Constructor
254
     *
255
     * $options is an assoc. Available options are :
256
     * $options = array(
257
     *     'cacheDir' => directory where to put the cache files (string) ,
258
     *     'caching' => enable / disable caching (boolean) ,
259
     *     'lifeTime' => cache lifetime in seconds (int) ,
260
     *     'fileLocking' => enable / disable fileLocking (boolean) ,
261
     *     'writeControl' => enable / disable write control (boolean) ,
262
     *     'readControl' => enable / disable read control (boolean) ,
263
     *     'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string) ,
264
     *     'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int) ,
265
     *     'memoryCaching' => enable / disable memory caching (boolean) ,
266
     *     'onlyMemoryCaching' => enable / disable only memory caching (boolean) ,
267
     *     'memoryCachingLimit' => max nbr of records to store into memory caching (int) ,
268
     *     'fileNameProtection' => enable / disable automatic file name protection (boolean) ,
269
     *     'automaticSerialization' => enable / disable automatic serialization (boolean) ,
270
     *     'automaticCleaningFactor' => distable / tune automatic cleaning process (int) ,
271
     *     'hashedDirectoryLevel' => level of the hashed directory system (int) ,
272
     *     'hashedDirectoryUmask' => umask for hashed directory structure (int) ,
273
     *     'errorHandlingAPIBreak' => API break for better error handling ? (boolean)
274
     * );
275
     *
276
     * @param array $options options
277
     * @access public
278
     */
279
    public function __construct($options = array(null))
280
    {
281
        foreach ($options as $key => $value) {
282
            $this->setOption($key, $value);
283
        }
284
    }
285
286
    /**
287
     * Generic way to set a Cache_Lite option
288
     *
289
     * see Cache_Lite constructor for available options
290
     *
291
     * @var string $name  name of the option
292
     * @var mixed  $value value of the option
293
     * @access public
294
     */
295
    public function setOption($name, $value)
296
    {
297
        $availableOptions = array(
298
            'errorHandlingAPIBreak',
299
            'hashedDirectoryUmask',
300
            'hashedDirectoryLevel',
301
            'automaticCleaningFactor',
302
            'automaticSerialization',
303
            'fileNameProtection',
304
            'memoryCaching',
305
            'onlyMemoryCaching',
306
            'memoryCachingLimit',
307
            'cacheDir',
308
            'caching',
309
            'lifeTime',
310
            'fileLocking',
311
            'writeControl',
312
            'readControl',
313
            'readControlType',
314
            'pearErrorMode'
315
        );
316
        if (in_array($name, $availableOptions)) {
317
            $property        = '_' . $name;
318
            $this->$property = $value;
319
        }
320
    }
321
322
    /**
323
     * Test if a cache is available and (if yes) return it
324
     *
325
     * @param string  $id                     cache id
326
     * @param string  $group                  name of the cache group
327
     * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
328
     * @return string data of the cache (else : false)
329
     * @access public
330
     */
331
    public function get($id, $group = 'default', $doNotTestCacheValidity = false)
332
    {
333
        $this->_id    = $id;
334
        $this->_group = $group;
335
        $data         = false;
336
        if ($this->_caching) {
337
            $this->_setRefreshTime();
338
            $this->_setFileName($id, $group);
339
            clearstatcache();
340
            if ($this->_memoryCaching) {
341
                if (isset($this->_memoryCachingArray[$this->_file])) {
342
                    if ($this->_automaticSerialization) {
343
                        return unserialize($this->_memoryCachingArray[$this->_file]);
344
                    }
345
346
                    return $this->_memoryCachingArray[$this->_file];
347
                }
348
                if ($this->_onlyMemoryCaching) {
349
                    return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by references_Cache_Lite::get of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
350
                }
351
            }
352
            if ($doNotTestCacheValidity || is_null($this->_refreshTime)) {
353
                if (file_exists($this->_file)) {
354
                    $data = $this->_read();
355
                }
356
            } else {
357
                if (file_exists($this->_file) && (@filemtime($this->_file) > $this->_refreshTime)) {
358
                    $data = $this->_read();
359
                }
360
            }
361
            if ($data && $this->_memoryCaching) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type string|false is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
362
                $this->_memoryCacheAdd($data);
363
            }
364
            if ($this->_automaticSerialization && is_string($data)) {
365
                $data = unserialize($data);
366
            }
367
368
            return $data;
369
        }
370
371
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by references_Cache_Lite::get of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
372
    }
373
374
    /**
375
     * Save some data in a cache file
376
     *
377
     * @param string $data  data to put in cache (can be another type than strings if automaticSerialization is on)
378
     * @param string $id    cache id
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be string|null?

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

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

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

Loading history...
379
     * @param string $group name of the cache group
380
     * @return boolean true if no problem (else : false or a PEAR_Error object)
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
381
     * @access public
382
     */
383
    public function save($data, $id = null, $group = 'default')
384
    {
385
        if ($this->_caching) {
386
            if ($this->_automaticSerialization) {
387
                $data = serialize($data);
388
            }
389
            if (isset($id)) {
390
                $this->_setFileName($id, $group);
391
            }
392
            if ($this->_memoryCaching) {
393
                $this->_memoryCacheAdd($data);
394
                if ($this->_onlyMemoryCaching) {
395
                    return true;
396
                }
397
            }
398
            if ($this->_automaticCleaningFactor > 0 && ($this->_automaticCleaningFactor == 1 || mt_rand(1, $this->_automaticCleaningFactor) == 1)) {
399
                $this->clean(false, 'old');
400
            }
401
            if ($this->_writeControl) {
402
                $res = $this->_writeAndControl($data);
403
                if (is_bool($res)) {
404
                    if ($res) {
405
                        return true;
406
                    }
407
                    // if $res if false, we need to invalidate the cache
408
                    @touch($this->_file, time() - 2 * abs($this->_lifeTime));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
409
410
                    return false;
411
                }
412
            } else {
413
                $res = $this->_write($data);
414
            }
415
            if (is_object($res)) {
416
                // $res is a PEAR_Error object
417
                if (!$this->_errorHandlingAPIBreak) {
418
                    return false; // we return false (old API)
419
                }
420
            }
421
422
            return $res;
423
        }
424
425
        return false;
426
    }
427
428
    /**
429
     * Remove a cache file
430
     *
431
     * @param string  $id                cache id
432
     * @param string  $group             name of the cache group
433
     * @param boolean $checkbeforeunlink check if file exists before removing it
434
     * @return boolean true if no problem
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
435
     * @access public
436
     */
437
    public function remove($id, $group = 'default', $checkbeforeunlink = false)
438
    {
439
        $this->_setFileName($id, $group);
440
        if ($this->_memoryCaching) {
441
            if (isset($this->_memoryCachingArray[$this->_file])) {
442
                unset($this->_memoryCachingArray[$this->_file]);
443
                --$this->_memoryCachingCounter;
444
            }
445
            if ($this->_onlyMemoryCaching) {
446
                return true;
447
            }
448
        }
449
        if ($checkbeforeunlink) {
450
            if (!file_exists($this->_file)) {
451
                return true;
452
            }
453
        }
454
455
        return $this->_unlink($this->_file);
456
    }
457
458
    /**
459
     * Clean the cache
460
     *
461
     * if no group is specified all cache files will be destroyed
462
     * else only cache files of the specified group will be destroyed
463
     *
464
     * @param bool|string $group              name of the cache group
465
     * @param string      $mode               flush cache mode : 'old', 'ingroup', 'notingroup',
466
     *                                        'callback_myFunction'
467
     * @return bool true if no problem
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
468
     * @access public
469
     */
470
    public function clean($group = false, $mode = 'ingroup')
471
    {
472
        return $this->_cleanDir($this->_cacheDir, $group, $mode);
473
    }
474
475
    /**
476
     * Set to debug mode
477
     *
478
     * When an error is found, the script will stop and the message will be displayed
479
     * (in debug mode only).
480
     *
481
     * @access public
482
     */
483
    public function setToDebug()
484
    {
485
        $this->setOption('pearErrorMode', REFERENCES_CACHE_LITE_ERROR_DIE);
486
    }
487
488
    /**
489
     * Set a new life time
490
     *
491
     * @param int $newLifeTime new life time (in seconds)
492
     * @access public
493
     */
494
    public function setLifeTime($newLifeTime)
495
    {
496
        $this->_lifeTime = $newLifeTime;
497
        $this->_setRefreshTime();
498
    }
499
500
    /**
501
     * Save the state of the caching memory array into a cache file cache
502
     *
503
     * @param string $id    cache id
504
     * @param string $group name of the cache group
505
     * @access public
506
     */
507
    public function saveMemoryCachingState($id, $group = 'default')
508
    {
509
        if ($this->_caching) {
510
            $array = array(
511
                'counter' => $this->_memoryCachingCounter,
512
                'array'   => $this->_memoryCachingArray
513
            );
514
            $data  = serialize($array);
515
            $this->save($data, $id, $group);
516
        }
517
    }
518
519
    /**
520
     * Load the state of the caching memory array from a given cache file cache
521
     *
522
     * @param string  $id                     cache id
523
     * @param string  $group                  name of the cache group
524
     * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
525
     * @access public
526
     */
527
    public function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false)
528
    {
529
        if ($this->_caching) {
530
            if ($data = $this->get($id, $group, $doNotTestCacheValidity)) {
531
                $array                       = unserialize($data);
532
                $this->_memoryCachingCounter = $array['counter'];
533
                $this->_memoryCachingArray   = $array['array'];
534
            }
535
        }
536
    }
537
538
    /**
539
     * Return the cache last modification time
540
     *
541
     * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY !
542
     *
543
     * @return int last modification time
544
     */
545
    public function lastModified()
546
    {
547
        return @filemtime($this->_file);
548
    }
549
550
    /**
551
     * Trigger a PEAR error
552
     *
553
     * To improve performances, the PEAR.php file is included dynamically.
554
     * The file is so included only when an error is triggered. So, in most
555
     * cases, the file isn't included and perfs are much better.
556
     *
557
     * @param string $msg  error message
558
     * @param int    $code error code
559
     * @access public
560
     * @return object
561
     */
562
    public function raiseError($msg, $code)
563
    {
564
        include_once('PEAR.php');
565
566
        return references_PEAR::raiseError($msg, $code, $this->_pearErrorMode);
567
    }
568
569
    /**
570
     * Extend the life of a valid cache file
571
     *
572
     * see http://pear.php.net/bugs/bug.php?id=6681
573
     *
574
     * @access public
575
     */
576
    public function extendLife()
577
    {
578
        @touch($this->_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
579
    }
580
581
    // --- Private methods ---
582
583
    /**
584
     * Compute & set the refresh time
585
     *
586
     * @access private
587
     */
588
    public function _setRefreshTime()
589
    {
590
        if (is_null($this->_lifeTime)) {
591
            $this->_refreshTime = null;
592
        } else {
593
            $this->_refreshTime = time() - $this->_lifeTime;
594
        }
595
    }
596
597
    /**
598
     * Remove a file
599
     *
600
     * @param string $file complete file path and name
601
     * @return boolean true if no problem
0 ignored issues
show
Documentation introduced by
Should the return type not be object|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
602
     * @access private
603
     */
604
    public function _unlink($file)
605
    {
606
        if (!@unlink($file)) {
607
            return $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
608
        }
609
610
        return true;
611
    }
612
613
    /**
614
     * Recursive function for cleaning cache file in the given directory
615
     *
616
     * @param string      $dir   directory complete path (with a trailing slash)
617
     * @param bool|string $group name of the cache group
618
     * @param string      $mode  flush cache mode : 'old', 'ingroup', 'notingroup',
619
     *                           'callback_myFunction'
620
     * @return bool true if no problem
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
621
     * @access private
622
     */
623
    public function _cleanDir($dir, $group = false, $mode = 'ingroup')
624
    {
625
        if ($this->_fileNameProtection) {
626
            $motif = $group ? 'cache_' . md5($group) . '_' : 'cache_';
627
        } else {
628
            $motif = $group ? 'cache_' . $group . '_' : 'cache_';
629
        }
630
        if ($this->_memoryCaching) {
631
            foreach ($this->_memoryCachingArray as $key => $v) {
632
                if (strpos($key, $motif) !== false) {
633
                    unset($this->_memoryCachingArray[$key]);
634
                    --$this->_memoryCachingCounter;
635
                }
636
            }
637
            if ($this->_onlyMemoryCaching) {
638
                return true;
639
            }
640
        }
641
        if (!($dh = opendir($dir))) {
642
            return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
643
        }
644
        $result = true;
645
        while ($file = readdir($dh)) {
646
            if (($file !== '.') && ($file !== '..')) {
647
                if (substr($file, 0, 6) === 'cache_') {
648
                    $file2 = $dir . $file;
649
                    if (is_file($file2)) {
650
                        switch (substr($mode, 0, 9)) {
651
                            case 'old':
652
                                // files older than lifeTime get deleted from cache
653
                                if (!is_null($this->_lifeTime)) {
654
                                    if ((time() - @filemtime($file2)) > $this->_lifeTime) {
655
                                        $result = ($result and $this->_unlink($file2));
656
                                    }
657
                                }
658
                                break;
659 View Code Duplication
                            case 'notingrou':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
660
                                if (strpos($file2, $motif) === false) {
661
                                    $result = ($result and $this->_unlink($file2));
662
                                }
663
                                break;
664
                            case 'callback_':
665
                                $func = substr($mode, 9, strlen($mode) - 9);
666
                                if ($func($file2, $group)) {
667
                                    $result = ($result and $this->_unlink($file2));
668
                                }
669
                                break;
670
                            case 'ingroup':
671 View Code Duplication
                            default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
672
                                if (strpos($file2, $motif) !== false) {
673
                                    $result = ($result and $this->_unlink($file2));
674
                                }
675
                                break;
676
                        }
677
                    }
678
                    if (is_dir($file2) and ($this->_hashedDirectoryLevel > 0)) {
679
                        $result = ($result and $this->_cleanDir($file2 . '/', $group, $mode));
680
                    }
681
                }
682
            }
683
        }
684
685
        return $result;
686
    }
687
688
    /**
689
     * Add some date in the memory caching array
690
     *
691
     * @param string $data data to cache
692
     * @access private
693
     */
694
    public function _memoryCacheAdd($data)
695
    {
696
        $this->_memoryCachingArray[$this->_file] = $data;
697
        if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) {
698
            list($key,) = each($this->_memoryCachingArray);
699
            unset($this->_memoryCachingArray[$key]);
700
        } else {
701
            ++$this->_memoryCachingCounter;
702
        }
703
    }
704
705
    /**
706
     * Make a file name (with path)
707
     *
708
     * @param string $id    cache id
709
     * @param string $group name of the group
710
     * @access private
711
     */
712
    public function _setFileName($id, $group)
713
    {
714
        if ($this->_fileNameProtection) {
715
            $suffix = 'cache_' . md5($group) . '_' . md5($id);
716
        } else {
717
            $suffix = 'cache_' . $group . '_' . $id;
718
        }
719
        $root = $this->_cacheDir;
720
        if ($this->_hashedDirectoryLevel > 0) {
721
            $hash = md5($suffix);
722
            for ($i = 0; $i < $this->_hashedDirectoryLevel; ++$i) {
723
                $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/';
724
            }
725
        }
726
        $this->_fileName = $suffix;
727
        $this->_file     = $root . $suffix;
728
    }
729
730
    /**
731
     * Read the cache file and return the content
732
     *
733
     * @return string content of the cache file (else : false or a PEAR_Error object)
734
     * @access private
735
     */
736
    public function _read()
737
    {
738
        $fp = @fopen($this->_file, 'rb');
739
        if ($this->_fileLocking) {
740
            @flock($fp, LOCK_SH);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
741
        }
742
        if ($fp) {
743
            clearstatcache();
744
            $length = @filesize($this->_file);
745
            //            $mqr = get_magic_quotes_runtime();
746
            //            @set_magic_quotes_runtime(0);
747
            if ($this->_readControl) {
748
                $hashControl = @fread($fp, 32);
749
                $length      = $length - 32;
750
            }
751
            if ($length) {
752
                $data = @fread($fp, $length);
753
            } else {
754
                $data = '';
755
            }
756
            //            @set_magic_quotes_runtime($mqr);
757
            if ($this->_fileLocking) {
758
                @flock($fp, LOCK_UN);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
759
            }
760
            @fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
761
            if ($this->_readControl) {
762
                $hashData = $this->_hash($data, $this->_readControlType);
0 ignored issues
show
Documentation introduced by
$this->_readControlType is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
763
                if ($hashData != $hashControl) {
0 ignored issues
show
Bug introduced by
The variable $hashControl does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
764
                    if (!is_null($this->_lifeTime)) {
765
                        @touch($this->_file, time() - 2 * abs($this->_lifeTime));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
766
                    } else {
767
                        @unlink($this->_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
768
                    }
769
770
                    return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by references_Cache_Lite::_read of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
771
                }
772
            }
773
774
            return $data;
775
        }
776
777
        return $this->raiseError('Cache_Lite : Unable to read cache !', -2);
778
    }
779
780
    /**
781
     * Write the given data in the cache file
782
     *
783
     * @param string $data data to put in cache
784
     * @return boolean true if ok (a PEAR_Error object else)
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
785
     * @access private
786
     */
787
    public function _write($data)
788
    {
789
        if ($this->_hashedDirectoryLevel > 0) {
790
            $hash = md5($this->_fileName);
791
            $root = $this->_cacheDir;
792
            for ($i = 0; $i < $this->_hashedDirectoryLevel; ++$i) {
793
                $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/';
794
                if (!(@is_dir($root))) {
795
                    @mkdir($root, $this->_hashedDirectoryUmask);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
796
                }
797
            }
798
        }
799
        $fp = @fopen($this->_file, 'wb');
800
        if ($fp) {
801
            if ($this->_fileLocking) {
802
                @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
803
            }
804
            if ($this->_readControl) {
805
                @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
0 ignored issues
show
Documentation introduced by
$this->_readControlType is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
806
            }
807
            //            $mqr = get_magic_quotes_runtime();
808
            //            @set_magic_quotes_runtime(0);
809
            @fwrite($fp, $data);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
810
            //            @set_magic_quotes_runtime($mqr);
811
            if ($this->_fileLocking) {
812
                @flock($fp, LOCK_UN);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
813
            }
814
            @fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
815
816
            return true;
817
        }
818
819
        return $this->raiseError('Cache_Lite : Unable to write cache file : ' . $this->_file, -1);
820
    }
821
822
    /**
823
     * Write the given data in the cache file and control it just after to avoir corrupted cache entries
824
     *
825
     * @param string $data data to put in cache
826
     * @return boolean true if the test is ok (else : false or a PEAR_Error object)
0 ignored issues
show
Documentation introduced by
Should the return type not be object|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
827
     * @access private
828
     */
829
    public function _writeAndControl($data)
830
    {
831
        $result = $this->_write($data);
832
        if (is_object($result)) {
833
            return $result; # We return the PEAR_Error object
834
        }
835
        $dataRead = $this->_read();
836
        if (is_object($dataRead)) {
837
            return $dataRead; # We return the PEAR_Error object
838
        }
839
        if (is_bool($dataRead) && (!$dataRead)) {
840
            return false;
841
        }
842
843
        return ($dataRead == $data);
844
    }
845
846
    /**
847
     * Make a control key with the string containing datas
848
     *
849
     * @param string $data        data
850
     * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
851
     * @return string control key
0 ignored issues
show
Documentation introduced by
Should the return type not be string|object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
852
     * @access private
853
     */
854
    public function _hash($data, $controlType)
855
    {
856
        switch ($controlType) {
857
            case 'md5':
858
                return md5($data);
859
            case 'crc32':
860
                return sprintf('% 32d', crc32($data));
861
            case 'strlen':
862
                return sprintf('% 32d', strlen($data));
863
            default:
864
                return $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5);
865
        }
866
    }
867
}
868