Subversion Repositories php_utils

Rev

Rev 78 | Rev 80 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 78 Rev 79
1
<?php
1
<?php
2
 
2
 
3
/*
3
/*
4
 * PHP git functions
4
 * PHP git functions
5
 * Copyright 2021 - 2023 Daniel Marschall, ViaThinkSoft
5
 * Copyright 2021 - 2023 Daniel Marschall, ViaThinkSoft
6
 * Revision 2023-04-09
6
 * Revision 2023-04-09
7
 *
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
10
 * You may obtain a copy of the License at
11
 *
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
18
 * limitations under the License.
19
 */
19
 */
20
 
20
 
21
function git_get_latest_commit_message($git_dir) {
21
function git_get_latest_commit_message($git_dir) {
22
        // First try an official git client
22
        // First try an official git client
23
        $cmd = "git --git-dir=".escapeshellarg("$git_dir")." log -1 2>&1";
23
        $cmd = "git --git-dir=".escapeshellarg("$git_dir")." log -1 2>&1";
24
        $ec = -1;
24
        $ec = -1;
25
        $out = array();
25
        $out = array();
26
        @exec($cmd, $out, $ec);
26
        @exec($cmd, $out, $ec);
27
        $out = implode("\n",$out);
27
        $out = implode("\n",$out);
28
        if (($ec == 0) && ($out != '')) return $out;
28
        if (($ec == 0) && ($out != '')) return $out;
29
 
29
 
30
        // If that failed, try to decode the binary files outselves
30
        // If that failed, try to decode the binary files outselves
31
        $cont = @file_get_contents($git_dir.'/HEAD');
31
        $cont = @file_get_contents($git_dir.'/HEAD');
32
        if (preg_match('@ref: (.+)[\r\n]@', "$cont\n", $m) && file_exists($git_dir.'/'.$m[1])) {
32
        if (preg_match('@ref: (.+)[\r\n]@', "$cont\n", $m) && file_exists($git_dir.'/'.$m[1])) {
33
                // Example content of a .git folder file:
33
                // Example content of a .git folder file:
34
                // 091a5fa6b157be035e88f5d24aa329ba44d20d63
34
                // 091a5fa6b157be035e88f5d24aa329ba44d20d63
35
                // Not available
35
                // Not available
36
                $commit_object = trim(file_get_contents($git_dir.'/'.$m[1]));
36
                $commit_object = trim(file_get_contents($git_dir.'/'.$m[1]));
37
        } else if (file_exists($git_dir.'/refs/heads/master')) {
37
        } else if (file_exists($git_dir.'/refs/heads/master')) {
38
                // Missing at Plesk Git initial checkout, but available on update.
38
                // Missing at Plesk Git initial checkout, but available on update.
39
                $commit_object = trim(file_get_contents($git_dir.'/refs/heads/master'));
39
                $commit_object = trim(file_get_contents($git_dir.'/refs/heads/master'));
40
        } else if (file_exists($git_dir.'/FETCH_HEAD')) {
40
        } else if (file_exists($git_dir.'/FETCH_HEAD')) {
41
                // Example content of a Plesk Git folder (fresh):
41
                // Example content of a Plesk Git folder (fresh):
42
                // 091a5fa6b157be035e88f5d24aa329ba44d20d63     not-for-merge   branch 'master' of https://github.com/danielmarschall/oidplus
42
                // 091a5fa6b157be035e88f5d24aa329ba44d20d63     not-for-merge   branch 'master' of https://github.com/danielmarschall/oidplus
43
                // 091a5fa6b157be035e88f5d24aa329ba44d20d63     not-for-merge   remote-tracking branch 'origin/trunk' of https://github.com/danielmarschall/oidplus
43
                // 091a5fa6b157be035e88f5d24aa329ba44d20d63     not-for-merge   remote-tracking branch 'origin/trunk' of https://github.com/danielmarschall/oidplus
44
                $cont = file_get_contents($git_dir.'/FETCH_HEAD');
44
                $cont = file_get_contents($git_dir.'/FETCH_HEAD');
45
                $commit_object = substr(trim($cont),0,40);
45
                $commit_object = substr(trim($cont),0,40);
46
        } else {
46
        } else {
47
                throw new Exception("Cannot detect last commit object");
47
                throw new Exception("Cannot detect last commit object");
48
        }
48
        }
49
 
49
 
50
        $objects_dir = $git_dir . '/objects';
50
        $objects_dir = $git_dir . '/objects';
51
 
51
 
52
 
52
 
53
        // Sometimes, objects are uncompressed, sometimes compressed in a pack file
53
        // Sometimes, objects are uncompressed, sometimes compressed in a pack file
54
        // Plesk initial checkout is compressed, but pulls via web interface
54
        // Plesk initial checkout is compressed, but pulls via web interface
55
        // save uncompressed files
55
        // save uncompressed files
56
 
56
 
57
        $uncompressed_file = $objects_dir . '/' . substr($commit_object,0,2) . '/' . substr($commit_object,2);
57
        $uncompressed_file = $objects_dir . '/' . substr($commit_object,0,2) . '/' . substr($commit_object,2);
58
        if (file_exists($uncompressed_file)) {
58
        if (file_exists($uncompressed_file)) {
59
                // Read compressed data
59
                // Read compressed data
60
                $compressed = file_get_contents($uncompressed_file);
60
                $compressed = file_get_contents($uncompressed_file);
61
 
61
 
62
                // Uncompress
62
                // Uncompress
63
                $uncompressed = @gzuncompress($compressed);
63
                $uncompressed = @gzuncompress($compressed);
64
                if ($uncompressed === false) throw new Exception("Decompression failed");
64
                if ($uncompressed === false) throw new Exception("Decompression failed");
65
 
65
 
66
                // The format is "commit <nnn>\0<Message>" where <nnn> is only a 3 digit number?!
66
                // The format is "commit <nnn>\0<Message>" where <nnn> is only a 3 digit number?!
67
                $ary = explode(chr(0), $uncompressed);
67
                $ary = explode(chr(0), $uncompressed);
68
                $uncompressed = array_pop($ary);
68
                $uncompressed = array_pop($ary);
69
 
69
 
70
                return $uncompressed;
70
                return $uncompressed;
71
        } else {
71
        } else {
72
                $pack_files = @glob($objects_dir.'/pack/pack-*.pack');
72
                $pack_files = @glob($objects_dir.'/pack/pack-*.pack');
73
                $last_exception = 'No pack files found';
73
                $last_exception = 'No pack files found';
74
                if ($pack_files) foreach ($pack_files as $basename) {
74
                if ($pack_files) foreach ($pack_files as $basename) {
75
                        $basename = substr(basename($basename),0,strlen(basename($basename))-5);
75
                        $basename = substr(basename($basename),0,strlen(basename($basename))-5);
76
                        try {
76
                        try {
-
 
77
                                if (class_exists('ViaThinkSoft\Glip\Git')) {
-
 
78
                                        // https://github.com/danielmarschall/glip
-
 
79
                                        // composer require danielmarschall/glip
-
 
80
                                        $git = new Git($git_dir);
-
 
81
                                        $obj = $git->getObject(hex2bin($commit_object));
-
 
82
                                        echo $obj->detail;
-
 
83
                                } else {
-
 
84
                                        // Own implementation (cannot read delta objects yet)
77
                                return git_read_object($commit_object,
85
                                        return git_read_object($commit_object,
78
                                        $objects_dir.'/pack/'.$basename.'.idx',
86
                                                $objects_dir.'/pack/'.$basename.'.idx',
79
                                        $objects_dir.'/pack/'.$basename.'.pack'
87
                                                $objects_dir.'/pack/'.$basename.'.pack'
80
                                );
88
                                        );
-
 
89
                                }
81
                        } catch (Exception $e) {
90
                        } catch (Exception $e) {
82
                                $last_exception = $e;
91
                                $last_exception = $e;
83
                        }
92
                        }
84
                }
93
                }
85
                throw new Exception($last_exception);
94
                throw new Exception($last_exception);
86
        }
95
        }
87
}
96
}
88
 
