GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( a0b1d8...98f4b9 )
by De
01:45
created

Cache::setMultiple()   C

Complexity

Conditions 8
Paths 31

Size

Total Lines 46
Code Lines 22

Duplication

Lines 5
Ratio 10.87 %

Importance

Changes 0
Metric Value
dl 5
loc 46
rs 5.5555
c 0
b 0
f 0
cc 8
eloc 22
nc 31
nop 3
1
<?php
2
3
/**
4
 * This file is part of the PHPMongo package.
5
 *
6
 * (c) Dmytro Sokil <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sokil\Mongo;
13
14
use Psr\SimpleCache\CacheInterface;
15
use Sokil\Mongo\Cache\Exception\CacheException;
16
use Sokil\Mongo\Cache\Exception\InvalidArgumentException;
17
18
class Cache implements \Countable, CacheInterface
19
{
20
    const FIELD_NAME_VALUE = 'v';
21
    const FIELD_NAME_EXPIRED = 'e';
22
    const FIELD_NAME_TAGS = 't';
23
    
24
    private $collection;
25
    
26
    public function __construct(Database $database, $collectionName)
27
    {
28
        $this->collection = $database
29
            ->map($collectionName, array(
30
                'index' => array(
31
                    // date field
32
                    array(
33
                        'keys' => array(self::FIELD_NAME_EXPIRED => 1),
34
                        'expireAfterSeconds' => 0
35
                    ),
36
                )
37
            ))
38
            ->getCollection($collectionName)
39
            ->disableDocumentPool();
40
    }
41
42
    /**
43
     * @return Cache
44
     */
45
    public function init()
46
    {
47
        $this->collection->initIndexes();
48
        return $this;
49
    }
50
51
    /**
52
     * Persists a set of key => value pairs in the cache, with an optional TTL.
53
     *
54
     * @param array                     $values A list of key => value pairs for a multiple-set operation.
55
     * @param null|int|\DateInterval    $ttl    Optional. The TTL value of this item. If no value is sent and
56
     *                                          the driver supports TTL then the library may set a default value
57
     *                                          for it or let the driver take care of that.
58
     * @param array                     $tags   List of tags
59
     *
60
     * @return bool True on success and false on failure.
61
     *
62
     * @throws \Psr\SimpleCache\InvalidArgumentException
63
     * MUST be thrown if $values is neither an array nor a Traversable,
64
     * or if any of the $values are not a legal value.
65
     */
66
    public function setMultiple($values, $ttl = null, array $tags = array())
67
    {
68
        // prepare expiration
69
        if (!empty($ttl)) {
70 View Code Duplication
            if ($ttl instanceof \DateInterval) {
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...
71
                $ttl = $ttl->s;
72
            } elseif (!is_int($ttl)) {
73
                throw new InvalidArgumentException('Invalid TTL specified');
74
            }
75
76
            $expirationTimestamp = time() + $ttl;
77
        }
78
79
        // prepare persistence
80
        $persistence = $this->collection->getDatabase()->getClient()->createPersistence();
81
82
        // prepare documents to store
83
        foreach ($values as $key => $value) {
84
            // create document
85
            $document = array(
86
                '_id' => $key,
87
                self::FIELD_NAME_VALUE => $value,
88
            );
89
90
            // add expiration
91
            if (!empty($expirationTimestamp)) {
92
                $document[self::FIELD_NAME_EXPIRED] = new \MongoDate($expirationTimestamp);
93
            }
94
95
            // prepare tags
96
            if (!empty($tags)) {
97
                $document[self::FIELD_NAME_TAGS] = $tags;
98
            }
99
100
            // attach document
101
            $persistence->persist($this->collection->createDocument($document));
102
        }
103
104
        try {
105
            $persistence->flush();
106
        } catch (\Exception $e) {
107
            return false;
108
        }
109
110
        return true;
111
    }
112
113
    /**
114
     * Set with expiration on concrete date
115
     *
116
     * @deprecated Use self::set() with calculated ttl
117
     *
118
     * @param int|string $key
119
     * @param mixed $value
120
     * @param int $expirationTime
121
     * @param array $tags
122
     *
123
     * @throws Exception
124
     *
125
     * @return bool
126
     */
127
    public function setDueDate($key, $value, $expirationTime, array $tags = null)
128
    {
129
        return $this->set($key, $value, $expirationTime - time(), $tags);
130
    }
131
    
