11
11
12
12
namespace Symfony \Component \HttpFoundation \Session \Storage ;
13
13
14
+ use Symfony \Component \Lock \Exception \LockConflictedException ;
15
+ use Symfony \Component \Lock \Exception \LockStorageException ;
16
+
14
17
/**
15
18
* MockFileSessionStorage is used to mock sessions for
16
19
* functional testing when done in a single PHP process.
25
28
class MockFileSessionStorage extends MockArraySessionStorage
26
29
{
27
30
private $ savePath ;
31
+ private $ handle ;
28
32
29
33
/**
30
34
* @param string $savePath Path of directory to save session files
@@ -103,7 +107,11 @@ public function save()
103
107
104
108
try {
105
109
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 ;
107
115
} else {
108
116
$ this ->destroy ();
109
117
}
@@ -123,9 +131,12 @@ public function save()
123
131
*/
124
132
private function destroy (): void
125
133
{
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 ;
129
140
}
130
141
131
142
/**
@@ -141,9 +152,38 @@ private function getFilePath(): string
141
152
*/
142
153
private function read (): void
143
154
{
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 )) ?: [];
146
158
147
159
$ this ->loadSession ();
148
160
}
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
+ }
149
189
}
0 commit comments