Passed
Push — master ( 5a87d9...5af04f )
by Mihail
03:04
created

ShmopClient::delete()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 3
c 1
b 0
f 1
nc 2
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
1
<?php
2
3
/*
4
 * This file is part of the Koded package.
5
 *
6
 * (c) Mihail Binev <[email protected]>
7
 *
8
 * Please view the LICENSE distributed with this source code
9
 * for the full copyright and license information.
10
 *
11
 */
12
13
namespace Koded\Caching\Client;
14
15
use Exception;
16
use Koded\Caching\{Cache, CacheException};
17
use function Koded\Caching\verify_key;
18
19
/**
20
 * @property ShmopClient client
21
 *
22
 */
23
final class ShmopClient implements Cache
24
{
25
    use ClientTrait, MultiplesTrait;
26
27
    /** @var string */
28
    private $dir;
29
30 187
    public function __construct(string $dir, ?int $ttl)
31
    {
32 187
        $this->dir = $dir;
33 187
        $this->ttl = $ttl;
34 187
        $this->setDirectory($dir);
35 187
    }
36
37
38 61
    public function get($key, $default = null)
39
    {
40 61
        if (false === $this->has($key, $filename)) {
41 12
            return $default;
42
        }
43
44
        try {
45 37
            $resource = shmop_open(fileinode($filename), 'a', 0, 0);
46 37
            return unserialize(shmop_read($resource, 0, shmop_size($resource)));
47
        } finally {
48 37
            @shmop_close($resource);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for shmop_close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

48
            /** @scrutinizer ignore-unhandled */ @shmop_close($resource);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Bug introduced by
Are you sure the usage of shmop_close($resource) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
49
        }
50
    }
51
52
53 70
    public function set($key, $value, $ttl = null)
54
    {
55 70
        verify_key($key);
56 53
        if (1 > $expiration = $this->timestampWithGlobalTtl($ttl, Cache::DATE_FAR_FAR_AWAY)) {
57
            // The item is considered expired and must be deleted
58 1
            return $this->delete($key);
59
        }
60
61 43
        $value = serialize($value);
62 43
        $size = strlen($value);
63 43
        $filename = $this->filename($key, true);
64
65
        try {
66 43
            $resource = shmop_open(fileinode($filename), 'n', 0666, $size);
67 1
        } catch (Exception $e) {
68 1
            $resource = shmop_open(fileinode($filename), 'w', 0666, $size);
69
        }
70
71
        try {
72 43
            return shmop_write($resource, $value, 0) === $size
73 43
                && false !== file_put_contents($filename . '-ttl', $expiration);
74
75
        } finally {
76 43
            @shmop_close($resource);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for shmop_close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

76
            /** @scrutinizer ignore-unhandled */ @shmop_close($resource);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Bug introduced by
Are you sure the usage of shmop_close($resource) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
77
        }
78
    }
79
80
81 23
    public function delete($key)
82
    {
83 23
        if (false === $this->has($key, $filename)) {
84 5
            return true;
85
        }
86 5
        return $this->expire($filename);
87
    }
88
89
90 187
    public function clear()
91
    {
92 187
        foreach ((glob($this->dir . 'shmop-*.cache*') ?: []) as $filename) {
93 187
            $this->expire($filename);
94
        }
95 187
        return true;
96
    }
97
98
99 96
    public function has($key, &$filename = '')
100
    {
101 96
        verify_key($key);
102 45
        $filename = $this->filename($key, false);
103 45
        $expiration = (int)(@file_get_contents($filename . '-ttl') ?: 0);
104
105 45
        if ($expiration <= time()) {
106 14
            $this->expire($filename);
107 14
            return false;
108
        }
109 42
        return true;
110
    }
111
112
113 45
    private function filename(string $key, bool $create): string
114
    {
115 45
        $filename = $this->dir . 'shmop-' . sha1($key) . '.cache';
116
117 45
        if ($create) {
118 43
            touch($filename);
119 43
            touch($filename . '-ttl');
120 43
            chmod($filename, 0666);
121 43
            chmod($filename . '-ttl', 0666);
122
        }
123 45
        return $filename;
124
    }
125
126
    /**
127
     * Prepares the cache directory.
128
     *
129
     * @param string $directory
130
     *
131
     * @throws CacheException
132
     */
133 187
    private function setDirectory(string $directory): void
134
    {
135
        // Overrule shell misconfiguration or the web server
136 187
        umask(umask() | 0002);
137 187
        $dir = $directory ?: sys_get_temp_dir();
138 187
        $dir = rtrim($dir, '/') . '/';
139
140 187
        if (false === is_dir($dir) && false === mkdir($dir, 0775, true)) {
141
            throw CacheException::forCreatingDirectory($dir);
142
        }
143
144 187
        $this->dir = $dir;
145 187
    }
146
147 187
    private function expire(string $filename): bool
148
    {
149 187
        if (false === $resource = @shmop_open(fileinode($filename), 'w', 0, 0)) {
150 187
            return false;
151
        }
152
153
        try {
154 43
            unlink($filename . '-ttl');
155 43
            return shmop_delete($resource);
156
        } finally {
157 43
            @shmop_close($resource);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for shmop_close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

157
            /** @scrutinizer ignore-unhandled */ @shmop_close($resource);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Bug introduced by
Are you sure the usage of shmop_close($resource) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
158
        }
159
    }
160
}
161