PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.2
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.2
16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / vendor / wp-php-toolkit / filesystem / class-sqlitefilesystem.php
jetpack / vendor / wp-php-toolkit / filesystem Last commit date
ByteStream 6 days ago Layer 6 days ago Mixin 6 days ago Path 6 days ago Visitor 6 days ago LICENSE.md 6 days ago README.md 6 days ago class-filesystemexception.php 6 days ago class-inmemoryfilesystem.php 6 days ago class-localfilesystem.php 6 days ago class-sqlitefilesystem.php 6 days ago class-uploadedfilesystem.php 6 days ago composer.json 6 days ago functions.php 6 days ago interface-filesystem.php 6 days ago
class-sqlitefilesystem.php
410 lines
1 <?php
2
3 namespace WordPress\Filesystem;
4
5 use Exception;
6 use SQLite3;
7 use WordPress\ByteStream\MemoryPipe;
8 use WordPress\ByteStream\ReadStream\ByteReadStream;
9 use WordPress\Filesystem\Layer\ChrootLayer;
10 use WordPress\Filesystem\Mixin\BufferedWriteStreamViaPutContents;
11 use WordPress\Filesystem\Mixin\CopyRecursiveViaStreaming;
12 use WordPress\Filesystem\Mixin\MkdirRecursive;
13
14 /**
15 * Stores files in SQLite database.
16 */
17 class SQLiteFilesystem implements Filesystem {
18
19 use BufferedWriteStreamViaPutContents;
20 use MkdirRecursive;
21 use CopyRecursiveViaStreaming;
22
23 private $db;
24 private $transaction_level = 0;
25
26 public static function create( $db_path = ':memory:' ) {
27 return new ChrootLayer(
28 new SQLiteFilesystem( $db_path ),
29 '/'
30 );
31 }
32
33 public function get_root(): string {
34 return '/';
35 }
36
37 private function __construct( $db_path ) {
38 $this->db = new SQLite3( $db_path );
39 $this->db->exec(
40 '
41 CREATE TABLE IF NOT EXISTS files (
42 path TEXT PRIMARY KEY,
43 type TEXT NOT NULL,
44 contents BLOB
45 );
46 CREATE TABLE IF NOT EXISTS directory_entries (
47 parent_path TEXT,
48 name TEXT,
49 PRIMARY KEY (parent_path, name)
50 );
51 '
52 );
53
54 // Create root directory if it doesn't exist.
55 $stmt = $this->db->prepare( 'INSERT OR IGNORE INTO files (path, type) VALUES (?, ?)' );
56 $stmt->bindValue( 1, '/', SQLITE3_TEXT );
57 $stmt->bindValue( 2, 'dir', SQLITE3_TEXT );
58 $stmt->execute();
59 }
60
61 public function ls( $path = '/' ) {
62 $stmt = $this->db->prepare(
63 '
64 SELECT name FROM directory_entries
65 WHERE parent_path = ?
66 '
67 );
68 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
69 $result = $stmt->execute();
70
71 $entries = array();
72 while ( $row = $result->fetchArray( SQLITE3_ASSOC ) ) {
73 $entries[] = $row['name'];
74 }
75
76 return $entries;
77 }
78
79 public function is_dir( $path ) {
80 $stmt = $this->db->prepare(
81 '
82 SELECT type FROM files
83 WHERE path = ? AND type = ?
84 '
85 );
86 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
87 $stmt->bindValue( 2, 'dir', SQLITE3_TEXT );
88 $result = $stmt->execute();
89
90 return false !== $result->fetchArray();
91 }
92
93 public function is_file( $path ) {
94 $stmt = $this->db->prepare(
95 '
96 SELECT type FROM files
97 WHERE path = ? AND type = ?
98 '
99 );
100 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
101 $stmt->bindValue( 2, 'file', SQLITE3_TEXT );
102 $result = $stmt->execute();
103
104 return false !== $result->fetchArray();
105 }
106
107 public function exists( $path ) {
108 $stmt = $this->db->prepare(
109 '
110 SELECT 1 FROM files
111 WHERE path = ?
112 '
113 );
114 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
115 $result = $stmt->execute();
116
117 return false !== $result->fetchArray();
118 }
119
120 public function open_read_stream( $path ): ByteReadStream {
121 return new MemoryPipe( $this->get_contents( $path ) );
122 }
123
124 public function get_contents( $path ) {
125 if ( ! $this->is_file( $path ) ) {
126 throw new FilesystemException(
127 sprintf( 'File not found: %s', $path )
128 );
129 }
130 $stmt = $this->db->prepare( 'SELECT contents FROM files WHERE path = ?' );
131 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
132 $result = $stmt->execute();
133 $row = $result->fetchArray( SQLITE3_ASSOC );
134
135 return $row['contents'];
136 }
137
138 public function rename( $from_path, $to_path, $options = array() ) {
139 if ( ! $this->exists( $from_path ) ) {
140 throw new FilesystemException(
141 sprintf( 'File not found: %s', $from_path )
142 );
143 }
144
145 $parent = wp_unix_dirname( $to_path );
146 if ( ! $this->is_dir( $parent ) ) {
147 throw new FilesystemException(
148 sprintf( 'Parent directory not found: %s', $parent )
149 );
150 }
151
152 try {
153 $this->in_transaction(
154 function () use ( $from_path, $to_path ) {
155 // Update the file path.
156 $stmt = $this->db->prepare( 'UPDATE files SET path = ? WHERE path = ?' );
157 $stmt->bindValue( 1, $to_path, SQLITE3_TEXT );
158 $stmt->bindValue( 2, $from_path, SQLITE3_TEXT );
159 $stmt->execute();
160
161 // Update directory entries.
162 $old_parent = wp_unix_dirname( $from_path );
163 $parent = wp_unix_dirname( $to_path );
164
165 $stmt = $this->db->prepare(
166 '
167 DELETE FROM directory_entries
168 WHERE parent_path = ? AND name = ?
169 '
170 );
171 $stmt->bindValue( 1, $old_parent, SQLITE3_TEXT );
172 $stmt->bindValue( 2, basename( $from_path ), SQLITE3_TEXT );
173 $stmt->execute();
174
175 $stmt = $this->db->prepare(
176 '
177 INSERT INTO directory_entries (parent_path, name)
178 VALUES (?, ?)
179 '
180 );
181 $stmt->bindValue( 1, $parent, SQLITE3_TEXT );
182 $stmt->bindValue( 2, basename( $to_path ), SQLITE3_TEXT );
183 $stmt->execute();
184 }
185 );
186
187 return true;
188 } catch ( Exception $e ) {
189 throw new FilesystemException(
190 sprintf( 'Failed to rename file: %s to %s', $from_path, $to_path ),
191 0,
192 $e
193 );
194 }
195 }
196
197 public function mkdir_single( $path, $options = array() ) {
198 if ( $this->exists( $path ) ) {
199 throw new FilesystemException(
200 sprintf( 'Directory already exists: %s', $path )
201 );
202 }
203
204 $parent = wp_unix_dirname( $path );
205 if ( ! $this->is_dir( $parent ) ) {
206 throw new FilesystemException(
207 sprintf( 'Parent directory not found: %s', $parent )
208 );
209 }
210
211 try {
212 $this->in_transaction(
213 function () use ( $path ) {
214 $parent = wp_unix_dirname( $path );
215
216 $stmt = $this->db->prepare(
217 '
218 INSERT INTO files (path, type)
219 VALUES (?, ?)
220 '
221 );
222 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
223 $stmt->bindValue( 2, 'dir', SQLITE3_TEXT );
224 $stmt->execute();
225
226 $stmt = $this->db->prepare(
227 '
228 INSERT INTO directory_entries (parent_path, name)
229 VALUES (?, ?)
230 '
231 );
232 $stmt->bindValue( 1, $parent, SQLITE3_TEXT );
233 $stmt->bindValue( 2, basename( $path ), SQLITE3_TEXT );
234 $stmt->execute();
235 }
236 );
237
238 return true;
239 } catch ( Exception $e ) {
240 throw new FilesystemException(
241 sprintf( 'Failed to create directory: %s', $path ),
242 0,
243 $e
244 );
245 }
246 }
247
248 public function rm( $path ) {
249 if ( ! $this->is_file( $path ) ) {
250 throw new FilesystemException(
251 sprintf( 'File not found: %s', $path )
252 );
253 }
254
255 try {
256 $this->in_transaction(
257 function () use ( $path ) {
258 $parent = wp_unix_dirname( $path );
259
260 $stmt = $this->db->prepare( 'DELETE FROM files WHERE path = ?' );
261 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
262 $stmt->execute();
263
264 $stmt = $this->db->prepare(
265 '
266 DELETE FROM directory_entries
267 WHERE parent_path = ? AND name = ?
268 '
269 );
270 $stmt->bindValue( 1, $parent, SQLITE3_TEXT );
271 $stmt->bindValue( 2, basename( $path ), SQLITE3_TEXT );
272 $stmt->execute();
273 }
274 );
275 } catch ( Exception $e ) {
276 throw new FilesystemException(
277 sprintf( 'Failed to remove file: %s', $path ),
278 0,
279 $e
280 );
281 }
282 }
283
284 public function rmdir( $path, $options = array() ) {
285 $recursive = $options['recursive'] ?? false;
286 if ( ! $this->is_dir( $path ) ) {
287 throw new FilesystemException(
288 sprintf( 'Directory not found: %s', $path )
289 );
290 }
291
292 try {
293 $this->in_transaction(
294 function () use ( $path, $options, $recursive ) {
295 if ( $recursive ) {
296 $path = rtrim( $path, '/' );
297 foreach ( $this->ls( $path ) as $child ) {
298 $child_path = wp_join_unix_paths( $path, $child );
299 if ( $this->is_dir( $child_path ) ) {
300 $this->rmdir( $child_path, $options );
301 } else {
302 $this->rm( $child_path );
303 }
304 }
305 }
306
307 $parent = wp_unix_dirname( $path );
308
309 $stmt = $this->db->prepare( 'DELETE FROM files WHERE path = ?' );
310 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
311 $stmt->execute();
312
313 $stmt = $this->db->prepare(
314 '
315 DELETE FROM directory_entries
316 WHERE parent_path = ? AND name = ?
317 '
318 );
319 $stmt->bindValue( 1, $parent, SQLITE3_TEXT );
320 $stmt->bindValue( 2, basename( $path ), SQLITE3_TEXT );
321 $stmt->execute();
322 }
323 );
324 } catch ( Exception $e ) {
325 throw new FilesystemException(
326 sprintf( 'Failed to remove directory: %s', $path ),
327 0,
328 $e
329 );
330 }
331 }
332
333 public function put_contents( $path, $data, $options = array() ) {
334 $parent = wp_unix_dirname( $path );
335 if ( ! $this->is_dir( $parent ) ) {
336 throw new FilesystemException(
337 sprintf( 'Parent directory not found: %s', $parent )
338 );
339 }
340
341 try {
342 $this->in_transaction(
343 function () use ( $path, $data ) {
344 $parent = wp_unix_dirname( $path );
345
346 $stmt = $this->db->prepare(
347 '
348 INSERT OR REPLACE INTO files (path, type, contents)
349 VALUES (?, ?, ?)
350 '
351 );
352 $stmt->bindValue( 1, $path, SQLITE3_TEXT );
353 $stmt->bindValue( 2, 'file', SQLITE3_TEXT );
354 $stmt->bindValue( 3, $data, SQLITE3_BLOB );
355 $stmt->execute();
356
357 $stmt = $this->db->prepare(
358 '
359 INSERT OR REPLACE INTO directory_entries (parent_path, name)
360 VALUES (?, ?)
361 '
362 );
363 $stmt->bindValue( 1, $parent, SQLITE3_TEXT );
364 $stmt->bindValue( 2, basename( $path ), SQLITE3_TEXT );
365 $stmt->execute();
366 }
367 );
368
369 return true;
370 } catch ( Exception $e ) {
371 throw new FilesystemException(
372 sprintf( 'Failed to put contents: %s', $path ),
373 0,
374 $e
375 );
376 }
377 }
378
379 private function in_transaction( $callback ) {
380 $current_level = $this->transaction_level++;
381 try {
382 if ( 0 === $current_level ) {
383 $this->db->exec( 'BEGIN TRANSACTION' );
384 try {
385 $callback();
386 $this->db->exec( 'COMMIT' );
387 } catch ( Exception $e ) {
388 $this->db->exec( 'ROLLBACK' );
389 throw $e;
390 }
391 } else {
392 $this->db->exec( 'SAVEPOINT level_' . $current_level );
393 try {
394 $callback();
395 $this->db->exec( 'RELEASE SAVEPOINT level_' . $current_level );
396 } catch ( Exception $e ) {
397 $this->db->exec( 'ROLLBACK TO SAVEPOINT level_' . $current_level );
398 throw $e;
399 }
400 }
401 } finally {
402 --$this->transaction_level;
403 }
404 }
405
406 public function get_meta(): array {
407 return array();
408 }
409 }
410