ricwein / filesystem
Filesystem abstraction layer
Installs: 2 323
Dependents: 4
Suggesters: 1
Security: 0
Stars: 8
Watchers: 2
Forks: 0
Open Issues: 1
Requires
- php: >=8.1
- ext-fileinfo: *
- ext-openssl: *
- ext-zip: *
Requires (Dev)
- ext-gd: *
- intervention/image: ^2.7
- league/flysystem: ^3.12
- overtrue/phplint: ^9.0
- phpunit/phpunit: ^10.0
Suggests
- ext-gd: Allows easy usage of gd resouces as Storage\Memory\Resource()
- intervention/image: Adds image-manipulation capabilities
- league/flysystem: Support for more filesystems
- dev-master
- 5.0-beta1
- 4.4.4
- 4.4.3
- 4.4.2
- 4.4.1
- 4.4.0
- 4.3.2
- 4.3.1
- 4.3.0
- 4.2.3
- 4.2.2
- 4.2.1
- 4.2.0
- 4.1.0
- 4.0.2
- 4.0.1
- 4.0
- 3.2.0
- 3.1.0
- 3.0.2
- 3.0.1
- 3.0
- 2.2.1
- 2.2.0
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.7
- 2.0.6
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0
- 1.4.8
- 1.4.7
- 1.4.6
- 1.4.5
- 1.4.4
- 1.4.3
- 1.4.2
- 1.4.1
- 1.4
- 1.3.6
- 1.3.5
- 1.3.4
- 1.3.3
- 1.3.2
- 1.3.1
- 1.3
- 1.2.2
- 1.2.1
- 1.2
- 1.1.5
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1
- 1.0.10
- 1.0.9
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0
- dev-dependabot/composer/guzzlehttp/psr7-2.5.0
This package is auto-updated.
Last update: 2024-09-12 11:13:01 UTC
README
This Library provides a Filesystem abstraction layer.
use ricwein\FileSystem\Exceptions\FilesystemException; use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; try { $file = new File(new Storage\Disk(__DIR__, '/test', '/dir2', 'test.json')); $alt = new File(new Storage\Memory('{"some": "content"}')); // read file into output-buffer if ($file->isReadable()) { header('Content-Type: ' . $file->getType(true)); echo $file->read(); } else { header('Content-Type: ' . $alt->getType(true)); echo $alt->read(); } } catch (FileSystemException $e) { http_response_code(500); echo json_encode(['errors' => [ 'status' => $e->getCode(), 'title' => 'something went wrong', 'detail' => $e->getMessage(), ]]); }
Installation
composer require ricwein/filesystem
Overview
Let's begin with a short overview over the supported Filesystem classes and abstractions.
All classes uses the root-namespace ricwein\FileSystem
.
Object-Types
A File is represented as a File
Object and a Directory as a Directory
Object.
Storage
Accessing the Objects (File/Directory) Content is abstracted as Storage
s. A File can be either:
- a
Disk
file/directory at your local filesystem (Storage\Disk
) - a
Memory
file, which only temporary exists in-memory (Storage\Memory
) - a
Stream
a stream which points to a file or resource (Storage\Stream
) - an abstract
Flysystem
file (Storage\Flysystem
)
All Storage-Types must extend the abstract base class Filesystem\Storage
.
WARNING: since storage-objects are mutable and php automatically handles class-objects as references when passed into a function (constructor), it's highly recommended using the
clone
keyword when storages are recycled between to FileSystem-Objects. DO NOT:
use ricwein\FileSystem\Directory; use ricwein\FileSystem\Storage; $originalDir = new Directory(new Storage\Disk(__DIR__)); $copyDir = new Directory($originalDir->storage()); $copyDir->cd('test'); // will also change $originalDir path!
DO:
use ricwein\FileSystem\Directory; use ricwein\FileSystem\Storage; $originalDir = new Directory(new Storage\Disk(__DIR__)); $copyDir = new Directory(clone $originalDir->storage()); $copyDir->cd('test'); // $originalDir will stay in __DIR__
Exceptions
Accessing File/Directory Attributes can result in throwing Exceptions. All Exceptions implement the Exceptions\FileSystemException
Interface.
Usage: Files
All FileSystem-base-classes must be initialized using a Storage.
Methods
Open and read a file from the local filesystem
use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $file = new File(new Storage\Disk(__DIR__, 'test.txt')); $content = $file->read();
or from in-memory-file
use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $file = new File(new Storage\Memory('some content')); $content = $file->read();
or from stream
use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $file = new File(new Storage\Stream(fopen('php://output', 'wb'))); $content = $file->write('content');
or from a Flysystem object
use League\Flysystem\Local\LocalFilesystemAdapter; use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $file = new File(new Storage\Flysystem(new LocalFilesystemAdapter(__DIR__), 'test.txt')); $content = $file->read();
Usage: Directories
Like Files, Directories must be initialized using a Storage.
Methods
check if directory is readable
use ricwein\FileSystem\Directory; use ricwein\FileSystem\Storage; $dir = new Directory(new Storage\Disk(__DIR__)); var_dump($dir->isReadable());
list all files inside a directory
use ricwein\FileSystem\Directory; use ricwein\FileSystem\Storage; $hashes = []; $dir = new Directory(new Storage\Disk(__DIR__)); foreach($dir->list(true)->files() as $file) { $hashes[$file->getPath()->getFilename()] = $file->getHash(); }
Security
Using this filesystem-layer also provides some kind of security for usage with user-defined file-paths. Accessing file attributes or content is only done after checking against so called Constraints.
Constraints
Constraints are defined on initialization of File
or Directory
objects and are stored inside the internal Storage
object. This allows Constraints-inheritance, if new FileSystem-objects are accessed from existing ones. Example:
use ricwein\FileSystem\Directory; use ricwein\FileSystem\Helper\Constraint; use ricwein\FileSystem\Storage; $dir = new Directory(new Storage\Disk(__DIR__), Constraint::STRICT); $file = $dir->file($_GET['filename']);
In this example, the $file
object shares the Constraints (inherited) and safepath with $dir
- allowing safely accessing a file in $dir
from user defined parameters. Path traversal is therefore prevented.
The following constraints are set as default (as part of Constraint::STRICT
), but can be overwritten with the second argument of the File($storage, $constraints)
or Directory($storage, $constraints)
constructor:
Constraint::IN_OPEN_BASEDIR
=> the path must be within theopen_basedir
php-ini paths, this allows throwing exceptions before running into php core errorsConstraint::DISALLOW_LINK
=> the path must not be a (symbolic-) linkConstraint::IN_SAFEPATH
=> if a file/directory path is build out of multiple components (parameters), the resulting file/directory destination must be inside the first path-component (called safepath)
use ricwein\FileSystem\File; use ricwein\FileSystem\Helper\Constraint; use ricwein\FileSystem\Storage; // let's assume $_GET['file'] == '/../file.txt' // path concatenated as a single string // this runs fine but is HIGHLY UNRECOMMENDED $file = new File(new Storage\Disk(__DIR__ . $_GET['file'])); // path is passed as single parameters (comma instead of dot!) // this throws an error since the resulting path is not within the safepath (__DIR__) $file = new File(new Storage\Disk(__DIR__, $_GET['file'])); // however: disabling the safepath-constraint would also allow path traversal attacks: $file = new File(new Storage\Disk(__DIR__, $_GET['file']), Constraint::STRICT & ~Constraint::IN_SAFEPATH);
Extensions
Directory Extensions
Directory\Command
: Allows running shell-commands inside the given directory.
use ricwein\FileSystem\Directory; use ricwein\FileSystem\Helper\Constraint; use ricwein\FileSystem\Storage; $git = new Directory\Command(new Storage\Disk(__DIR__), Constraint::STRICT, ['/usr/local/bin/git', '/usr/bin/git']); $ref = $git->execSafe('rev-parse HEAD');
File Extensions
File\Image
: Allows image-manipulations based onimagemagick
orgd
(later one is default). Requires theIntervention\Image
package.
Be aware: all image-file manipulations are directly mutating the original file!
use Intervention\Image\Image; use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $image = new File\Image(new Storage\Disk('test.png')); $image->resizeToFit(1024, 1024); $image->compress(1048576); // iterative process to reduce filesize to be less than given filesize (1MB) by reducing the jpg-quality // $image->encode('jpg'); $image->edit(function (Image $image): Image { // add advanced image-manipulation here [...] return $image; });
File\Zip
: Allows basic zip-operations, like creating a new archive or extracting an existing one.
use ricwein\FileSystem\Directory; use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $zip = new File\Zip(new Storage\Disk('archive.zip')); // create zip file $zip->add(new File(new Storage\Disk('file.json'))); // or $zip->addFile(...) $zip->add(new File(new Storage\Memory('some file-content')), 'anotherfile.txt'); // or $zip->addFile(...) $zip->add(new Directory(new Storage\Disk(__DIR__, 'data-dir'))); // or $zip->addDirectory(...) $zip->commit(); // extract zip file $extractDir = $zip->extractTo(new Storage\Disk\Temp);
File\SSLCertificate
: Access some basic x509 SSL Certificate information, either from:- a certificate-file (usually
.crt
) - a Server (by URL) if it's serving a ssl based protocol like
HTTPS
- a certificate-file (usually
use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $cert = new File\SSLCertificate(new Storage\File('certs/domain.crt')); // OR $cert = new File\SSLCertificate(new Storage\Memory('ssl://domain.com:443')); // or for HTTPS simply: 'domain.com' // check if cert it currently valid (by timestamps) $isValid = $cert->isValid(); // check if cert is also valid for a given host $isValidForHost = $cert->isValidFor('subdomain.domain.com'); // show some certificate details print_r([ 'issuer' => $cert->getIssuer(), 'subject' => $cert->getValidDomains(), 'validFrom' => $cert->validFrom()->format('d.m.Y H:i:s'), 'validTo' => $cert->validTo()->format('d.m.Y H:i:s'), ]);
Storage Extensions
Disk\Current
: Uses current-working-directory (getcwd()
) as safepath. Useful for cli-scripts in combination withDirectory\Command
.
use ricwein\FileSystem\File; use ricwein\FileSystem\Helper\Constraint; use ricwein\FileSystem\Storage; $current = new File(new Storage\Disk(getcwd(), 'file.json'), Constraint::STRICT & ~Constraint::IN_SAFEPATH); // is the same as: $current = new File(new Storage\Disk\Current('file.json'));
use ricwein\FileSystem\Directory; use ricwein\FileSystem\Helper\Constraint; use ricwein\FileSystem\Storage; $git = new Directory\Command(new Storage\Disk\Current, Constraint::STRICT, ['/usr/local/bin/git', '/usr/bin/git']); if (!$git->execSafe('pull $branch', ['branch' => 'develop'])) { echo 'failed to execute: ' . $git->getLastCommand() . PHP_EOL; } exit($git->lastExitCode());
Disk\Temp
: Uses the system-temp directory to create a temporary file/directory. The file is automatically removed after freeing the object instance!
use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $temp = new File(new Storage\Disk\Temp); $temp->write('test'); $temp->read(); $temp = null; // will delete the temp-file again!
Disk\Uploaded
: Provides safe and easy uploaded-files access through php's nativeis_uploaded_file()
andmove_uploaded_file()
functions.
use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $uploaded = new File(new Storage\Disk\Uploaded($_FILES['file'])); $file = $uploaded->moveTo(new Storage\Disk(__DIR__, 'uploads'));
Memory\Resource
: Reads resource content into MEMORY on construction. The resource can be closed afterward.
ATTENTION: Usually it's a better idea to just use
Storage\Stream
instead!
use ricwein\FileSystem\File; use ricwein\FileSystem\Storage; $resource = fopen('test.json', 'rb'); $file = new File(new Storage\Memory\Resource($resource)); fclose($resource); $content = $file->read();