Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7
 
8
declare(strict_types=1);
9
 
10
namespace Nette\Utils;
11
 
12
use Nette;
13
 
14
 
15
/**
16
 * File system tool.
17
 */
18
final class FileSystem
19
{
20
	use Nette\StaticClass;
21
 
22
	/**
23
	 * Creates a directory if it doesn't exist.
24
	 * @throws Nette\IOException  on error occurred
25
	 */
26
	public static function createDir(string $dir, int $mode = 0777): void
27
	{
28
		if (!is_dir($dir) && !@mkdir($dir, $mode, true) && !is_dir($dir)) { // @ - dir may already exist
29
			throw new Nette\IOException(sprintf(
30
				"Unable to create directory '%s' with mode %s. %s",
31
				self::normalizePath($dir),
32
				decoct($mode),
33
				Helpers::getLastError()
34
			));
35
		}
36
	}
37
 
38
 
39
	/**
40
	 * Copies a file or a directory. Overwrites existing files and directories by default.
41
	 * @throws Nette\IOException  on error occurred
42
	 * @throws Nette\InvalidStateException  if $overwrite is set to false and destination already exists
43
	 */
44
	public static function copy(string $origin, string $target, bool $overwrite = true): void
45
	{
46
		if (stream_is_local($origin) && !file_exists($origin)) {
47
			throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($origin)));
48
 
49
		} elseif (!$overwrite && file_exists($target)) {
50
			throw new Nette\InvalidStateException(sprintf("File or directory '%s' already exists.", self::normalizePath($target)));
51
 
52
		} elseif (is_dir($origin)) {
53
			static::createDir($target);
54
			foreach (new \FilesystemIterator($target) as $item) {
55
				static::delete($item->getPathname());
56
			}
57
 
58
			foreach ($iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($origin, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $item) {
59
				if ($item->isDir()) {
60
					static::createDir($target . '/' . $iterator->getSubPathName());
61
				} else {
62
					static::copy($item->getPathname(), $target . '/' . $iterator->getSubPathName());
63
				}
64
			}
65
		} else {
66
			static::createDir(dirname($target));
67
			if (
68
				($s = @fopen($origin, 'rb'))
69
				&& ($d = @fopen($target, 'wb'))
70
				&& @stream_copy_to_stream($s, $d) === false
71
			) { // @ is escalated to exception
72
				throw new Nette\IOException(sprintf(
73
					"Unable to copy file '%s' to '%s'. %s",
74
					self::normalizePath($origin),
75
					self::normalizePath($target),
76
					Helpers::getLastError()
77
				));
78
			}
79
		}
80
	}
81
 
82
 
83
	/**
84
	 * Deletes a file or directory if exists.
85
	 * @throws Nette\IOException  on error occurred
86
	 */
87
	public static function delete(string $path): void
88
	{
89
		if (is_file($path) || is_link($path)) {
90
			$func = DIRECTORY_SEPARATOR === '\\' && is_dir($path) ? 'rmdir' : 'unlink';
91
			if (!@$func($path)) { // @ is escalated to exception
92
				throw new Nette\IOException(sprintf(
93
					"Unable to delete '%s'. %s",
94
					self::normalizePath($path),
95
					Helpers::getLastError()
96
				));
97
			}
98
		} elseif (is_dir($path)) {
99
			foreach (new \FilesystemIterator($path) as $item) {
100
				static::delete($item->getPathname());
101
			}
102
 
103
			if (!@rmdir($path)) { // @ is escalated to exception
104
				throw new Nette\IOException(sprintf(
105
					"Unable to delete directory '%s'. %s",
106
					self::normalizePath($path),
107
					Helpers::getLastError()
108
				));
109
			}
110
		}
111
	}
112
 
113
 
114
	/**
115
	 * Renames or moves a file or a directory. Overwrites existing files and directories by default.
116
	 * @throws Nette\IOException  on error occurred
117
	 * @throws Nette\InvalidStateException  if $overwrite is set to false and destination already exists
118
	 */
119
	public static function rename(string $origin, string $target, bool $overwrite = true): void
120
	{
121
		if (!$overwrite && file_exists($target)) {
122
			throw new Nette\InvalidStateException(sprintf("File or directory '%s' already exists.", self::normalizePath($target)));
123
 
124
		} elseif (!file_exists($origin)) {
125
			throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($origin)));
