The framework provides a simple component spiral/files
to work with the filesystem.
Most of the Spiral components rely on the directory registry instead of hard-coded paths. The registry is represented
using Spiral\Boot\DirectoriesInterface
.
See more
Read more about application directory structure in the Getting started — Directory Structure section.
You can configure application specific directories in the app entry point (app.php
):
$app = \App\Application\Kernel::create(
directories: [
'root' => __DIR__,
'uploadDir' => __DIR__ . '/upload'
]
)->run();
Or using the Bootloader:
namespace App\Application\Bootloader;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Boot\DirectoriesInterface;
final class AppBootloader extends Bootloader
{
public function boot(DirectoriesInterface $dirs): void
{
$dirs->set(
'uploadDir',
$dirs->get('root') . '/upload'
);
}
}
To access the directory paths:
use Spiral\Boot\DirectoriesInterface;
final class UploadService {
public function __construct(
private readonly DirectoriesInterface $dirs
) {}
public function store(UploadedFile $file) {
$filePath = $this->dirs->get('uploadDir') . $file->getFilename();
// ...
}
}
Note
You can also use the short functiondirectory
inside your config files. Note that this function does not work outside of the framework as it relies on the global container scope.
If you would like a directory names always be constrained by a specific method, you may create a helper class, for
example AppDirectories
.
namespace App\Application;
use Spiral\Boot\DirectoriesInterface;
final class AppDirectories
{
public function __construct(
private readonly DirectoriesInterface $directories
) {
}
/**
* Application root directory.
* @return non-empty-string
*/
public function getRoot(?string $path = null): string
{
return $this->buildPath('root', $path);
}
/**
* Application directory.
* @return non-empty-string
*/
public function getApp(?string $path = null): string
{
return $this->buildPath('app', $path);
}
/**
* Public directory.
* @return non-empty-string
*/
public function getPublic(?string $path = null): string
{
return $this->buildPath('public', $path);
}
/**
* Runtime directory.
* @return non-empty-string
*/
public function getRuntime(?string $path = null): string
{
return $this->buildPath('runtime', $path);
}
/**
* Runtime cache directory.
* @return non-empty-string
*/
public function getCache(?string $path = null): string
{
return $this->buildPath('cache', $path);
}
/**
* Vendor libraries directory.
* @return non-empty-string
*/
public function getVendor(?string $path = null): string
{
return $this->buildPath('vendor', $path);
}
/**
* Config directory.
* @return non-empty-string
*/
public function getConfig(?string $path = null): string
{
return $this->buildPath('config', $path);
}
/**
* Resources directory.
* @return non-empty-string
*/
public function getResources(?string $path = null): string
{
return $this->buildPath('resources', $path);
}
private function buildPath(string $key, ?string $path = null): string
{
return \rtrim($this->directories->get($key), '/') . ($path ? '/' . \ltrim($path, '/') : '');
}
}
Use the Spiral\Files\FilesInterface
component to work with the filesystem:
use Spiral\Files\FilesInterface;
final class FileService
{
public function __construct(
private readonly FilesInterface $files,
private readonly AppDirectories $dirs
) {}
public function getRootFiles()
{
// get all files from root directory recursively
dump(
$files->getFiles($dirs->getRoot())
);
}
}
You can also access this instance using prototyped property files
:
use Spiral\Prototype\Traits\PrototypeTrait;
final class FileService
{
use PrototypeTrait;
public function __construct(
private readonly AppDirectories $dirs
) {}
public function store()
{
dump($this->files->exists(__FILE__)); // true
}
}
See more
Read more about prototyped properties in The Basics — Prototyping section.
To ensure that a given directory exists, use method ensureDirectory
, the second argument accepts the access
permission:
public function store()
{
$this->files->ensureDirectory(
$dirs->get('customDir'),
FilesInterface::READONLY // or FilesInterface::RUNTIME for editable dirs and files
);
}
To check if a directory exists:
dump($files->isDirectory(__DIR__));
To delete a directory and its content:
$files->deleteDirectory('custom');
To delete directory content only:
$files->deleteDirectory('custom', true);
To check if a file exists:
dump($files->exists(__FILE__)); // bool
To get file creation/update time:
dump($files->time(__FILE__)); // unix timestamp
To get file MD5:
dump($files->md5('filename'));
To get a filename extension:
dump($files->extension(__FILE__)); // without leading "."
To check if the path is a file:
dump($files->isFile(__DIR__));
To get the file size:
dump($files->size(__DIR__));
To get file/directory permissions:
dump($files->getPermissions(__FILE__)); // int
To set file permissions:
$files->setPermissions(__FILE__, 0777)
Use constants to control the file mode:
Constant | Value |
---|---|
FilesInterface::READONLY | 644 |
FilesInterface::RUNTIME | 666 |
To copy a file from one path to another:
$files->copy('old-path', 'new-path');
To move a file:
$files->move('old-path', 'new-path');
To issue a temporary filename:
dump($files->tempFilename());
To issue a temporary filename with a specific extension:
dump($files->tempFilename('php'));
To issue a temporary filename in a specific location:
dump($files->tempFilename('php', __DIR__));
The component provides multiple methods to operate with file content in an atomic way (without acquiring the file resource):
To write content to a file (exclusive lock):
$files->write('filename', 'data');
To write/create a file and ensure a proper access mode:
$files->write('filename', 'data', 0777);
To check and automatically create a file directory:
$files->write('filename', 'data', 0777, true);
Note
Make sure to handleSpiral\Files\Exception\WriteErrorException
if the file can is not writable.
To append file content:
$files->append('filename', 'data');
To append and ensure a file mode:
$files->append('filename', 'data', 0777);
To append/create a file and ensure that a target directory exists:
$files->append('filename', 'data', 0777, true);
To touch the file and create it, if missing:
$files->touch('filename');
To touch the file and ensure a file mode:
$files->touch('filename', 0777);
To read file content:
dump($files->read('filename'));
Note
Make sure to handleSpiral\Files\Exception\FileNotFoundException
when files are not found.