Completed
Branch 2.x (8ca90a)
by Julián
08:08
created

Filesystem::close()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/*
4
 * sessionware (https://github.com/juliangut/sessionware).
5
 * PSR7 compatible session management.
6
 *
7
 * @license BSD-3-Clause
8
 * @link https://github.com/juliangut/sessionware
9
 * @author Julián Gutiérrez <[email protected]>
10
 */
11
12
declare(strict_types=1);
13
14
namespace Jgut\Sessionware\Handler;
15
16
use Jgut\Sessionware\Configuration;
17
18
/**
19
 * Filesystem session handler.
20
 */
21
class Filesystem implements Handler
22
{
23
    use HandlerTrait;
24
25
    /**
26
     * @var string
27
     */
28
    protected $savePath;
29
30
    /**
31
     * @var string
32
     */
33
    protected $filePrefix;
34
35
    /**
36
     * File session handler constructor.
37
     *
38
     * @param string $filePrefix
39
     */
40
    public function __construct($filePrefix = 'sess_')
41
    {
42
        $this->filePrefix = $filePrefix;
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     *
48
     * @throws \RuntimeException
49
     */
50
    public function open($savePath, $sessionName)
51
    {
52
        $this->testConfiguration();
53
54
        $savePath = $this->configuration->getSavePath();
55
        $sessionName = $this->configuration->getName();
56
57
        $savePathParts = explode(DIRECTORY_SEPARATOR, rtrim($savePath, DIRECTORY_SEPARATOR));
58
        if ($sessionName !== Configuration::SESSION_NAME_DEFAULT && $sessionName !== array_pop($savePathParts)) {
59
            $savePath .= DIRECTORY_SEPARATOR . $sessionName;
60
        }
61
62
        if (!is_dir($savePath) && !@mkdir($savePath, 0777, true) && !is_dir($savePath)) {
63
            // @codeCoverageIgnoreStart
64
            throw new \RuntimeException(
65
                sprintf('Failed to create session save path "%s", directory might be write protected', $savePath)
66
            );
67
            // @codeCoverageIgnoreEnd
68
        }
69
70
        $this->savePath = $savePath;
71
72
        return true;
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     *
78
     * @return bool
79
     */
80
    public function close()
81
    {
82
        return true;
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     *
88
     * @return bool
89
     */
90
    public function destroy($sessionId)
91
    {
92
        $sessionFile = $this->getSessionFile($sessionId);
93
94
        if (file_exists($sessionFile)) {
95
            unlink($sessionFile);
96
        }
97
98
        return false;
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104
    public function read($sessionId)
105
    {
106
        $sessionFile = $this->getSessionFile($sessionId);
107
108
        if (!file_exists($sessionFile)) {
109
            $sessionData = serialize([]);
110
111
            $this->write($sessionId, $sessionData);
112
113
            return $sessionData;
114
        }
115
116
        $fileDescriptor = fopen($sessionFile, 'rb');
117
        flock($fileDescriptor, \LOCK_SH);
118
119
        $sessionData = stream_get_contents($fileDescriptor);
120
121
        flock($fileDescriptor, \LOCK_UN);
122
        fclose($fileDescriptor);
123
124
        return $this->decryptSessionData($sessionData);
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function write($sessionId, $sessionData)
131
    {
132
        $sessionFile = $this->getSessionFile($sessionId);
133
        if (!file_exists($sessionFile)) {
134
            touch($sessionFile);
135
            chmod($sessionFile, 0777);
136
        }
137
138
        // file_put_contents locking does not support user-land stream wrappers (https://bugs.php.net/bug.php?id=72950)
139
        $fileDescriptor = fopen($sessionFile, 'w');
140
        flock($fileDescriptor, \LOCK_EX);
141
142
        fwrite($fileDescriptor, $this->encryptSessionData($sessionData));
143
144
        flock($fileDescriptor, \LOCK_UN);
145
        fclose($fileDescriptor);
146
147
        return true;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     *
153
     * @SuppressWarnings(PMD.ShortMethodName)
154
     * @SuppressWarnings(PMD.UnusedFormalParameter)
155
     */
156
    public function gc($maxLifetime)
157
    {
158
        $currentTime = time();
159
160
        foreach (new \DirectoryIterator($this->savePath) as $sessionFile) {
161
            if ($sessionFile->isFile()
162
                && strpos($sessionFile->getFilename(), $this->filePrefix) === 0
163
                && $this->configuration->getLifetime() < ($currentTime - $sessionFile->getMTime())
164
            ) {
165
                unlink($sessionFile->getPathname());
166
            }
167
        }
168
169
        return true;
170
    }
171
172
    /**
173
     * Get session file.
174
     *
175
     * @param string $sessionId
176
     *
177
     * @return string
178
     */
179
    protected function getSessionFile(string $sessionId) : string
180
    {
181
        return $this->savePath . DIRECTORY_SEPARATOR . $this->filePrefix . $sessionId;
182
    }
183
}
184