Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 97bab39

Browse files
committed
Add lock in MockFileSession to provide atomic writes
1 parent e6cfa09 commit 97bab39

File tree

1 file changed

+46
-6
lines changed

1 file changed

+46
-6
lines changed

src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
namespace Symfony\Component\HttpFoundation\Session\Storage;
1313

14+
use Symfony\Component\Lock\Exception\LockConflictedException;
15+
use Symfony\Component\Lock\Exception\LockStorageException;
16+
1417
/**
1518
* MockFileSessionStorage is used to mock sessions for
1619
* functional testing when done in a single PHP process.
@@ -25,6 +28,7 @@
2528
class MockFileSessionStorage extends MockArraySessionStorage
2629
{
2730
private $savePath;
31+
private $handle;
2832

2933
/**
3034
* @param string $savePath Path of directory to save session files
@@ -103,7 +107,11 @@ public function save()
103107

104108
try {
105109
if ($data) {
106-
file_put_contents($this->getFilePath(), serialize($data));
110+
rewind($this->handle);
111+
fwrite($this->handle, serialize($data));
112+
flock($this->handle, \LOCK_UN | \LOCK_NB);
113+
fclose($this->handle);
114+
$this->handle = null;
107115
} else {
108116
$this->destroy();
109117
}
@@ -123,9 +131,12 @@ public function save()
123131
*/
124132
private function destroy(): void
125133
{
126-
if (is_file($this->getFilePath())) {
127-
unlink($this->getFilePath());
128-
}
134+
$this->lock();
135+
ftruncate($this->handle, 0);
136+
137+
flock($this->handle, \LOCK_UN | \LOCK_NB);
138+
fclose($this->handle);
139+
$this->handle = null;
129140
}
130141

131142
/**
@@ -141,9 +152,38 @@ private function getFilePath(): string
141152
*/
142153
private function read(): void
143154
{
144-
$filePath = $this->getFilePath();
145-
$this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : [];
155+
$this->lock();
156+
rewind($this->handle);
157+
$this->data = unserialize(stream_get_contents($this->handle)) ?: [];
146158

147159
$this->loadSession();
148160
}
161+
162+
private function lock(): void
163+
{
164+
if (null !== $this->handle) {
165+
return;
166+
}
167+
168+
$filePath = $this->getFilePath();
169+
// Silence error reporting
170+
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
171+
if (!$this->handle = fopen($filePath, 'r+')) {
172+
if ($this->handle = fopen($filePath, 'x+')) {
173+
chmod($filePath, 0666);
174+
} elseif (!$this->handle = fopen($filePath, 'r+')) {
175+
usleep(100); // Give some time for chmod() to complete
176+
$this->handle = fopen($filePath, 'r+');
177+
}
178+
}
179+
restore_error_handler();
180+
if (!$this->handle) {
181+
throw new \RuntimeException('Unable to read session file');
182+
}
183+
if (!flock($this->handle, \LOCK_EX|\LOCK_NB)) {
184+
fclose($this->handle);
185+
$this->handle = null;
186+
throw new \RuntimeException('Unable to read session file');
187+
}
188+
}
149189
}

0 commit comments

Comments
 (0)