Passed
Push — master ( ddbf8b...086098 )
by Robbie
11:17
created

DefaultCacheFactory::create()   C

Complexity

Conditions 11
Paths 256

Size

Total Lines 35
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 16
nc 256
nop 2
dl 0
loc 35
rs 5.7833
c 0
b 0
f 0

How to fix   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
namespace SilverStripe\Core\Cache;
4
5
use Psr\Log\LoggerAwareInterface;
6
use Psr\Log\LoggerInterface;
7
use Psr\SimpleCache\CacheInterface;
8
use SilverStripe\Control\Director;
9
use SilverStripe\Core\Injector\Injector;
10
use Symfony\Component\Cache\Simple\FilesystemCache;
11
use Symfony\Component\Cache\Simple\ApcuCache;
12
use Symfony\Component\Cache\Simple\ChainCache;
13
use Symfony\Component\Cache\Simple\PhpFilesCache;
14
use Symfony\Component\Cache\Adapter\ApcuAdapter;
15
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
16
17
/**
18
 * Returns the most performant combination of caches available on the system:
19
 * - `PhpFilesCache` (PHP 7 with opcache enabled)
20
 * - `ApcuCache` (requires APC) with a `FilesystemCache` fallback (for larger cache volumes)
21
 * - `FilesystemCache` if none of the above is available
22
 *
23
 * Modelled after `Symfony\Component\Cache\Adapter\AbstractAdapter::createSystemCache()`
24
 */
25
class DefaultCacheFactory implements CacheFactory
26
{
27
    /**
28
     * @var string Absolute directory path
29
     */
30
    protected $args = [];
31
32
    /**
33
     * @var LoggerInterface
34
     */
35
    protected $logger;
36
37
    /**
38
     * @param array $args List of global options to merge with args during create()
39
     * @param LoggerInterface $logger Logger instance to assign
40
     */
41
    public function __construct($args = [], LoggerInterface $logger = null)
42
    {
43
        $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...
44
        $this->logger = $logger;
45
    }
46
47
    /**
48
     * @inheritdoc
49
     */
50
    public function create($service, array $args = array())
51
    {
52
        // merge args with default
53
        $args = array_merge($this->args, $args);
0 ignored issues
show
Bug introduced by
$this->args of type string is incompatible with the type array expected by parameter $array1 of array_merge(). ( Ignorable by Annotation )

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

53
        $args = array_merge(/** @scrutinizer ignore-type */ $this->args, $args);
Loading history...
54
        $namespace = isset($args['namespace']) ? $args['namespace'] : '';
55
        $defaultLifetime = isset($args['defaultLifetime']) ? $args['defaultLifetime'] : 0;
56
        $directory = isset($args['directory']) ? $args['directory'] : null;
57
        $version = isset($args['version']) ? $args['version'] : null;
58
59
        // In-memory caches are typically more resource constrained (number of items and storage space).
60
        // Give cache consumers an opt-out if they are expecting to create large caches with long lifetimes.
61
        $useInMemoryCache = isset($args['useInMemoryCache']) ? $args['useInMemoryCache'] : true;
62
63
        // Check support
64
        $apcuSupported = ($this->isAPCUSupported() && $useInMemoryCache);
65
        $phpFilesSupported = $this->isPHPFilesSupported();
66
67
        // If apcu isn't supported, phpfiles is the next best preference
68
        if (!$apcuSupported && $phpFilesSupported) {
69
            return $this->createCache(PhpFilesCache::class, [$namespace, $defaultLifetime, $directory]);
70
        }
71
72
        // Create filessytem cache
73
        $fs = $this->createCache(FilesystemCache::class, [$namespace, $defaultLifetime, $directory]);
74
        if (!$apcuSupported) {
75
            return $fs;
76
        }
77
78
        // Chain this cache with ApcuCache
79
        // Note that the cache lifetime will be shorter there by default, to ensure there's enough
80
        // resources for "hot cache" items in APCu as a resource constrained in memory cache.
81
        $apcuNamespace = $namespace . ($namespace ? '_' : '') . md5(BASE_PATH);
82
        $apcu = $this->createCache(ApcuCache::class, [$apcuNamespace, (int) $defaultLifetime / 5, $version]);
83
84
        return $this->createCache(ChainCache::class, [[$apcu, $fs]]);
85
    }
86
87
    /**
88
     * Determine if apcu is supported
89
     *
90
     * @return bool
91
     */
92
    protected function isAPCUSupported()
93
    {
94
        static $apcuSupported = null;
95
        if (null === $apcuSupported) {
96
            // Need to check for CLI because Symfony won't: https://github.com/symfony/symfony/pull/25080
97
            $apcuSupported = Director::is_cli()
98
                ? ini_get('apc.enable_cli') && ApcuAdapter::isSupported()
99
                : ApcuAdapter::isSupported();
100
        }
101
        return $apcuSupported;
102
    }
103
104
    /**
105
     * Determine if PHP files is supported
106
     *
107
     * @return bool
108
     */
109
    protected function isPHPFilesSupported()
110
    {
111
        static $phpFilesSupported = null;
112
        if (null === $phpFilesSupported) {
113
            $phpFilesSupported = PhpFilesAdapter::isSupported();
114
        }
115
        return $phpFilesSupported;
116
    }
117
118
    /**
119
     * @param string $class
120
     * @param array $args
121
     * @return CacheInterface
122
     */
123
    protected function createCache($class, $args)
124
    {
125
        /** @var CacheInterface $cache */
126
        $cache = Injector::inst()->createWithArgs($class, $args);
127
128
        // Assign cache logger
129
        if ($this->logger && $cache instanceof LoggerAwareInterface) {
130
            $cache->setLogger($this->logger);
131
        }
132
133
        return $cache;
134
    }
135
}
136