Standard   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 361
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 49
eloc 82
c 2
b 0
f 0
dl 0
loc 361
rs 8.48

18 Methods

Rating   Name   Duplication   Size   Complexity  
A isdir() 0 3 1
A mkdir() 0 7 2
B __construct() 0 21 7
A rmdir() 0 7 2
A reads() 0 7 2
A writes() 0 22 5
A resolve() 0 9 2
A time() 0 13 3
A copy() 0 11 3
A writef() 0 10 2
A read() 0 7 2
A write() 0 11 3
A has() 0 3 1
A scan() 0 6 3
A size() 0 7 2
A rm() 0 7 2
A readf() 0 11 4
A move() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like Standard often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Standard, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2025
6
 * @package Base
7
 * @subpackage Filesystem
8
 */
9
10
11
namespace Aimeos\Base\Filesystem;
12
13
14
/**
15
 * Implementation of basic file system methods
16
 *
17
 * @package Base
18
 * @subpackage Filesystem
19
 */
20
class Standard implements Iface, DirIface, MetaIface
21
{
22
	private string $basedir;
23
	private string $tempdir;
24
25
26
	/**
27
	 * Initializes the object
28
	 *
29
	 * @param array $config Adapter configuration
30
	 */
31
	public function __construct( array $config )
32
	{
33
		if( !isset( $config['tempdir'] ) ) {
34
			$config['tempdir'] = sys_get_temp_dir();
35
		}
36
37
		if( !is_dir( $config['tempdir'] ) && @mkdir( $config['tempdir'], 0755, true ) === false ) {
38
			throw new Exception( sprintf( 'Directory "%1$s" could not be created', $config['tempdir'] ) );
39
		}
40
41
		if( !isset( $config['basedir'] ) ) {
42
			throw new Exception( sprintf( 'Configuration option "%1$s" missing', 'basedir' ) );
43
		}
44
45
		if( !is_dir( $config['basedir'] ) && @mkdir( $config['basedir'], 0755, true ) === false ) {
46
			throw new Exception( sprintf( 'Directory "%1$s" could not be created', $config['basedir'] ) );
47
		}
48
49
		$ds = DIRECTORY_SEPARATOR;
50
		$this->basedir = realpath( str_replace( '/', $ds, rtrim( $config['basedir'], '/' ) ) ) . $ds;
51
		$this->tempdir = realpath( str_replace( '/', $ds, rtrim( $config['tempdir'], '/' ) ) ) . $ds;
52
	}
53
54
55
	/**
56
	 * Tests if the given path is a directory
57
	 *
58
	 * @param string $path Path to the file or directory
59
	 * @return bool True if directory, false if not
60
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
61
	 */
62
	public function isdir( string $path ) : bool
63
	{
64
		return is_dir( $this->resolve( $path ) );
65
	}
66
67
68
	/**
69
	 * Creates a new directory for the given path
70
	 *
71
	 * @param string $path Path to the directory
72
	 * @return \Aimeos\Base\Filesystem\DirIface Filesystem object for fluent interface
73
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
74
	 */
75
	public function mkdir( string $path ) : DirIface
76
	{
77
		if( @mkdir( $this->resolve( $path ), 0775, true ) === false ) {
78
			throw new Exception( sprintf( 'Couldn\'t create directory "%1$s"', $path ) );
79
		}
80
81
		return $this;
82
	}
83
84
85
	/**
86
	 * Deletes the directory for the given path
87
	 *
88
	 * @param string $path Path to the directory
89
	 * @return \Aimeos\Base\Filesystem\DirIface Filesystem object for fluent interface
90
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
91
	 */
92
	public function rmdir( string $path ) : DirIface
93
	{
94
		if( @rmdir( $this->resolve( $path ) ) === false ) {
95
			throw new Exception( sprintf( 'Couldn\'t remove directory "%1$s"', $path ) );
96
		}
97
98
		return $this;
99
	}
100
101
102
	/**
103
	 * Returns an iterator over the entries in the given path
104
	 *
105
	 * @param string|null $path Path to the filesystem or directory
106
	 * @return iterable Iterator over the entries or array with entries
107
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
108
	 */
109
	public function scan( ?string $path = null ) : iterable
110
	{
111
		foreach( new \DirectoryIterator( $this->resolve( (string) $path ) ) as $entry )
112
		{
113
			if( !$entry->isDot() ) {
114
				yield $entry->getFileName();
115
			}
116
		}
117
	}
118
119
120
	/**
121
	 * Returns the file size
122
	 *
123
	 * @param string $path Path to the file
124
	 * @return int Size in bytes
125
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
126
	 */
127
	public function size( string $path ) : int
128
	{
129
		if( ( $size = @filesize( $this->resolve( $path ) ) ) === false ) {
130
			throw new Exception( sprintf( 'Couldn\'t get file size for "%1$s"', $path ) );
131
		}
132
133
		return $size;
134
	}
135
136
137
	/**
138
	 * Returns the Unix time stamp for the file
139
	 *
140
	 * @param string $path Path to the file
141
	 * @return int Unix time stamp in seconds
142
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
143
	 */
144
	public function time( string $path ) : int
145
	{
146
		$path = $this->resolve( $path );
147
148
		if( ( $mtime = @filemtime( $path ) ) === false ) {
149
			throw new Exception( sprintf( 'Couldn\'t get file mtime for "%1$s"', $path ) );
150
		}
151
152
		if( ( $ctime = @filectime( $path ) ) === false ) {
153
			throw new Exception( sprintf( 'Couldn\'t get file ctime for "%1$s"', $path ) );
154
		}
155
156
		return max( $mtime, $ctime );
157
	}
158
159
160
	/**
161
	 * Deletes the file for the given path
162
	 *
163
	 * @param string $path Path to the file
164
	 * @return \Aimeos\Base\Filesystem\Iface Filesystem object for fluent interface
165
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
166
	 */
167
	public function rm( string $path ) : Iface
168
	{
169
		if( @unlink( $this->resolve( $path ) ) === false ) {
170
			throw new Exception( sprintf( 'Couldn\'t delete file "%1$s"', $path ) );
171
		}
172
173
		return $this;
174
	}
175
176
177
	/**
178
	 * Tests if a file exists at the given path
179
	 *
180
	 * @param string $path Path to the file
181
	 * @return bool True if it exists, false if not
182
	 */
183
	public function has( string $path ) : bool
184
	{
185
		return file_exists( $this->resolve( $path ) );
186
	}
187
188
189
	/**
190
	 * Returns the content of the file
191
	 *
192
	 * @param string $path Path to the file
193
	 * @return string File content
194
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
195
	 */
196
	public function read( string $path ) : string
197
	{
198
		if( ( $content = @file_get_contents( $this->resolve( $path ) ) ) === false ) {
199
			throw new Exception( sprintf( 'Couldn\'t read file "%1$s"', $path ) );
200
		}
201
202
		return $content;
203
	}
204
205
206
	/**
207
	 * Reads the content of the remote file and writes it to a local one
208
	 *
209
	 * @param string $path Path to the remote file
210
	 * @param string|null $local Path to the local file (optional)
211
	 * @return string Path of the local file
212
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
213
	 */
214
	public function readf( string $path, ?string $local = null ) : string
215
	{
216
		if( $local === null && ( $local = @tempnam( $this->tempdir, 'ai-' ) ) === false ) {
217
			throw new Exception( sprintf( 'Unable to create file in "%1$s"', $this->tempdir ) );
218
		}
219
220
		if( @copy( $this->resolve( $path ), $local ) === false ) {
221
			throw new Exception( sprintf( 'Couldn\'t copy file from "%1$s" to "%2$s"', $path, $local ) );
222
		}
223
224
		return $local;
225
	}
226
227
228
	/**
229
	 * Returns the stream descriptor for the file
230
	 *
231
	 * @param string $path Path to the file
232
	 * @return resource File stream descriptor
233
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
234
	 */
235
	public function reads( string $path )
236
	{
237
		if( ( $handle = @fopen( $this->resolve( $path ), 'r' ) ) === false ) {
238
			throw new Exception( sprintf( 'Couldn\'t read file "%1$s"', $path ) );
239
		}
240
241
		return $handle;
242
	}
243
244
245
	/**
246
	 * Writes the given content to the file
247
	 *
248
	 * @param string $path Path to the file
249
	 * @param string $content New file content
250
	 * @return \Aimeos\Base\Filesystem\Iface Filesystem object for fluent interface
251
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
252
	 */
253
	public function write( string $path, string $content ) : Iface
254
	{
255
		if( !$this->isDir( dirname( $path ) ) ) {
256
			$this->mkdir( dirname( $path ) );
257
		}
258
259
		if( @file_put_contents( $this->resolve( $path ), $content ) === false ) {
260
			throw new Exception( sprintf( 'Couldn\'t write file "%1$s"', $path ) );
261
		}
262
263
		return $this;
264
	}
265
266
267
	/**
268
	 * Writes the content of the local file to the remote path
269
	 *
270
	 * @param string $path Path to the remote file
271
	 * @param string $local Path to the local file
272
	 * @return \Aimeos\Base\Filesystem\Iface Filesystem object for fluent interface
273
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
274
	 */
275
	public function writef( string $path, string $local ) : Iface
276
	{
277
		if( ( $handle = @fopen( $local, 'r' ) ) === false ) {
278
			throw new Exception( sprintf( 'Unable to open file "%1$s"', $local ) );
279
		}
280
281
		$this->writes( $path, $handle );
282
283
		fclose( $handle );
284
		return $this;
285
	}
286
287
288
	/**
289
	 * Write the content of the stream descriptor into the remote file
290
	 *
291
	 * @param string $path Path to the file
292
	 * @param resource $stream File stream descriptor
293
	 * @return \Aimeos\Base\Filesystem\Iface Filesystem object for fluent interface
294
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
295
	 */
296
	public function writes( string $path, $stream ) : Iface
297
	{
298
		if( !$this->isDir( dirname( $path ) ) ) {
299
			$this->mkdir( dirname( $path ) );
300
		}
301
302
		if( ( $handle = @fopen( $this->resolve( $path ), 'w' ) ) === false ) {
303
			throw new Exception( sprintf( 'Couldn\'t open file "%1$s"', $path ) );
304
		}
305
306
		try
307
		{
308
			if( !is_resource( $stream ) || @stream_copy_to_stream( $stream, $handle ) === false ) {
309
				throw new Exception( sprintf( 'Couldn\'t copy stream for "%1$s"', $path ) );
310
			}
311
		}
312
		finally
313
		{
314
			fclose( $handle );
315
		}
316
317
		return $this;
318
	}
319
320
321
	/**
322
	 * Renames a file, moves it to a new location or both at once
323
	 *
324
	 * @param string $from Path to the original file
325
	 * @param string $to Path to the new file
326
	 * @return \Aimeos\Base\Filesystem\Iface Filesystem object for fluent interface
327
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
328
	 */
329
	public function move( string $from, string $to ) : Iface
330
	{
331
		if( !$this->isDir( dirname( $to ) ) ) {
332
			$this->mkdir( dirname( $to ) );
333
		}
334
335
		if( @rename( $this->resolve( $from ), $this->resolve( $to ) ) === false ) {
336
			throw new Exception( sprintf( 'Couldn\'t move file from "%1$s" to "%2$s"', $from, $to ) );
337
		}
338
339
		return $this;
340
	}
341
342
343
	/**
344
	 * Copies a file to a new location
345
	 *
346
	 * @param string $from Path to the original file
347
	 * @param string $to Path to the new file
348
	 * @return \Aimeos\Base\Filesystem\Iface Filesystem object for fluent interface
349
	 * @throws \Aimeos\Base\Filesystem\Exception If an error occurs
350
	 */
351
	public function copy( string $from, string $to ) : Iface
352
	{
353
		if( !$this->isDir( dirname( $to ) ) ) {
354
			$this->mkdir( dirname( $to ) );
355
		}
356
357
		if( @copy( $this->resolve( $from ), $this->resolve( $to ) ) === false ) {
358
			throw new Exception( sprintf( 'Couldn\'t copy file from "%1$s" to "%2$s"', $from, $to ) );
359
		}
360
361
		return $this;
362
	}
363
364
365
	/**
366
	 * Resolves the relative path to the absolute one
367
	 *
368
	 * @param string $path Relative path within file system
369
	 * @return string Absolute path
370
	 * @throws Exception If relative path is invalid
371
	 */
372
	protected function resolve( string $path = '' ) : string
373
	{
374
		$path = trim( $path, '/' );
375
376
		if( strpos( $path, '..' ) !== false ) {
377
			throw new Exception( sprintf( 'No ".." allowed in path "%1$s"', $path ) );
378
		}
379
380
		return $this->basedir . str_replace( '/', DIRECTORY_SEPARATOR, $path );
381
	}
382
}
383