Completed
Push — master ( e4daf7...e7a4df )
by Daniel
09:00
created

DefaultCacheFactory::createCache()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 2
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core\Cache;
4
5
use Psr\Log\LoggerAwareInterface;
6
use Psr\Log\LoggerInterface;
7
use Psr\SimpleCache\CacheInterface;
8
use SilverStripe\Core\Injector\Injector;
9
use Symfony\Component\Cache\Simple\FilesystemCache;
10
use Symfony\Component\Cache\Simple\ApcuCache;
11
use Symfony\Component\Cache\Simple\ChainCache;
12
use Symfony\Component\Cache\Simple\PhpFilesCache;
13
use Symfony\Component\Cache\Adapter\ApcuAdapter;
14
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
15
16
/**
17
 * Returns the most performant combination of caches available on the system:
18
 * - `PhpFilesCache` (PHP 7 with opcache enabled)
19
 * - `ApcuCache` (requires APC) with a `FilesystemCache` fallback (for larger cache volumes)
20
 * - `FilesystemCache` if none of the above is available
21
 *
22
 * Modelled after `Symfony\Component\Cache\Adapter\AbstractAdapter::createSystemCache()`
23
 */
24
class DefaultCacheFactory implements CacheFactory
25
{
26
    /**
27
     * @var string Absolute directory path
28
     */
29
    protected $args = [];
30
31
    /**
32
     * @var LoggerInterface
33
     */
34
    protected $logger;
35
36
    /**
37
     * @param array $args List of global options to merge with args during create()
38
     * @param LoggerInterface $logger Logger instance to assign
39
     */
40
    public function __construct($args = [], LoggerInterface $logger = null)
41
    {
42
        $this->args = $args;
0 ignored issues
show
Documentation Bug introduced by
It seems like $args of type array is incompatible with the declared type string of property $args.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
43
        $this->logger = $logger;
44
    }
45
46
    /**
47
     * @inheritdoc
48
     */
49
    public function create($service, array $args = array())
50
    {
51
        // merge args with default
52
        $args = array_merge($this->args, $args);
53
        $namespace = isset($args['namespace']) ? $args['namespace'] : '';
54
        $defaultLifetime = isset($args['defaultLifetime']) ? $args['defaultLifetime'] : 0;
55
        $directory = isset($args['directory']) ? $args['directory'] : null;
56
        $version = isset($args['version']) ? $args['version'] : null;
57
58
        // Check support
59
        $apcuSupported = $this->isAPCUSupported();
60
        $phpFilesSupported = $this->isPHPFilesSupported();
61
62
        // If apcu isn't supported, phpfiles is the next best preference
63
        if (!$apcuSupported && $phpFilesSupported) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $apcuSupported of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
64
            return $this->createCache(PhpFilesCache::class, [$namespace, $defaultLifetime, $directory]);
65
        }
66
67
        // Create filessytem cache
68
        $fs = $this->createCache(FilesystemCache::class, [$namespace, $defaultLifetime, $directory]);
69
        if (!$apcuSupported) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $apcuSupported of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
70
            return $fs;
71
        }
72
73
        // Chain this cache with ApcuCache
74
        $apcu = $this->createCache(ApcuCache::class, [$namespace, (int) $defaultLifetime / 5, $version]);
75
        return $this->createCache(ChainCache::class, [[$apcu, $fs]]);
76
    }
77
78
    /**
79
     * Determine if apcu is supported
80
     *
81
     * @return bool
82
     */
83
    protected function isAPCUSupported()
84
    {
85
        static $apcuSupported = null;
86
        if (null === $apcuSupported) {
87
            $apcuSupported = ApcuAdapter::isSupported();
88
        }
89
        return $apcuSupported;
90
    }
91
92
    /**
93
     * Determine if PHP files is supported
94
     *
95
     * @return bool
96
     */
97
    protected function isPHPFilesSupported()
98
    {
99
        static $phpFilesSupported = null;
100
        if (null === $phpFilesSupported) {
101
            $phpFilesSupported = PhpFilesAdapter::isSupported();
102
        }
103
        return $phpFilesSupported;
104
    }
105
106
    /**
107
     * @param string $class
108
     * @param array $args
109
     * @return CacheInterface
110
     */
111
    protected function createCache($class, $args)
112
    {
113
        /** @var CacheInterface $cache */
114
        $cache = Injector::inst()->createWithArgs($class, $args);
115
116
        // Assign cache logger
117
        if ($this->logger && $cache instanceof LoggerAwareInterface) {
118
            $cache->setLogger($this->logger);
119
        }
120
121
        return $cache;
122
    }
123
}
124