126
 
127
		} else {
128
			static::createDir(dirname($target));
129
			if (realpath($origin) !== realpath($target)) {
130
				static::delete($target);
131
			}
132
 
133
			if (!@rename($origin, $target)) { // @ is escalated to exception
134
				throw new Nette\IOException(sprintf(
135
					"Unable to rename file or directory '%s' to '%s'. %s",
136
					self::normalizePath($origin),
137
					self::normalizePath($target),
138
					Helpers::getLastError()
139
				));
140
			}
141
		}
142
	}
143
 
144
 
145
	/**
146
	 * Reads the content of a file.
147
	 * @throws Nette\IOException  on error occurred
148
	 */
149
	public static function read(string $file): string
150
	{
151
		$content = @file_get_contents($file); // @ is escalated to exception
152
		if ($content === false) {
153
			throw new Nette\IOException(sprintf(
154
				"Unable to read file '%s'. %s",
155
				self::normalizePath($file),
156
				Helpers::getLastError()
157
			));
158
		}
159
 
160
		return $content;
161
	}
162
 
163
 
164
	/**
165
	 * Writes the string to a file.
166
	 * @throws Nette\IOException  on error occurred
167
	 */
168
	public static function write(string $file, string $content, ?int $mode = 0666): void
169
	{
170
		static::createDir(dirname($file));
171
		if (@file_put_contents($file, $content) === false) { // @ is escalated to exception
172
			throw new Nette\IOException(sprintf(
173
				"Unable to write file '%s'. %s",
174
				self::normalizePath($file),
175
				Helpers::getLastError()
176
			));
177
		}
178
 
179
		if ($mode !== null && !@chmod($file, $mode)) { // @ is escalated to exception
180
			throw new Nette\IOException(sprintf(
181
				"Unable to chmod file '%s' to mode %s. %s",
182
				self::normalizePath($file),
183
				decoct($mode),
184
				Helpers::getLastError()
185
			));
186
		}
187
	}
188
 
189
 
190
	/**
191
	 * Fixes permissions to a specific file or directory. Directories can be fixed recursively.
192
	 * @throws Nette\IOException  on error occurred
193
	 */
194
	public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void
195
	{
196
		if (is_file($path)) {
197
			if (!@chmod($path, $fileMode)) { // @ is escalated to exception
198
				throw new Nette\IOException(sprintf(
199
					"Unable to chmod file '%s' to mode %s. %s",
200
					self::normalizePath($path),
201
					decoct($fileMode),
202
					Helpers::getLastError()
203
				));
204
			}
205
		} elseif (is_dir($path)) {
206
			foreach (new \FilesystemIterator($path) as $item) {
207
				static::makeWritable($item->getPathname(), $dirMode, $fileMode);
208
			}
209
 
210
			if (!@chmod($path, $dirMode)) { // @ is escalated to exception
211
				throw new Nette\IOException(sprintf(
212
					"Unable to chmod directory '%s' to mode %s. %s",
213
					self::normalizePath($path),
214
					decoct($dirMode),
215
					Helpers::getLastError()
216
				));
217
			}
218
		} else {
219
			throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($path)));
220
		}
221
	}
222
 
223
 
224
	/**
225
	 * Determines if the path is absolute.
226
	 */
227
	public static function isAbsolute(string $path): bool
228
	{
229
		return (bool) preg_match('#([a-z]:)?[/\\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
230
	}
231
 
232
 
233
	/**
234
	 * Normalizes `..` and `.` and directory separators in path.
235
	 */
236
	public static function normalizePath(string $path): string
237
	{
238
		$parts = $path === '' ? [] : preg_split('~[/\\\\]+~', $path);
239
		$res = [];
240
		foreach ($parts as $part) {
241
			if ($part === '..' && $res && end($res) !== '..' && end($res) !== '') {
242
				array_pop($res);
243
			} elseif ($part !== '.') {
244
				$res[] = $part;
245
			}
246
		}
247
 
248
		return $res === ['']
249
			? DIRECTORY_SEPARATOR
250
			: implode(DIRECTORY_SEPARATOR, $res);
251
	}
252
 
253
 
254
	/**
255
	 * Joins all segments of the path and normalizes the result.
256
	 */
257
	public static function joinPaths(string ...$paths): string
258
	{
259
		return self::normalizePath(implode('/', $paths));
260
	}
261
}