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