PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 3.1.18.8
JetBackup – Backup, Restore & Migrate v3.1.18.8
3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / Archive / Gzip.php
backup / src / JetBackup / Archive Last commit date
Data 5 months ago File 5 months ago Header 5 months ago Scan 5 months ago .htaccess 5 months ago Archive.php 5 months ago Gzip.php 5 months ago index.html 5 months ago web.config 5 months ago
Gzip.php
195 lines
1 <?php
2
3 namespace JetBackup\Archive;
4
5 use JetBackup\DirIterator\DirIteratorFile;
6 use JetBackup\Exception\GzipException;
7
8 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
9
10 /**
11 * Class for compressing files using gzip.
12 */
13 class Gzip {
14
15 const MAX_RETRIES = 3;
16 const RETRY_DELAY_MS = 100;
17 const DEFAULT_COMPRESS_CHUNK_SIZE = 10485760; // 10MB
18 const DEFAULT_DECOMPRESS_CHUNK_SIZE = 1048576; // 1MB
19 const DEFAULT_COMPRESSION_LEVEL = -1;
20
21 private function __construct() {}
22
23 /**
24 * @throws GzipException
25 */
26 private static function _getInfo($file) {
27 $info = new \stdClass();
28 if(file_exists($file)) {
29 $info = json_decode(file_get_contents($file));
30 if($info === false) throw new GzipException("Failed fetching compress information");
31 }
32 return $info;
33 }
34
35 /**
36 * @param $file
37 * @param $data
38 *
39 * @return void
40 * @throws GzipException
41 */
42 private static function _putInfo($file, $data)
43 {
44 $tempFile = $file . '.tmp';
45
46 $jsonData = json_encode($data);
47 if ($jsonData === false) throw new GzipException("Failed to encode compress information: " . json_last_error_msg());
48
49 $maxRetries = self::MAX_RETRIES;
50 $retryDelayMs = self::RETRY_DELAY_MS;
51 $lastErrorWrite = null;
52 $bytesWritten = false;
53
54 for ($i = 0; $i < $maxRetries; $i++) {
55
56 $lastErrorWrite = null;
57
58 set_error_handler(function ($severity, $message, $errFile, $errLine) use (&$lastErrorWrite) {
59 $lastErrorWrite = $message . " in {$errFile}:{$errLine}";
60 return true;
61 });
62
63 $bytesWritten = @file_put_contents($tempFile, $jsonData);
64 restore_error_handler();
65
66 if ($bytesWritten !== false) break;
67 usleep($retryDelayMs * 1000); // retry
68 }
69
70 if ($bytesWritten === false) {
71 $extra = $lastErrorWrite ? " ({$lastErrorWrite})" : '';
72 throw new GzipException("Failed to write compress information to temporary file '{$tempFile}' after {$maxRetries} attempts{$extra}");
73 }
74
75 $lastErrorRename = null;
76
77 for ($i = 0; $i < $maxRetries; $i++) {
78
79 $lastErrorRename = null;
80
81 set_error_handler(function ($severity, $message, $errFile, $errLine) use (&$lastErrorRename) {
82 $lastErrorRename = $message . " in {$errFile}:{$errLine}";
83 return true;
84 });
85
86 $renamed = @rename($tempFile, $file);
87 restore_error_handler();
88
89 if ($renamed === true) return;
90 usleep($retryDelayMs * 1000);
91 }
92
93 $extra = $lastErrorRename ? " ({$lastErrorRename})" : '';
94 throw new GzipException("Failed to atomically write compress information to '{$file}' after {$maxRetries} attempts{$extra}");
95 }
96
97
98
99 /**
100 * @throws GzipException
101 */
102 public static function compress($file, $chunkSize=self::DEFAULT_COMPRESS_CHUNK_SIZE, $compressionLevel=self::DEFAULT_COMPRESSION_LEVEL, ?callable $callback=null) {
103 if(!file_exists($file) || !is_file($file)) throw new GzipException("Source file not found");
104
105 $target = $file . '.gz';
106
107 $info_file = $target . '.compress.info';
108 $info = self::_getInfo($info_file);
109
110 $fd = fopen($file, 'r');
111 $gzfd = fopen($target, 'ab');
112
113 $read = $write = 0;
114 if(isset($info->fdpos) && $info->fdpos) {
115 $read = $info->fdpos;
116 fseek($fd, $info->fdpos);
117 }
118 if(isset($info->fdgzpos) && $info->fdgzpos) {
119 $write = $info->fdgzpos;
120 fseek($gzfd, $info->fdgzpos);
121 }
122
123 while(!feof($fd)) {
124 $write += fwrite($gzfd, gzencode(fread($fd, $chunkSize), $compressionLevel));
125 $read += $chunkSize;
126
127 self::_putInfo($info_file, [
128 'fdpos' => $read,
129 'fdgzpos' => $write,
130 ]);
131
132 if($callback) $callback($read, DirIteratorFile::safe_filesize($file));
133 }
134
135 if(feof($fd)) {
136 @unlink($file);
137 @unlink($info_file);
138 }
139
140 fclose($fd);
141 fclose($gzfd);
142 }
143
144 /**
145 * @throws GzipException
146 */
147 public static function decompress($file, ?callable $callback=null, $chunkSize=self::DEFAULT_DECOMPRESS_CHUNK_SIZE) {
148 if(!file_exists($file) || !is_file($file)) throw new GzipException("Source file not found");
149
150 $info_file = $file . '.decompress.info';
151 $info = self::_getInfo($info_file);
152
153 // remove .gz suffix
154 $target = substr($file, 0, -3);
155
156 $fd = fopen($target, 'a');
157 $gzfd = gzopen($file, 'rb');
158
159 $read = $write = 0;
160 if(isset($info->fdpos) && $info->fdpos) {
161 $write = $info->fdpos;
162 fseek($fd, $info->fdpos);
163 }
164
165 if(isset($info->fdgzpos) && $info->fdgzpos) {
166 $read = $info->fdgzpos;
167 gzseek($gzfd, $info->fdgzpos);
168 }
169
170 while(!feof($gzfd)) {
171 $write += fwrite($fd, gzread($gzfd, $chunkSize));
172 $read += $chunkSize;
173
174 self::_putInfo($info_file, [
175 'fdpos' => $write,
176 'fdgzpos' => $read,
177 ]);
178
179 if($callback) $callback(
180 'Gzip',
181 'Decompressing',
182 (int) (DirIteratorFile::safe_filesize($file) * 3), // estimating X3 compression ratio, just for percentage calc
183 $read)
184 ;
185 }
186
187 if(feof($gzfd)) {
188 @unlink($file);
189 @unlink($info_file);
190 }
191
192 fclose($fd);
193 gzclose($gzfd);
194 }
195 }