97
 
89
function git_read_object($object_wanted, $idx_file, $pack_file, $debug=false) {
98
function git_read_object($object_wanted, $idx_file, $pack_file, $debug=false) {
90
        // More info about the IDX and PACK format: https://git-scm.com/docs/pack-format
99
        // More info about the IDX and PACK format: https://git-scm.com/docs/pack-format
91
 
100
 
92
        // Do some checks
101
        // Do some checks
93
        if (!preg_match('/^[0-9a-fA-F]{40}$/', $object_wanted, $m)) throw new Exception("Is not a valid object: $object_wanted");
102
        if (!preg_match('/^[0-9a-fA-F]{40}$/', $object_wanted, $m)) throw new Exception("Is not a valid object: $object_wanted");
94
        if (!file_exists($idx_file)) throw new Exception("Idx file $idx_file not found");
103
        if (!file_exists($idx_file)) throw new Exception("Idx file $idx_file not found");
95
        if (!file_exists($pack_file)) throw new Exception("Pack file $pack_file not found");
104
        if (!file_exists($pack_file)) throw new Exception("Pack file $pack_file not found");
96
 
105
 
97
        // Open index file
106
        // Open index file
98
        $fp = fopen($idx_file, 'rb');
107
        $fp = fopen($idx_file, 'rb');
99
        if (!$fp) throw new Exception("Cannot open index file $idx_file");
108
        if (!$fp) throw new Exception("Cannot open index file $idx_file");
100
 
109
 
101
        // Read version
110
        // Read version
102
        fseek($fp, 0);
111
        fseek($fp, 0);
103
        $unpacked = unpack('H8', fread($fp, 4)); // H8 = 8x "Hex string, high nibble first"
112
        $unpacked = unpack('H8', fread($fp, 4)); // H8 = 8x "Hex string, high nibble first"
104
        if ($unpacked[1] === bin2hex("\377tOc")) {
113
        if ($unpacked[1] === bin2hex("\377tOc")) {
105
                $version = unpack('N', fread($fp, 4))[1]; // N = "unsigned long (always 32 bit, big endian byte order)"
114
                $version = unpack('N', fread($fp, 4))[1]; // N = "unsigned long (always 32 bit, big endian byte order)"
106
                $fanout_offset = 8;
115
                $fanout_offset = 8;
107
                if ($version != 2) throw new Exception("Version $version unknown");
116
                if ($version != 2) throw new Exception("Version $version unknown");
108
        } else {
117
        } else {
109
                $version = 1;
118
                $version = 1;
110
                $fanout_offset = 0;
119
                $fanout_offset = 0;
111
        }
120
        }
112
        if ($debug) echo "Index file version = $version\n";
121
        if ($debug) echo "Index file version = $version\n";
113
 
122
 
114
        // Read fanout table
123
        // Read fanout table
115
        fseek($fp, $fanout_offset);
124
        fseek($fp, $fanout_offset);
116
        $fanout_ary[0] = 0;
125
        $fanout_ary[0] = 0;
117
        $fanout_ary = unpack('N*', fread($fp, 4*256));
126
        $fanout_ary = unpack('N*', fread($fp, 4*256));
118
        $num_objects = $fanout_ary[256];
127
        $num_objects = $fanout_ary[256];
119
 
128
 
120
        // Find out approximate object number (from fanout table)
129
        // Find out approximate object number (from fanout table)
121
        $fanout_index = hexdec(substr($object_wanted,0,2));
130
        $fanout_index = hexdec(substr($object_wanted,0,2));
122
        if ($debug) echo "Fanout index = ".($fanout_index-1)."\n";
131
        if ($debug) echo "Fanout index = ".($fanout_index-1)."\n";
123
        $object_no = $fanout_ary[$fanout_index]; // approximate
132
        $object_no = $fanout_ary[$fanout_index]; // approximate
124
        if ($debug) echo "Object no approx $object_no\n";
133
        if ($debug) echo "Object no approx $object_no\n";
125
 
134
 
126
        // Find the exact object number
135
        // Find the exact object number
127
        fseek($fp, $fanout_offset + 4*256 + 20*$object_no);
136
        fseek($fp, $fanout_offset + 4*256 + 20*$object_no);
128
        $object_no--;
137
        $object_no--;
129
        $pack_offset = -1; // avoid that phpstan complains
138
        $pack_offset = -1; // avoid that phpstan complains
130
        do {
139
        do {
131
                $object_no++;
140
                $object_no++;
132
                if ($version == 1) {
141
                if ($version == 1) {
133
                        $pack_offset = fread($fp, 4);
142
                        $pack_offset = fread($fp, 4);
134
                }
143
                }
135
                $binary = fread($fp, 20);
144
                $binary = fread($fp, 20);
136
                if (substr(bin2hex($binary),0,2) != substr(strtolower($object_wanted),0,2)) {
145
                if (substr(bin2hex($binary),0,2) != substr(strtolower($object_wanted),0,2)) {
137
                        throw new Exception("Object $object_wanted not found");
146
                        throw new Exception("Object $object_wanted not found");
138
                }
147
                }
139
        } while (bin2hex($binary) != strtolower($object_wanted));
148
        } while (bin2hex($binary) != strtolower($object_wanted));
140
        if ($debug) echo "Exact object no = $object_no\n";
149
        if ($debug) echo "Exact object no = $object_no\n";
141
 
150
 
142
        if ($version == 2) {
151
        if ($version == 2) {
143
                // Get CRC32
152
                // Get CRC32
144
                // TODO: is this correct? or do we need to read 'H8' ?
-
 
145
                fseek($fp, $fanout_offset + 4*256 + 20*$num_objects + 4*$object_no);
153
                fseek($fp, $fanout_offset + 4*256 + 20*$num_objects + 4*$object_no);
146
                $crc32 = unpack('N', fread($fp,4))[1];
154
                $crc32 = unpack('H8', fread($fp,4))[1];
147
                if ($debug) echo "CRC32 = ".sprintf('0x%08x',$crc32)."\n";
155
                if ($debug) echo "CRC32 = ".$crc32."\n";
148
 
156
 
149
                // Get offset (32 bit)
157
                // Get offset (32 bit)
150
                fseek($fp, $fanout_offset + 4*256 + 20*$num_objects + 4*$num_objects + 4*$object_no);
158
                fseek($fp, $fanout_offset + 4*256 + 20*$num_objects + 4*$num_objects + 4*$object_no);
151
                $offset_info = unpack('N', fread($fp,4))[1];
159
                $offset_info = unpack('N', fread($fp,4))[1];
152
                if ($offset_info >= 0x80000000) {
160
                if ($offset_info >= 0x80000000) {
153
                        // MSB set, so the offset is 64 bit
161
                        // MSB set, so the offset is 64 bit
154
                        if ($debug) echo "64 bit pack offset\n";
162
                        if ($debug) echo "64 bit pack offset\n";
155
                        $offset_info &= 0x7FFFFFFF;
163
                        $offset_info &= 0x7FFFFFFF;
156
                        fseek($fp, $fanout_offset + 4*256 + 20*$num_objects + 4*$num_objects + 4*$num_objects + 8*$offset_info);
164
                        fseek($fp, $fanout_offset + 4*256 + 20*$num_objects + 4*$num_objects + 4*$num_objects + 8*$offset_info);
157
                        $pack_offset = unpack('J', fread($fp,8))[1];
165
                        $pack_offset = unpack('J', fread($fp,8))[1];
158
                } else {
166
                } else {
159
                        // MSB is not set, so the offset is 32 bit
167
                        // MSB is not set, so the offset is 32 bit
160
                        if ($debug) echo "32 bit pack offset\n";
168
                        if ($debug) echo "32 bit pack offset\n";
161
                        $offset_info &= 0x7FFFFFFF;
169
                        $offset_info &= 0x7FFFFFFF;
162
                        $pack_offset = $offset_info;
170
                        $pack_offset = $offset_info;
163
                }
171
                }
164
        }
172
        }
165
 
173
 
166
        if ($debug) echo "Pack file offset = ".sprintf('0x%x',$pack_offset)."\n";
174
        if ($debug) echo "Pack file offset = ".sprintf('0x%x',$pack_offset)."\n";
167
 
175
 
168
        // Close index file
176
        // Close index file
169
        fclose($fp);
177
        fclose($fp);
170
 
178
 
171
        // Open pack file
179
        // Open pack file
172
        $fp = fopen($pack_file, 'rb');
180
        $fp = fopen($pack_file, 'rb');
173
        if (!$fp) throw new Exception("Cannot open pack file $pack_file");
181
        if (!$fp) throw new Exception("Cannot open pack file $pack_file");
174
 
182
 
175
        // Find out type
183
        // Read type and first part of the size
176
        fseek($fp, $pack_offset);
184
        fseek($fp, $pack_offset);
177
        $size_info = unpack('C', fread($fp,1))[1];
185
        $size_info = unpack('C', fread($fp,1))[1];
178
 
186
 
-
 
187
        // Detect type
179
        $type = ($size_info & 0x70) >> 4; /*0b01110000*/
188
        $type = ($size_info & 0x70) >> 4; /*0b01110000*/
180
        switch ($type) {
189
        switch ($type) {
181
                case 1:
190
                case 1:
182
                        if ($debug) echo "Type = OBJ_COMMIT ($type)\n";
191
                        if ($debug) echo "Type = OBJ_COMMIT ($type)\n";
183
                        break;
192
                        break;
184
                case 2:
193
                case 2:
185
                        if ($debug) echo "Type = OBJ_TREE ($type)\n";
194
                        if ($debug) echo "Type = OBJ_TREE ($type)\n";
186
                        break;
195
                        break;
187
                case 3:
196
                case 3:
188
                        if ($debug) echo "Type = OBJ_BLOB ($type)\n";
197
                        if ($debug) echo "Type = OBJ_BLOB ($type)\n";
189
                        break;
198
                        break;
190
                case 4:
199
                case 4:
191
                        if ($debug) echo "Type = OBJ_TAG ($type)\n";
200
                        if ($debug) echo "Type = OBJ_TAG ($type)\n";
192
                        break;
201
                        break;
193
                case 6:
202
                case 6:
194
                        if ($debug) echo "Type = OBJ_OFS_DELTA ($type)\n";
203
                        if ($debug) echo "Type = OBJ_OFS_DELTA ($type)\n";
195
                        break;
204
                        break;
196
                case 7:
205
                case 7:
197
                        if ($debug) echo "Type = OBJ_REF_DELTA ($type)\n";
206
                        if ($debug) echo "Type = OBJ_REF_DELTA ($type)\n";
198
                        break;
207
                        break;
199
                default:
208
                default:
200
                        if ($debug) echo "Type = Invalid ($type)\n";
209
                        if ($debug) echo "Type = Invalid ($type)\n";
201
                        break;
210
                        break;
202
        }
211
        }
203
 
212
 
204
        // Find out size
213
        // Find out the expected unpacked size
205
        $size = $size_info & 0xF /*0x00001111*/;
214
        $size = $size_info & 0xF /*0x00001111*/;
206
        $shift_info = 4;
215
        $shift_info = 4;
207
        while ($size_info >= 0x80) {
216
        while ($size_info >= 0x80) {
208
                $size_info = unpack('C', fread($fp,1))[1];
217
                $size_info = unpack('C', fread($fp,1))[1];
209
                $size = (($size_info & 0x7F) << $shift_info) + $size;
218
                $size = (($size_info & 0x7F) << $shift_info) + $size;
210
                $shift_info += 7;
219
                $shift_info += 7;
211
        }
220
        }
212
 
-
 
213
        // TODO: Is this the packed or unpacked size? http://driusan.github.io/git-pack.html
-
 
214
        if ($debug) echo "Packed size = ".sprintf('0x%x',$size)."\n";
221
        if ($debug) echo "Expected unpacked size = $size\n";
215
 
222
 
216
        // Read delta base type
223
        // Read delta base type
-
 
224
        // Example implementation: https://github.com/AlexFBP/glip/blob/master/lib/git.class.php#L240
217
        if ($type == 6/*OBJ_OFS_DELTA*/) {
225
        if ($type == 6/*OBJ_OFS_DELTA*/) {
218
                // "a negative relative offset from the delta object's position in the pack if this is an OBJ_OFS_DELTA object"
226
                // "a negative relative offset from the delta object's position in the pack if this is an OBJ_OFS_DELTA object"
219
 
227
 
220
                // Offset encoding
228
                // Offset encoding
221
                $offset = 0;
229
                $offset = 0;
222
                $shift_info = 0;
230
                $shift_info = 0;
223
                do {
231
                do {
224
                        $offset_info = unpack('C', fread($fp,1))[1];
232
                        $offset_info = unpack('C', fread($fp,1))[1];
225
                        $offset = (($offset_info & 0x7F) << $shift_info) + $offset;
233
                        $offset = (($offset_info & 0x7F) << $shift_info) + $offset;
226
                        $shift_info += 7;
234
                        $shift_info += 7;
227
                } while ($offset_info >= 0x80);
235
                } while ($offset_info >= 0x80);
228
 
236
 
229
                if ($debug) echo "Delta negative offset: $offset\n";
237
                if ($debug) echo "Delta negative offset: $offset\n";
230
                throw new Exception("OBJ_OFS_DELTA is currently not implemented"); // TODO! Implement OBJ_OFS_DELTA!
238
                throw new Exception("OBJ_OFS_DELTA is currently not implemented"); // TODO! Implement OBJ_OFS_DELTA!
231
        }
239
        }
232
        if ($type == 7/*OBJ_REF_DELTA*/) {
240
        if ($type == 7/*OBJ_REF_DELTA*/) {
233
                // "base object name if OBJ_REF_DELTA"
241
                // "base object name if OBJ_REF_DELTA"
234
                $delta_info = bin2hex(fread($fp,20));
242
                $delta_info = bin2hex(fread($fp,20));
235
                if ($debug) echo "Delta base object name: $delta_info\n";
243
                if ($debug) echo "Delta base object name: $delta_info\n";
236
                throw new Exception("OBJ_REF_DELTA is currently not implemented"); // TODO! Implement OBJ_REF_DELTA!
244
                throw new Exception("OBJ_REF_DELTA is currently not implemented"); // TODO! Implement OBJ_REF_DELTA!
237
        }
245
        }
238
 
246
 
239
        // Read compressed data
247
        // Read and uncompress the compressed data
240
        $compressed = fread($fp,$size);
248
        $compressed = '';
241
 
-
 
242
        // Uncompress
249
        $uncompressed = false;
-
 
250
        for ($compressed_size=1; $compressed_size<=32768*$size; $compressed_size++) {
-
 
251
                // Since we don't know the compressed size, we need to do trial and error
-
 
252
                // TODO: this is a super stupid algorithm! Is there a better way???
-
 
253
                $compressed .= fread($fp,1);
243
        $uncompressed = @gzuncompress($compressed);
254
                $uncompressed = @gzuncompress($compressed);
-
 
255
                if (strlen($uncompressed) === $size) {
-
 
256
                        if ($debug) echo "Detected compressed size = $compressed_size\n";
-
 
257
                        break;
-
 
258
                }
-
 
259
        }
244
        if ($uncompressed === false) throw new Exception("Decompression failed");
260
        if ($uncompressed === false) throw new Exception("Decompression failed");
245
        if ($debug) echo "$uncompressed\n";
261
        if ($debug) echo "$uncompressed\n";
246
 
262
 
247
        // Close pack file
263
        // Close pack file
248
        fclose($fp);
264
        fclose($fp);
249
 
265
 
-
 
266
        if ($version == 2) {
250
        // Check CRC32
267
                // Check CRC32
251
        // TODO: Does not fit; neither crc32, nor crc32b...
268
                // TODO: Hash does not match. What are we doing wrong?!
252
        // if ($debug) echo "CRC32 found = 0x".hash('crc32',$compressed)." vs $crc32\n";
269
                // if ($debug) echo "CRC32 found = ".hash('crc32',$compressed)." vs $crc32\n";
-
 
270
                // if ($debug) echo "CRC32 found = ".hash('crc32b',$compressed)." vs $crc32\n";
-
 
271
        }
253
 
272
 
254
        return $uncompressed;
273
        return $uncompressed;
255
}
274
}
256
 
275