132
    /**
133
     * Set key that never expired
134
     *
135
     * @deprecated Use self::set() with null in ttl
136
     *
137
     * @param int|string $key
138
     * @param mixed $value
139
     * @param array $tags
140
     *
141
     * @return bool
142
     */
143
    public function setNeverExpired($key, $value, array $tags = null)
144
    {
145
        return $this->set($key, $value, null, $tags);
146
    }
147
    
148
    /**
149
     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
150
     *
151
     * @param string                    $key    The key of the item to store.
152
     * @param mixed                     $value  The value of the item to store, must be serializable.
153
     * @param null|int|\DateInterval    $ttl    Optional. The TTL value of this item. If no value is sent and
154
     *                                          the driver supports TTL then the library may set a default value
155
     *                                          for it or let the driver take care of that.
156
     * @param array                     $tags   List of tags
157
     *
158
     * @return bool True on success and false on failure.
159
     *
160
     * @throws InvalidArgumentException
161
     * @throws CacheException
162
     */
163
    public function set($key, $value, $ttl = null, array $tags = null)
164
    {
165
        // create document
166
        $document = array(
167
            '_id' => $key,
168
            self::FIELD_NAME_VALUE => $value,
169
        );
170
171
        // prepare expiration
172
        if (!empty($ttl)) {
173 View Code Duplication
            if ($ttl instanceof \DateInterval) {
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...
174
                $ttl = $ttl->s;
175
            } elseif (!is_int($ttl)) {
176
                throw new InvalidArgumentException('Invalid TTL specified');
177
            }
178
179
            $expirationTimestamp = time() + $ttl;
180
            $document[self::FIELD_NAME_EXPIRED] = new \MongoDate((int) $expirationTimestamp);
181
        }
182
183
        // prepare tags
184
        if (!empty($tags)) {
185
            $document[self::FIELD_NAME_TAGS] = $tags;
186
        }
187
188
        // create document
189
        $result = $this
190
            ->collection
191
            ->getMongoCollection()
192
            ->update(
193
                array(
194
                    '_id' => $key,
195
                ),
196
                $document,
197
                array(
198
                    'upsert' => true,
199
                )
200
            );
201
202
        // check result
203
        return (double) 1 === $result['ok'];
204
    }
205
206
        /**
207
     * @param array $keys
208
     * @param mixed|null $default
209
     *
210
     * @return array
211
     */
212
    public function getMultiple($keys, $default = null)
213
    {
214
        // Prepare defaults
215
        $values = array_fill_keys($keys, $default);
216
217
        // Get document
218
        $documents = $this->collection->getDocuments($keys);
219
        if (empty($documents)) {
220
            return $values;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $values; (array) is incompatible with the return type declared by the interface Psr\SimpleCache\CacheInterface::getMultiple of type Psr\SimpleCache\iterable.

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...
221
        }
222
223
        // Mongo deletes document not exactly when set in field
224
        // Required date checking
225
        // Expiration may be empty for keys which never expired
226
        foreach ($documents as $document) {
227
            /** @var \MongoDate $expiredAt */
228
            $expiredAt = $document->get(self::FIELD_NAME_EXPIRED);
229
            if (empty($expiredAt) || $expiredAt->sec >= time()) {
230
                $values[$document->getId()] = $document->get(self::FIELD_NAME_VALUE);
231
            } else {
232
                $values[$document->getId()] = $default;
233
            }
234
        }
235
236
        return $values;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $values; (array) is incompatible with the return type declared by the interface Psr\SimpleCache\CacheInterface::getMultiple of type Psr\SimpleCache\iterable.

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...
237
    }
238
239
    /**
240
     * Get value by key
241
     *
242
     * @param string $key
243
     * @param mixed $default
244
     *
245
     * @return array|null
246
     */
247
    public function get($key, $default = null)
248
    {
249
        // Get document
250
        $document = $this->collection->getDocument($key);
251
        if (!$document) {
252
            return $default;
253
        }
254
255
        // Mongo deletes document not exactly when set in field
256
        // Required date checking
257
        // Expiration may be empty for keys which never expired
258
        $expiredAt = $document->get(self::FIELD_NAME_EXPIRED);
259
        if (!empty($expiredAt) && $expiredAt->sec < time()) {
260
            return $default;
261
        }
262
263
        // Return value
264
        return $document->get(self::FIELD_NAME_VALUE);
265
    }
266
267
268
    /**
269
     * @return Cache
270
     */
271
    public function clear()
272
    {
273
        $this->collection->delete();
274
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Sokil\Mongo\Cache) is incompatible with the return type declared by the interface Psr\SimpleCache\CacheInterface::clear of type boolean.

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...
275
    }
