Passed
Pull Request — master (#42)
by
unknown
12:42
created

ApcuCache::has()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\MoTranslator\Cache;
6
7
use PhpMyAdmin\MoTranslator\CacheException;
8
use PhpMyAdmin\MoTranslator\MoParser;
9
10
use function apcu_enabled;
11
use function apcu_entry;
12
use function apcu_exists;
13
use function apcu_fetch;
14
use function apcu_store;
15
use function array_combine;
16
use function array_keys;
17
use function array_map;
18
use function assert;
19
use function function_exists;
20
use function is_array;
21
use function is_string;
22
23
final class ApcuCache implements CacheInterface
24
{
25
    public const LOADED_KEY = '__TRANSLATIONS_LOADED__';
26
27
    /** @var MoParser */
28
    private $parser;
29
    /** @var string */
30
    private $locale;
31
    /** @var string */
32
    private $domain;
33
    /** @var int */
34
    private $ttl;
35
    /** @var bool */
36
    private $reloadOnMiss;
37
    /** @var string */
38
    private $prefix;
39
40
    public function __construct(
41
        MoParser $parser,
42
        string $locale,
43
        string $domain,
44
        int $ttl = 0,
45
        bool $reloadOnMiss = true,
46
        string $prefix = 'mo_'
47
    ) {
48
        // @codeCoverageIgnoreStart
49
        if (! (function_exists('apcu_enabled') && apcu_enabled())) {
50
            throw new CacheException('ACPu extension must be installed and enabled');
51
        }
52
53
        // @codeCoverageIgnoreEnd
54
55
        $this->parser = $parser;
56
        $this->locale = $locale;
57
        $this->domain = $domain;
58
        $this->ttl = $ttl;
59
        $this->reloadOnMiss = $reloadOnMiss;
60
        $this->prefix = $prefix;
61
62
        $this->ensureTranslationsLoaded();
63
    }
64
65
    public function get(string $msgid): string
66
    {
67
        $msgstr = apcu_fetch($this->getKey($msgid), $success);
68
        if ($success && is_string($msgstr)) {
69
            return $msgstr;
70
        }
71
72
        if (! $this->reloadOnMiss) {
73
            return $msgid;
74
        }
75
76
        // store original in case translation is not present
77
        apcu_store($msgid, $msgid);
78
        // reload .mo file, in case entry has been evicted
79
        $this->parser->parseIntoCache($this);
80
81
        $msgstr = apcu_fetch($this->getKey($msgid), $success);
82
83
        return $success && is_string($msgstr) ? $msgstr : $msgid;
84
    }
85
86
    public function set(string $msgid, string $msgstr): void
87
    {
88
        apcu_store($this->getKey($msgid), $msgstr, $this->ttl);
89
    }
90
91
    public function has(string $msgid): bool
92
    {
93
        return apcu_exists($this->getKey($msgid));
0 ignored issues
show
Bug Best Practice introduced by
The expression return apcu_exists($this->getKey($msgid)) could return the type string[] which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
94
    }
95
96
    public function setAll(array $translations): void
97
    {
98
        $keys = array_map(function (string $msgid): string {
99
            return $this->getKey($msgid);
100
        }, array_keys($translations));
101
        $translations = array_combine($keys, $translations);
102
        assert(is_array($translations));
103
104
        apcu_store($translations, null, $this->ttl);
105
    }
106
107
    private function getKey(string $msgid): string
108
    {
109
        return $this->prefix . $this->locale . '.' . $this->domain . '.' . $msgid;
110
    }
111
112
    private function ensureTranslationsLoaded(): void
113
    {
114
        // Try to prevent cache slam if multiple processes are trying to load translations. There is still a race
115
        // between the exists check and creating the entry, but at least it's small
116
        $key = $this->getKey(self::LOADED_KEY);
117
        $loaded = apcu_exists($key) || apcu_entry($key, static function (): int {
118
            return 0;
119
        });
120
        if ($loaded) {
121
            return;
122
        }
123
124
        $this->parser->parseIntoCache($this);
125
        apcu_store($this->getKey(self::LOADED_KEY), 1, $this->ttl);
126
    }
127
}
128