1<?php 2 3namespace League\Flysystem; 4 5use InvalidArgumentException; 6use League\Flysystem\Adapter\CanOverwriteFiles; 7use League\Flysystem\Plugin\PluggableTrait; 8use League\Flysystem\Util\ContentListingFormatter; 9 10/** 11 * @method void emptyDir(string $dirname) 12 * @method array|false getWithMetadata(string $path, string[] $metadata) 13 * @method bool forceCopy(string $path, string $newpath) 14 * @method bool forceRename(string $path, string $newpath) 15 * @method array listFiles(string $path = '', boolean $recursive = false) 16 * @method string[] listPaths(string $path = '', boolean $recursive = false) 17 * @method array listWith(string[] $keys = [], $directory = '', $recursive = false) 18 */ 19class Filesystem implements FilesystemInterface 20{ 21 use PluggableTrait; 22 use ConfigAwareTrait; 23 24 /** 25 * @var AdapterInterface 26 */ 27 protected $adapter; 28 29 /** 30 * Constructor. 31 * 32 * @param AdapterInterface $adapter 33 * @param Config|array $config 34 */ 35 public function __construct(AdapterInterface $adapter, $config = null) 36 { 37 $this->adapter = $adapter; 38 $this->setConfig($config); 39 } 40 41 /** 42 * Get the Adapter. 43 * 44 * @return AdapterInterface adapter 45 */ 46 public function getAdapter() 47 { 48 return $this->adapter; 49 } 50 51 /** 52 * @inheritdoc 53 */ 54 public function has($path) 55 { 56 $path = Util::normalizePath($path); 57 58 return strlen($path) === 0 ? false : (bool) $this->getAdapter()->has($path); 59 } 60 61 /** 62 * @inheritdoc 63 */ 64 public function write($path, $contents, array $config = []) 65 { 66 $path = Util::normalizePath($path); 67 $this->assertAbsent($path); 68 $config = $this->prepareConfig($config); 69 70 return (bool) $this->getAdapter()->write($path, $contents, $config); 71 } 72 73 /** 74 * @inheritdoc 75 */ 76 public function writeStream($path, $resource, array $config = []) 77 { 78 if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') { 79 throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.'); 80 } 81 82 $path = Util::normalizePath($path); 83 $this->assertAbsent($path); 84 $config = $this->prepareConfig($config); 85 86 Util::rewindStream($resource); 87 88 return (bool) $this->getAdapter()->writeStream($path, $resource, $config); 89 } 90 91 /** 92 * @inheritdoc 93 */ 94 public function put($path, $contents, array $config = []) 95 { 96 $path = Util::normalizePath($path); 97 $config = $this->prepareConfig($config); 98 99 if ( ! $this->getAdapter() instanceof CanOverwriteFiles && $this->has($path)) { 100 return (bool) $this->getAdapter()->update($path, $contents, $config); 101 } 102 103 return (bool) $this->getAdapter()->write($path, $contents, $config); 104 } 105 106 /** 107 * @inheritdoc 108 */ 109 public function putStream($path, $resource, array $config = []) 110 { 111 if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') { 112 throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.'); 113 } 114 115 $path = Util::normalizePath($path); 116 $config = $this->prepareConfig($config); 117 Util::rewindStream($resource); 118 119 if ( ! $this->getAdapter() instanceof CanOverwriteFiles && $this->has($path)) { 120 return (bool) $this->getAdapter()->updateStream($path, $resource, $config); 121 } 122 123 return (bool) $this->getAdapter()->writeStream($path, $resource, $config); 124 } 125 126 /** 127 * @inheritdoc 128 */ 129 public function readAndDelete($path) 130 { 131 $path = Util::normalizePath($path); 132 $this->assertPresent($path); 133 $contents = $this->read($path); 134 135 if ($contents === false) { 136 return false; 137 } 138 139 $this->delete($path); 140 141 return $contents; 142 } 143 144 /** 145 * @inheritdoc 146 */ 147 public function update($path, $contents, array $config = []) 148 { 149 $path = Util::normalizePath($path); 150 $config = $this->prepareConfig($config); 151 152 $this->assertPresent($path); 153 154 return (bool) $this->getAdapter()->update($path, $contents, $config); 155 } 156 157 /** 158 * @inheritdoc 159 */ 160 public function updateStream($path, $resource, array $config = []) 161 { 162 if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') { 163 throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.'); 164 } 165 166 $path = Util::normalizePath($path); 167 $config = $this->prepareConfig($config); 168 $this->assertPresent($path); 169 Util::rewindStream($resource); 170 171 return (bool) $this->getAdapter()->updateStream($path, $resource, $config); 172 } 173 174 /** 175 * @inheritdoc 176 */ 177 public function read($path) 178 { 179 $path = Util::normalizePath($path); 180 $this->assertPresent($path); 181 182 if ( ! ($object = $this->getAdapter()->read($path))) { 183 return false; 184 } 185 186 return $object['contents']; 187 } 188 189 /** 190 * @inheritdoc 191 */ 192 public function readStream($path) 193 { 194 $path = Util::normalizePath($path); 195 $this->assertPresent($path); 196 197 if ( ! $object = $this->getAdapter()->readStream($path)) { 198 return false; 199 } 200 201 return $object['stream']; 202 } 203 204 /** 205 * @inheritdoc 206 */ 207 public function rename($path, $newpath) 208 { 209 $path = Util::normalizePath($path); 210 $newpath = Util::normalizePath($newpath); 211 $this->assertPresent($path); 212 $this->assertAbsent($newpath); 213 214 return (bool) $this->getAdapter()->rename($path, $newpath); 215 } 216 217 /** 218 * @inheritdoc 219 */ 220 public function copy($path, $newpath) 221 { 222 $path = Util::normalizePath($path); 223 $newpath = Util::normalizePath($newpath); 224 $this->assertPresent($path); 225 $this->assertAbsent($newpath); 226 227 return $this->getAdapter()->copy($path, $newpath); 228 } 229 230 /** 231 * @inheritdoc 232 */ 233 public function delete($path) 234 { 235 $path = Util::normalizePath($path); 236 $this->assertPresent($path); 237 238 return $this->getAdapter()->delete($path); 239 } 240 241 /** 242 * @inheritdoc 243 */ 244 public function deleteDir($dirname) 245 { 246 $dirname = Util::normalizePath($dirname); 247 248 if ($dirname === '') { 249 throw new RootViolationException('Root directories can not be deleted.'); 250 } 251 252 return (bool) $this->getAdapter()->deleteDir($dirname); 253 } 254 255 /** 256 * @inheritdoc 257 */ 258 public function createDir($dirname, array $config = []) 259 { 260 $dirname = Util::normalizePath($dirname); 261 $config = $this->prepareConfig($config); 262 263 return (bool) $this->getAdapter()->createDir($dirname, $config); 264 } 265 266 /** 267 * @inheritdoc 268 */ 269 public function listContents($directory = '', $recursive = false) 270 { 271 $directory = Util::normalizePath($directory); 272 $contents = $this->getAdapter()->listContents($directory, $recursive); 273 274 return (new ContentListingFormatter($directory, $recursive, $this->config->get('case_sensitive', true))) 275 ->formatListing($contents); 276 } 277 278 /** 279 * @inheritdoc 280 */ 281 public function getMimetype($path) 282 { 283 $path = Util::normalizePath($path); 284 $this->assertPresent($path); 285 286 if (( ! $object = $this->getAdapter()->getMimetype($path)) || ! array_key_exists('mimetype', $object)) { 287 return false; 288 } 289 290 return $object['mimetype']; 291 } 292 293 /** 294 * @inheritdoc 295 */ 296 public function getTimestamp($path) 297 { 298 $path = Util::normalizePath($path); 299 $this->assertPresent($path); 300 301 if (( ! $object = $this->getAdapter()->getTimestamp($path)) || ! array_key_exists('timestamp', $object)) { 302 return false; 303 } 304 305 return (int) $object['timestamp']; 306 } 307 308 /** 309 * @inheritdoc 310 */ 311 public function getVisibility($path) 312 { 313 $path = Util::normalizePath($path); 314 $this->assertPresent($path); 315 316 if (( ! $object = $this->getAdapter()->getVisibility($path)) || ! array_key_exists('visibility', $object)) { 317 return false; 318 } 319 320 return $object['visibility']; 321 } 322 323 /** 324 * @inheritdoc 325 */ 326 public function getSize($path) 327 { 328 $path = Util::normalizePath($path); 329 $this->assertPresent($path); 330 331 if (( ! $object = $this->getAdapter()->getSize($path)) || ! array_key_exists('size', $object)) { 332 return false; 333 } 334 335 return (int) $object['size']; 336 } 337 338 /** 339 * @inheritdoc 340 */ 341 public function setVisibility($path, $visibility) 342 { 343 $path = Util::normalizePath($path); 344 $this->assertPresent($path); 345 346 return (bool) $this->getAdapter()->setVisibility($path, $visibility); 347 } 348 349 /** 350 * @inheritdoc 351 */ 352 public function getMetadata($path) 353 { 354 $path = Util::normalizePath($path); 355 $this->assertPresent($path); 356 357 return $this->getAdapter()->getMetadata($path); 358 } 359 360 /** 361 * @inheritdoc 362 */ 363 public function get($path, Handler $handler = null) 364 { 365 $path = Util::normalizePath($path); 366 367 if ( ! $handler) { 368 $metadata = $this->getMetadata($path); 369 $handler = ($metadata && $metadata['type'] === 'file') ? new File($this, $path) : new Directory($this, $path); 370 } 371 372 $handler->setPath($path); 373 $handler->setFilesystem($this); 374 375 return $handler; 376 } 377 378 /** 379 * Assert a file is present. 380 * 381 * @param string $path path to file 382 * 383 * @throws FileNotFoundException 384 * 385 * @return void 386 */ 387 public function assertPresent($path) 388 { 389 if ($this->config->get('disable_asserts', false) === false && ! $this->has($path)) { 390 throw new FileNotFoundException($path); 391 } 392 } 393 394 /** 395 * Assert a file is absent. 396 * 397 * @param string $path path to file 398 * 399 * @throws FileExistsException 400 * 401 * @return void 402 */ 403 public function assertAbsent($path) 404 { 405 if ($this->config->get('disable_asserts', false) === false && $this->has($path)) { 406 throw new FileExistsException($path); 407 } 408 } 409} 410