2017 © Pedro Peláez
 

library filesystem

Filesystem abstraction layer

image

ricwein/filesystem

Filesystem abstraction layer

  • Sunday, July 29, 2018
  • by ricwein
  • Repository
  • 1 Watchers
  • 1 Stars
  • 98 Installations
  • PHP
  • 1 Dependents
  • 1 Suggesters
  • 0 Forks
  • 0 Open issues
  • 5 Versions
  • 0 % Grown

The README.md

FileSystem

This Library provides a Filesystem abstraction layer., (*1)

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., (*2)

All classes uses the root-namespace ricwein\FileSystem., (*3)

Object-Types

A File is represented as a File Object and a Directory as a Directory Object., (*4)

Storage

Accessing the Objects (File/Directory) Content is abstracted as Storages. A File can be either:, (*5)

  • 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., (*6)

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:, (*7)

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:, (*8)

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., (*9)

Usage: Files

All FileSystem-base-classes must be initialized using a Storage., (*10)

Methods

method description
read(?$offset, ?$length, $mode) read and return file-content, allows partial read with $offset and $length parameters, file is locked while reading with $mode
stream(?$offset, ?$length, $mode) stream file into output-buffer, file is locked while reading with $mode
write($content, $append, $mode) write $content to file, creates new file if it doesn't already exists, allows appended writing if $append isset, locks file with $mode
copyTo($destination [,Constraint $constraints]) copy file to new $destination Storage-adapter
moveTo($destination [,Constraint $constraints]) like copyTo(), but moves files instead
touch([bool $ifNewOnly]) create file if it doesn't exists, updates last-modified timestamp
remove() try to remove file
getType([bool $withEncoding]) guess files mime-type
getTime([Time $type]) get last-modified unix-timestamp
getDate([Time $type]) same as getTime(), but returns a DateTime object instead
getSize() calculate size
getHash([Hash $mode [,string $algo [,bool $raw]]]) calculates a hash over $mode with $algo algorithm
isReadable() is file readable?
isWriteable() is file writeable?
isSymlink() is file a symlink?
isFile() is selected path an actual file?
isDir() is selected path a directory? => always false for File instance
isDotfile() is file a hidden dot-file?
isValid() run constraints validation
getHandle([string $mode]) gets new file-Handle for binary file-access
storage() access internal storage adapter
getPath() fetch filesystem-path
getStream() returns Stream wrapper around internal resource pointing to actual file
dir([,int $constraints [,string $as [,...$arguments]]]) get parent Directory of file

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., (*11)

Methods

method description
list($recursive) returns DirectoryIterator-Object to list directory-content as new File/Directory objects
mkdir() try to create directory
remove() try to remove directory
copyTo($destination [,Constraint $constraints]) copy directory recursively to new $destination Storage-adapter
getTime([Time $type]) get last-modified unix-timestamp
getDate([Time $type]) same as getTime(), but returns a DateTime object instead
getSize([bool $recursive]) calculate size
getHash([Hash $mode [,string $algo [,bool $raw]]]) calculates a hash over $mode with $algo algorithm
isReadable() is directory readable?
isWriteable() is directory writeable?
isSymlink() is directory a symlink?
isFile() is selected path a file? => always false for Directory instance
isDir() is selected path an actual directory?
isDotfile() is directory a hidden dot-file?
isValid() run constraints validation
storage() access internal storage adapter
getPath() fetch filesystem-path
file(string $filename [,int $constraints [,string $as [,...$arguments]]]) get File in current directory by name
dir(string $dirname [,int $constraints [,string $as [,...$arguments]]]) get Directory in current directory by name (clones storage!)

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., (*12)

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:, (*13)

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., (*14)

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:, (*15)

  • Constraint::IN_OPEN_BASEDIR => the path must be within the open_basedir php-ini paths, this allows throwing exceptions before running into php core errors
  • Constraint::DISALLOW_LINK => the path must not be a (symbolic-) link
  • Constraint::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), (*16)

    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 on imagemagick or gd (later one is default). Requires the Intervention\Image package.

Be aware: all image-file manipulations are directly mutating the original file!, (*17)

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
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 with Directory\Command., (*18)

    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!, (*19)

    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 native is_uploaded_file() and move_uploaded_file() functions., (*20)

    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., (*21)

ATTENTION: Usually it's a better idea to just use Storage\Stream instead!, (*22)

```php use ricwein\FileSystem\File; use ricwein\FileSystem\Storage;, (*23)

$resource = fopen('test.json', 'rb'); $file = new File(new Storage\Memory\Resource($resource)); fclose($resource); $content = $file->read(); ```, (*24)

The Versions

29/07 2018

dev-master

9999999-dev

Filesystem abstraction layer

  Sources   Download

MIT

The Requires

  • php >= 7.2.0

 

The Development Requires

by Richard Weinhold

29/07 2018

1.0.3

1.0.3.0

Filesystem abstraction layer

  Sources   Download

MIT

The Requires

  • php >= 7.2.0

 

The Development Requires

by Richard Weinhold

26/07 2018

1.0.2

1.0.2.0

Filesystem abstraction layer

  Sources   Download

MIT

The Requires

  • php >= 7.2.0

 

The Development Requires

by Richard Weinhold

26/07 2018

1.0.1

1.0.1.0

Filesystem abstraction layer

  Sources   Download

MIT

The Requires

  • php >= 7.2.0

 

The Development Requires

by Richard Weinhold

19/07 2018

1.0

1.0.0.0

Filesystem abstraction layer

  Sources   Download

MIT

The Requires

  • php >= 7.2.0

 

The Development Requires

by Richard Weinhold