PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.5.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.5.0
4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Framework / Filesystem / DiskWriteCheck.php
wp-staging / Framework / Filesystem Last commit date
Filters 6 months ago Scanning 5 years ago AbstractFileObject.php 1 year ago AbstractFilesystemScanner.php 1 year ago DebugLogReader.php 2 years ago DirectoryListing.php 5 months ago DiskWriteCheck.php 5 months ago FileObject.php 1 year ago Filesystem.php 6 months ago FilesystemExceptions.php 5 years ago FilesystemScanner.php 6 months ago FilesystemScannerDto.php 1 year ago FilterableDirectoryIterator.php 1 year ago LogCleanup.php 5 months ago LogFiles.php 1 year ago MissingFileException.php 3 years ago OPcache.php 5 months ago PartIdentifier.php 8 months ago PathChecker.php 2 years ago PathIdentifier.php 6 months ago Permissions.php 5 months ago WpUploadsFolderSymlinker.php 4 years ago
DiskWriteCheck.php
150 lines
1 <?php
2
3 namespace WPStaging\Framework\Filesystem;
4
5 use WPStaging\Framework\Adapter\Directory;
6 use WPStaging\Framework\Facades\Hooks;
7 use WPStaging\Framework\Job\Exception\DiskNotWritableException;
8
9 class DiskWriteCheck
10 {
11 /** @var string */
12 const OPTION_DISK_WRITABLE_FAILED = 'wpstg_disk_writable_check_failed';
13
14 /** @var string */
15 const FILTER_FILESYSTEM_DISABLED_DISK_FREE_SPACE_CHECK = 'wpstg.filesystem.disableDiskFreeSpaceCheck';
16
17 protected $directory;
18
19 protected $filesystem;
20
21 protected $reservedMemory;
22
23 public function __construct(Filesystem $filesystem, Directory $directory)
24 {
25 $this->directory = $directory;
26 $this->filesystem = $filesystem;
27 // 1kb
28 $this->reservedMemory = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
29 }
30
31 /**
32 * @param string $path An absolute path to check for free disk space.
33 * @param int|float $bytesToStore The number of bytes intended to be written.
34 *
35 * @throws \RuntimeException When something happened that prevented us from checking if there's enough free disk space.
36 * @throws DiskNotWritableException When disk_free_space reports there's not enough disk space to store this amount of bytes.
37 */
38 public function checkPathCanStoreEnoughBytes($path, $bytesToStore)
39 {
40 // Early bail: Disabled by filter
41 if (Hooks::applyFilters(self::FILTER_FILESYSTEM_DISABLED_DISK_FREE_SPACE_CHECK, false)) {
42 throw new \RuntimeException();
43 }
44
45 // Early bail: disk_free_space might have been disabled using php.ini "disable_functions"
46 if (!function_exists('disk_free_space')) {
47 throw new \RuntimeException('The disk_free_space function is not available.');
48 }
49
50 $path = untrailingslashit($path);
51
52 clearstatcache();
53 if (!file_exists($path)) {
54 throw new \RuntimeException('The given path does not exist.');
55 }
56
57 if (is_link($path)) {
58 throw new \RuntimeException('The given path must be a directory.');
59 }
60
61 if (!is_dir($path)) {
62 throw new \RuntimeException('The path must be a directory.');
63 }
64
65 $freeSpaceInBytes = @disk_free_space($path);
66
67 if ($freeSpaceInBytes === false) {
68 $message = '';
69 $error = error_get_last();
70
71 if (is_array($error) && array_key_exists('message', $error)) {
72 $message = $error['message'];
73 }
74
75 throw new \RuntimeException($message);
76 }
77
78 if (!is_numeric($freeSpaceInBytes)) {
79 throw new \RuntimeException('disk_free_space returned an unexpected result');
80 }
81
82 if ($freeSpaceInBytes - $bytesToStore < 0) {
83 throw DiskNotWritableException::willExceedFreeDiskSpace(abs($freeSpaceInBytes - $bytesToStore));
84 }
85 }
86
87 /**
88 * @throws DiskNotWritableException If a previous disk write test has failed.
89 */
90 public function hasDiskWriteTestFailed()
91 {
92 if (get_option(self::OPTION_DISK_WRITABLE_FAILED) === 'fail') {
93 throw DiskNotWritableException::diskNotWritable();
94 }
95 }
96
97 /**
98 * @return bool
99 * @throws DiskNotWritableException
100 * @throws FilesystemExceptions
101 */
102 public function testDiskIsWriteable()
103 {
104 $destination = $this->directory->getPluginUploadsDirectory() . '.wpstgDiskWriteCheck';
105
106 if (file_exists($destination)) {
107 unlink($destination);
108 }
109
110 // Early bail: Disk writeable check pass
111 if (@file_put_contents($destination, $this->reservedMemory)) {
112 unlink($destination);
113
114 delete_option(self::OPTION_DISK_WRITABLE_FAILED);
115
116 return true;
117 }
118
119 // First try, this might fail as the disk is full.
120 $result = $this->setLowLevelDiskFullFlag();
121
122 $this->filesystem->delete($this->directory->getCacheDirectory());
123
124 // Second try, this might succeed if the first failed as we freed up a few kb of data.
125 if (!$result) {
126 $result = $this->setLowLevelDiskFullFlag();
127 }
128
129 $this->filesystem->delete($this->directory->getTmpDirectory());
130
131 // Third try, this should succeed if the second failed, but it's the tmp directory can be very big and the request might timeout before getting here.
132 if (!$result) {
133 $result = $this->setLowLevelDiskFullFlag();
134
135 if (!$result) {
136 \WPStaging\functions\debug_log('WP STAGING DiskWriteCheck failed and could not update the option in the database.');
137 }
138 }
139
140 throw DiskNotWritableException::diskNotWritable();
141 }
142
143 protected function setLowLevelDiskFullFlag()
144 {
145 global $wpdb;
146
147 return $wpdb->query($wpdb->prepare("INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE `option_name` = VALUES(`option_name`), `option_value` = VALUES(`option_value`), `autoload` = VALUES(`autoload`)", self::OPTION_DISK_WRITABLE_FAILED, 'fail', 'no'));
148 }
149 }
150