Completed
Push — master ( c1245a...219d8a )
by Hannes
03:55
created

CacheItemPool::clear()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 13
ccs 7
cts 7
cp 1
rs 9.4285
cc 2
eloc 8
nc 2
nop 0
crap 2
1
<?php
2
namespace Madewithlove\IlluminatePsrCacheBridge\Laravel;
3
4
use DateTimeImmutable;
5
use Exception;
6
use Illuminate\Contracts\Cache\Repository;
7
use Madewithlove\IlluminatePsrCacheBridge\Exceptions\InvalidArgumentException;
8
use Psr\Cache\CacheItemInterface;
9
use Psr\Cache\CacheItemPoolInterface;
10
11
class CacheItemPool implements CacheItemPoolInterface
12
{
13
    /**
14
     * @var \Illuminate\Contracts\Cache\Repository
15
     */
16
    private $repository;
17
18
    /**
19
     * @var \Psr\Cache\CacheItemInterface[]
20
     */
21
    private $deferred = [];
22
23
    /**
24
     * @param \Illuminate\Contracts\Cache\Repository $repository
25
     */
26 140
    public function __construct(Repository $repository)
27
    {
28 140
        $this->repository = $repository;
29 140
    }
30
31
    /**
32
     * Destructor.
33
     */
34 22
    public function __destruct()
35
    {
36 22
        $this->commit();
37 22
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42 75
    public function getItem($key)
43
    {
44 75
        $this->validateKey($key);
45
46 58
        if (isset($this->deferred[$key])) {
47 5
            return clone($this->deferred[$key]);
48 58
        } elseif ($this->repository->has($key)) {
49 2
            return new CacheItem($key, unserialize($this->repository->get($key)), true);
50
        } else {
51 57
            return new CacheItem($key);
52
        }
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function getItems(array $keys = array())
59
    {
60 22
        return array_combine($keys, array_map(function ($key) {
61 20
            return $this->getItem($key);
62 22
        }, $keys));
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68 31
    public function hasItem($key)
69
    {
70 31
        $this->validateKey($key);
71
72 13
        if (isset($this->deferred[$key])) {
73 1
            $item = $this->deferred[$key];
74
75 1
            if (! $item->getExpiresAt()) {
0 ignored issues
show
Bug introduced by
The method getExpiresAt() does not exist on Psr\Cache\CacheItemInterface. Did you maybe mean expiresAt()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
76 1
                return true;
77
            }
78
79
            return $item->getExpiresAt() > new DateTimeImmutable();
0 ignored issues
show
Bug introduced by
The method getExpiresAt() does not exist on Psr\Cache\CacheItemInterface. Did you maybe mean expiresAt()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
80
        }
81
82 12
        return $this->repository->has($key);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 121
    public function clear()
89
    {
90
        try {
91
            /* @var \Illuminate\Contracts\Cache\Store $store */
92 121
            $this->deferred = [];
93 121
            $store = $this->repository;
94 121
            $store->flush();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Cache\Repository as the method flush() does only exist in the following implementations of said interface: Illuminate\Cache\RedisTaggedCache, Illuminate\Cache\TaggedCache.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
95 1
        } catch (Exception $exception) {
96 1
            return false;
97
        }
98
99 120
        return true;
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105 24
    public function deleteItem($key)
106
    {
107 24
        $this->validateKey($key);
108
109 6
        unset($this->deferred[$key]);
110
111 6
        if (! $this->hasItem($key)) {
112 4
            return true;
113
        }
114
115 2
        return $this->repository->forget($key);
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121 21
    public function deleteItems(array $keys)
122
    {
123
        // Validating all keys first.
124 21
        foreach ($keys as $key) {
125 20
            $this->validateKey($key);
126
        }
127
128 3
        $success = true;
129
130 3
        foreach ($keys as $key) {
131 2
            $success = $success && $this->deleteItem($key);
132
        }
133
134 3
        return $success;
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140 34
    public function save(CacheItemInterface $item)
141
    {
142 34
        $expiresAt = $item->getExpiresAt();
0 ignored issues
show
Bug introduced by
The method getExpiresAt() does not exist on Psr\Cache\CacheItemInterface. Did you maybe mean expiresAt()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
143
144
        try {
145 34
            if (! $expiresAt) {
146 30
                $this->repository->forever($item->getKey(), serialize($item->get()));
147
            } else {
148 4
                $now = new DateTimeImmutable('now', $expiresAt->getTimezone());
149
150 4
                $seconds = $expiresAt->getTimestamp() - $now->getTimestamp();
151 4
                $minutes = (int) floor($seconds / 60.0);
152
153 4
                if ($minutes <= 0) {
154 2
                    $this->repository->forget($item->getKey());
155
156 2
                    return false;
157
                }
158
159 31
                $this->repository->put($item->getKey(), serialize($item->get()), $minutes);
160
            }
161 1
        } catch (Exception $exception) {
162 1
            return false;
163
        }
164
165 31
        return true;
166
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171 11
    public function saveDeferred(CacheItemInterface $item)
172
    {
173 11
        $expiresAt = $item->getExpiresAt();
0 ignored issues
show
Bug introduced by
The method getExpiresAt() does not exist on Psr\Cache\CacheItemInterface. Did you maybe mean expiresAt()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
174
175 11
        if ($expiresAt && ($expiresAt < new DateTimeImmutable())) {
176 1
            return false;
177
        }
178
179 10
        $item = (new CacheItem($item->getKey(), $item->get(), true))->expiresAt($expiresAt);
180
181 10
        $this->deferred[$item->getKey()] = $item;
182
183 10
        return true;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189 30
    public function commit()
190
    {
191 30
        $success = true;
192
193 30
        foreach ($this->deferred as $key => $item) {
194 8
            $success = $success && $this->save($item);
195
        }
196
197 30
        $this->deferred = [];
198
199 30
        return $success;
200
    }
201
202
    /**
203
     * @param string $key
204
     *
205
     * @throws \Psr\Cache\InvalidArgumentException
206
     */
207 134
    private function validateKey($key)
208
    {
209 134
        if (!is_string($key) || preg_match('#[{}\(\)/\\\\@:]#', $key)) {
210 88
            throw new InvalidArgumentException();
211
        }
212 81
    }
213
}
214