276
277
    /**
278
     * Delete an item from the cache by its unique key.
279
     *
280
     * @param string $key The unique cache key of the item to delete.
281
     *
282
     * @return bool True if the item was successfully removed. False if there was an error.
283
     *
284
     * @throws \Psr\SimpleCache\InvalidArgumentException
285
     * MUST be thrown if the $key string is not a legal value.
286
     */
287
    public function delete($key)
288
    {
289
        if (empty($key) || !is_string($key)) {
290
            throw new InvalidArgumentException('Key must be string');
291
        }
292
        try {
293
            $this->collection->batchDelete(array(
294
                '_id' => $key,
295
            ));
296
        } catch (\Exception $e) {
297
            return false;
298
        }
299
300
        return true;
301
    }
302
303
    /**
304
     * Deletes multiple cache items in a single operation.
305
     *
306
     * @param array $keys A list of string-based keys to be deleted.
307
     *
308
     * @return bool True if the items were successfully removed. False if there was an error.
309
     *
310
     * @throws \Psr\SimpleCache\InvalidArgumentException
311
     * MUST be thrown if $keys is neither an array nor a Traversable,
312
     * or if any of the $keys are not a legal value.
313
     */
314
    public function deleteMultiple($keys)
315
    {
316
        try {
317
            $this->collection->batchDelete(
318
                function(Expression $e) use($keys) {
319
                    $e->whereIn('_id', $keys);
320
                }
321
            );
322
        } catch (\Exception $e) {
323
            return false;
324
        }
325
326
        return true;
327
    }
328
    
329
    /**
330
     * Delete documents by tag
331
     */
332
    public function deleteMatchingTag($tag)
333
    {
334
        $this->collection->batchDelete(function (\Sokil\Mongo\Expression $e) use ($tag) {
335
            return $e->where(Cache::FIELD_NAME_TAGS, $tag);
336
        });
337
        
338
        return $this;
339
    }
340
    
341
    /**
342
     * Delete documents by tag
343
     */
344
    public function deleteNotMatchingTag($tag)
345
    {
346
        $this->collection->batchDelete(function (\Sokil\Mongo\Expression $e) use ($tag) {
347
            return $e->whereNotEqual(Cache::FIELD_NAME_TAGS, $tag);
348
        });
349
        
350
        return $this;
351
    }
352
    
353
    /**
354
     * Delete documents by tag
355
     * Document deletes if it contains all passed tags
356
     */
357
    public function deleteMatchingAllTags(array $tags)
358
    {
359
        $this->collection->batchDelete(function (\Sokil\Mongo\Expression $e) use ($tags) {
360
            return $e->whereAll(Cache::FIELD_NAME_TAGS, $tags);
361
        });
362
        
363
        return $this;
364
    }
365
    
366
    /**
367
     * Delete documents by tag
368
     * Document deletes if it not contains all passed tags
369
     */
370
    public function deleteMatchingNoneOfTags(array $tags)
371
    {
372
        $this->collection->batchDelete(function (\Sokil\Mongo\Expression $e) use ($tags) {
373
            return $e->whereNoneOf(Cache::FIELD_NAME_TAGS, $tags);
374
        });
375
        
376
        return $this;
377
    }
378
    
379
    /**
380
     * Delete documents by tag
381
     * Document deletes if it contains any of passed tags
382
     */
383
    public function deleteMatchingAnyTag(array $tags)
384
    {
385
        $this->collection->batchDelete(function (\Sokil\Mongo\Expression $e) use ($tags) {
386
            return $e->whereIn(Cache::FIELD_NAME_TAGS, $tags);
387
        });
388
        
389
        return $this;
390
    }
391
    
392
    /**
393
     * Delete documents by tag
394
     * Document deletes if it contains any of passed tags
395
     */
396
    public function deleteNotMatchingAnyTag(array $tags)
397
    {
398
        $this->collection->batchDelete(function (\Sokil\Mongo\Expression $e) use ($tags) {
399
            return $e->whereNotIn(Cache::FIELD_NAME_TAGS, $tags);
400
        });
401
        
402
        return $this;
403
    }
404
405
    /**
406
     * Get total count of documents in cache
407
     *
408
     * @return int
409
     */
410
    public function count()
411
    {
412
        return $this->collection->count();
413
    }
414
    
415
    public function has($key)
416
    {
417
        return (bool) $this->get($key);
418
    }
419
}
420