Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
77.27% |
34 / 44 |
|
20.00% |
2 / 10 |
CRAP | |
0.00% |
0 / 1 |
FileUtils | |
77.27% |
34 / 44 |
|
20.00% |
2 / 10 |
26.18 | |
0.00% |
0 / 1 |
copy | |
33.33% |
1 / 3 |
|
0.00% |
0 / 1 |
3.19 | |||
getContents | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
putContents | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
2.50 | |||
openInputFile | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
openOutputFile | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
openInputFileStream | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
openOutputFileStream | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
copyStreamToFile | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
4.37 | |||
mkdir | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
2.50 | |||
getMwHashes | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
3.00 |
1 | <?php |
2 | |
3 | namespace Shellbox; |
4 | |
5 | use GuzzleHttp\Psr7\Utils; |
6 | use Psr\Http\Message\StreamInterface; |
7 | |
8 | /** |
9 | * Throwing wrappers for file functions |
10 | * |
11 | * @internal |
12 | */ |
13 | class FileUtils { |
14 | private const COPY_BUFFER_SIZE = 65536; |
15 | |
16 | /** |
17 | * Copy file |
18 | * |
19 | * @param string $source |
20 | * @param string $dest |
21 | * @throws ShellboxError |
22 | */ |
23 | public static function copy( $source, $dest ) { |
24 | if ( !copy( $source, $dest ) ) { |
25 | throw new ShellboxError( "Error while copying " . |
26 | basename( $source ) . ' to ' . basename( $dest ) ); |
27 | } |
28 | } |
29 | |
30 | /** |
31 | * Get contents |
32 | * |
33 | * @param string $path |
34 | * @return string |
35 | * @throws ShellboxError |
36 | */ |
37 | public static function getContents( $path ) { |
38 | $contents = file_get_contents( $path ); |
39 | if ( $contents === false ) { |
40 | throw new ShellboxError( "Unable to read " . basename( $path ) ); |
41 | } |
42 | return $contents; |
43 | } |
44 | |
45 | /** |
46 | * Put contents |
47 | * |
48 | * @param string $path |
49 | * @param string $contents |
50 | * @throws ShellboxError |
51 | */ |
52 | public static function putContents( $path, $contents ) { |
53 | if ( !file_put_contents( $path, $contents ) ) { |
54 | throw new ShellboxError( "Unable to write " . basename( $path ) ); |
55 | } |
56 | } |
57 | |
58 | /** |
59 | * Open a file in read mode |
60 | * |
61 | * @param string $path |
62 | * @return resource |
63 | * @throws ShellboxError |
64 | */ |
65 | public static function openInputFile( $path ) { |
66 | $file = fopen( $path, 'r' ); |
67 | if ( !$file ) { |
68 | throw new ShellboxError( "Error opening input file " . basename( $path ) ); |
69 | } |
70 | return $file; |
71 | } |
72 | |
73 | /** |
74 | * Open a file in write mode |
75 | * |
76 | * @param string $path |
77 | * @return resource |
78 | * @throws ShellboxError |
79 | */ |
80 | public static function openOutputFile( $path ) { |
81 | $file = fopen( $path, 'w' ); |
82 | if ( !$file ) { |
83 | throw new ShellboxError( "Error opening output file " . basename( $path ) ); |
84 | } |
85 | return $file; |
86 | } |
87 | |
88 | /** |
89 | * Open a file in read mode and convert it to a StreamInterface |
90 | * |
91 | * @param string $path |
92 | * @return StreamInterface |
93 | * @throws ShellboxError |
94 | */ |
95 | public static function openInputFileStream( $path ) { |
96 | return Utils::streamFor( self::openInputFile( $path ) ); |
97 | } |
98 | |
99 | /** |
100 | * Open a file in write mode and convert it to a StreamInterface |
101 | * |
102 | * @param string $path |
103 | * @return StreamInterface |
104 | * @throws ShellboxError |
105 | */ |
106 | public static function openOutputFileStream( $path ) { |
107 | return Utils::streamFor( self::openOutputFile( $path ) ); |
108 | } |
109 | |
110 | /** |
111 | * Copy a stream to a file |
112 | * |
113 | * @param StreamInterface $stream |
114 | * @param string $path |
115 | * @throws ShellboxError |
116 | */ |
117 | public static function copyStreamToFile( StreamInterface $stream, string $path ) { |
118 | $file = self::openOutputFile( $path ); |
119 | while ( !$stream->eof() ) { |
120 | $buf = $stream->read( self::COPY_BUFFER_SIZE ); |
121 | if ( fwrite( $file, $buf ) !== strlen( $buf ) ) { |
122 | throw new ShellboxError( "Error copying stream to file " . basename( $path ) ); |
123 | } |
124 | } |
125 | if ( !fclose( $file ) ) { |
126 | throw new ShellboxError( "Error copying stream to file " . basename( $path ) ); |
127 | } |
128 | } |
129 | |
130 | /** |
131 | * Make a directory with group/other permission bits masked out |
132 | * |
133 | * @param string $path |
134 | * @throws ShellboxError |
135 | */ |
136 | public static function mkdir( $path ) { |
137 | if ( !mkdir( $path, 0700 ) ) { |
138 | throw new ShellboxError( "Error creating directory " . basename( $path ) ); |
139 | } |
140 | } |
141 | |
142 | /** |
143 | * Get content hash headers for MediaWiki from a stream |
144 | * |
145 | * @param StreamInterface $stream |
146 | * @return array |
147 | * @throws ShellboxError |
148 | */ |
149 | public static function getMwHashes( StreamInterface $stream ) { |
150 | $srcSize = $stream->getSize(); |
151 | $md5Context = hash_init( 'md5' ); |
152 | $sha1Context = hash_init( 'sha1' ); |
153 | $hashDigestSize = 0; |
154 | while ( !$stream->eof() ) { |
155 | $buffer = $stream->read( 131_072 ); // 128 KiB |
156 | hash_update( $md5Context, $buffer ); |
157 | hash_update( $sha1Context, $buffer ); |
158 | $hashDigestSize += strlen( $buffer ); |
159 | } |
160 | if ( $hashDigestSize !== $srcSize ) { |
161 | throw new ShellboxError( "Stream truncated while hashing" ); |
162 | } |
163 | return [ |
164 | 'etag' => hash_final( $md5Context ), |
165 | 'x-object-meta-sha1base36' => |
166 | \Wikimedia\base_convert( hash_final( $sha1Context ), 16, 36, 31 ) |
167 | ]; |
168 | } |
169 | } |