FileNamingResolver
A lightweight library which helps to resolve a file/directory naming of uploaded files using various naming strategies.
This library solves only naming things and do nothing with any files or directories directly. If you are looking for some filesystem abstraction layer - looks closer to Gaufrette, symfony/filesystem or Flysystem libraries.
Contents
Requirements
This library is standalone without any dependencies. To use it in your project ensure you correspond to the next requirements:
- PHP
5.3
or higher
Installation
The preferred way to install this package is to use Composer:
$ composer require bocharsky-bw/file-naming-resolver
If you don't use Composer
- register this package in your autoloader manually following PSR-4 autoloader standard or simply download this library and require
the necessary files directly in your scripts:
require_once __DIR__ . '/path/to/library/src/FileNamingResolver.php';
// and other files you intend to use...
Usage
First of all, before using file naming resolver, you should determine which naming strategy to use. You can use different strategies out-of-the-box or easily create your own one by implementing the NamingStrategyInterface
interface or extends the AbstractNamingStrategy
class which already implemented it for you.
// don't forget to use namespaces
use FileNamingResolver\FileInfo;
use FileNamingResolver\FileNamingResolver;
use FileNamingResolver\NamingStrategy\HashNamingStrategy;
// First of all, upload new file or use any uploaded file on the your server
// Create source FileInfo object from FQFN of uploaded file
$srcFileInfo = new FileInfo(__DIR__.'/uploads/image.jpg');
// Create at least one naming strategy object
$hashStrategy = new HashNamingStrategy();
// Create file naming resolver and pass naming strategy to it
$namingResolver = new FileNamingResolver($hashStrategy);
// Resolve new name using specified naming strategy
$dstFileInfo = $namingResolver->resolve($srcFileInfo);
echo $dstFileInfo->toString(); // /var/www/html/web/uploads/4e/d3/a51a07c8e89ff8f228075b7fc76b.jpg
// Use rename() / move_uploaded_file() built-in functions, Gaufrette or any other filesystem
// abstraction library to move uploaded file to the directory according to suggested pathname.
NOTE: In all examples hereinafter the
__DIR__
equals to/var/www/html/web
.
FileInfo
The FileNamingResolver\FileInfo
class extends \SplFileInfo
object. For more info check the SplFileInfo. Below shown list of frequently used getters of this class:
$fileInfo = new FileInfo('/var/www/html/web/uploads/products/image.jpg');
echo $fileInfo->toString(); // '/var/www/html/web/uploads/products/image.jpg'
echo $fileInfo->getPathname(); // '/var/www/html/web/uploads/products/image.jpg'
echo $fileInfo->getPath(); // '/var/www/html/web/uploads/products'
echo $fileInfo->getFilename(); // 'image.jpg'
echo $fileInfo->getBasename(); // 'image'
echo $fileInfo->getExtension(); // 'jpg'
echo $fileInfo->getPathRelativeTo('/var/www/html/web'); // 'uploads/products'
echo $fileInfo->getPathnameRelativeTo('/var/www/html/web'); // 'uploads/products/image.jpg'
Strategy list
AggregateNamingStrategy
This naming strategy allows to use as many naming strategies as you need at once. Its aggregate results. Each new result pathname based on the previous one.
use FileNamingResolver\FileInfo;
use FileNamingResolver\FileNamingResolver;
use FileNamingResolver\NamingStrategy\AggregateNamingStrategy;
use FileNamingResolver\NamingStrategy\DatetimeNamingStrategy;
use FileNamingResolver\NamingStrategy\HashNamingStrategy;
$srcFileInfo = new FileInfo(__DIR__.'/uploads/image.jpg');
$datetimeStrategy = new DatetimeNamingStrategy();
$hashStrategy = new HashNamingStrategy();
// Create an aggregate naming strategy object
$strategies = [
$datetimeStrategy,
$hashStrategy,
// and so on as many as you need...
];
$aggregateStrategy = new AggregateNamingStrategy($strategies);
$namingResolver = new FileNamingResolver($aggregateStrategy);
$dstFileInfo = $namingResolver->resolve($srcFileInfo);
echo $dstFileInfo->toString(); // /var/www/html/web/uploads/2015/12/9c/98/87cbf44f53c9f6fa08f44ce705c8.jpg
To reverse applying order of strategies pass true
as second parameter to the constructor of AggregateNamingStrategy
class:
$aggregateStrategy = new AggregateNamingStrategy($strategies, AggregateNamingStrategy::MODE_REVERSE);
$namingResolver = new FileNamingResolver($aggregateStrategy);
$dstFileInfo = $namingResolver->resolve($srcFileInfo);
echo $dstFileInfo->toString(); // /var/www/html/web/uploads/a0/cb/2015/12/11-23-35-039900.jpg
CallbackNamingStrategy
This naming strategy allows create a custom naming logic using custom callbacks.
use FileNamingResolver\FileInfo;
use FileNamingResolver\FileNamingResolver;
use FileNamingResolver\NamingStrategy\CallbackNamingStrategy;
$srcFileInfo = new FileInfo(__DIR__.'/uploads/image.jpg');
// Create a custom callback naming strategy object
$callbackStrategy = new CallbackNamingStrategy(function (FileInfo $srcFileInfo) {
$dstFileInfo = $srcFileInfo
// Add 'products' suffix to the path
->changePath($srcFileInfo->getPath().FileInfo::SEPARATOR_DIRECTORY.'products')
// Generate custom basename of the file without extension
->changeBasename(time().'-'.uniqid()) // comment this line to keep original basename
// or do whatever custom naming logic you want here...
;
return $dstFileInfo;
});
$namingResolver = new FileNamingResolver($callbackStrategy);
$dstFileInfo = $namingResolver->resolve($srcFileInfo);
echo $dstFileInfo->toString(); // /var/www/html/web/uploads/products/1450004778-566d512a32d2c.jpg
ContentHashNamingStrategy
The naming behavior of hash naming strategy looks like Twig naming of cached files.
use FileNamingResolver\FileInfo;
use FileNamingResolver\FileNamingResolver;
use FileNamingResolver\NamingStrategy\ContentHashNamingStrategy;
$srcFileInfo = new FileInfo(__DIR__.'/uploads/image.jpg');
// Create a hash naming strategy object
$contentHashStrategy = new ContentHashNamingStrategy(
// Hashing algorithm, by default: 'md5'
ContentHashNamingStrategy::ALGORITHM_SHA1, // 'sha1'
// Count of parts for explode, by default: 2
3,
// Length of each exploded part, by default: 2
3,
// Keep full filename for easy searching
true
);
$namingResolver = new FileNamingResolver($contentHashStrategy);
$dstFileInfo = $namingResolver->resolve($srcFileInfo);
echo $dstFileInfo->toString(); // /var/www/html/web/uploads/4ed/3a5/1a0/4ed3a51a07c8e89ff8f228075b7fc76b.jpg
NOTE: Be sure that source file really exist before using
ContentHashNamingStrategy
otherwise anInvalidArgumentException
will be thrown. You probably need to use aisFile()
method onFileInfo
object to be sure that file exists. It's necessary because this naming strategy try to hash a real file content.
DatetimeNamingStrategy
The naming behavior of datetime naming strategy looks like WordPress naming of uploaded media files.
use FileNamingResolver\FileInfo;
use FileNamingResolver\FileNamingResolver;
use FileNamingResolver\NamingStrategy\DatetimeNamingStrategy;
$srcFileInfo = new FileInfo(__DIR__.'/uploads/image.jpg');
// Create a datetime naming strategy object
$datetimeStrategy = new DatetimeNamingStrategy(
// DateTime format for directories, by default: 'Y/m'
DateTimeNamingStrategy::FORMAT_DIR_YEAR_MONTH_DAY, // 'Y/m/d'
// DateTime format for files, by default: 'H-i-s-u'
DateTimeNamingStrategy::FORMAT_FILE_TIMESTAMP_MICROSECONDS // 'U-u'
);
$namingResolver = new FileNamingResolver($datetimeStrategy);
$dstFileInfo = $namingResolver->resolve($srcFileInfo);
echo $dstFileInfo->toString(); // /var/www/html/web/uploads/2015/12/13/1450004392-907500.jpg
HashNamingStrategy
The naming behavior of hash naming strategy looks like Twig naming of cached files.
use FileNamingResolver\FileInfo;
use FileNamingResolver\FileNamingResolver;
use FileNamingResolver\NamingStrategy\HashNamingStrategy;
$srcFileInfo = new FileInfo(__DIR__.'/uploads/image.jpg');
// Create a hash naming strategy object
$hashStrategy = new HashNamingStrategy(
// Hashing algorithm, by default: 'md5'
HashNamingStrategy::ALGORITHM_SHA1, // 'sha1'
// Count of parts for explode, by default: 2
3,
// Length of each exploded part, by default: 2
3,
// Keep full filename for easy searching
true
);
$namingResolver = new FileNamingResolver($hashStrategy);
$dstFileInfo = $namingResolver->resolve($srcFileInfo);
echo $dstFileInfo->toString(); // /var/www/html/web/uploads/4ed/3a5/1a0/4ed3a51a07c8e89ff8f228075b7fc76b.jpg
Example
There is a full working example how to upload files with a simple HTML form and built-in PHP functions using FileNamingResolver library.
require_once __DIR__.'/vendor/autoload.php';
use FileNamingResolver\FileInfo;
use FileNamingResolver\FileNamingResolver;
use FileNamingResolver\NamingStrategy\HashNamingStrategy;
// Define a few constants for convenience
define('WEB_ROOT_DIR', __DIR__);
define('UPLOAD_ROOT_DIR', WEB_ROOT_DIR.'/uploads');
// Check whether form was submitted...
if (isset($_POST['upload'])) {
// and file was attached - process file uploading
if ($_FILES['attachment']['tmp_name']) {
$strategy = new HashNamingStrategy(); // or create any other strategy you need here
$namingResolver = new FileNamingResolver($strategy); // use created naming strategy in resolver
$dstFileInfo = new FileInfo(UPLOAD_ROOT_DIR.'/'.$_FILES['attachment']['name']); // build destination pathname
$dstFileInfo = $namingResolver->resolve($dstFileInfo); // apply specified naming strategy to the destination file
// Change extension based on mime type of uploaded file
// WARNING: Could be skipped if you want to base on original extension in $_FILES['attachment']['name'] but it could cause some security issues!
switch ($_FILES['attachment']['type']) {
case 'image/jpeg':
$extension = 'jpg';
break;
case 'image/png':
$extension = 'png';
break;
default:
$extension = 'bin';
}
// The FileInfo object is immutable so changeExtension() creates a new object.
// Don't forget to override previous variable
$dstFileInfo = $dstFileInfo->changeExtension($extension);
// create non-existent directories for the destination file
if (false === is_dir($dstFileInfo->getPath())) {
if (false === mkdir($dstFileInfo->getPath(), 0777, true)) {
throw new RuntimeException('Unable to create a directory for the destination file.');
}
}
// move uploaded file according to the destination pathname
if (false === move_uploaded_file($_FILES['attachment']['tmp_name'], $dstFileInfo->toString())) {
throw new RuntimeException('Unable to move an uploaded file to the specified directory.');
}
// Do whatever you want after successful uploading (i.e redirect user after success uploading)
echo sprintf(
'File "%s" successfully uploaded to the "%s" web directory! :)',
$dstFileInfo->getFilename(),
$dstFileInfo->getPathRelativeTo(WEB_ROOT_DIR) // get path relative to the web directory
);
}
}
?>
<form action="" method="POST" enctype="multipart/form-data">
<input type="file" name="attachment">
<input type="submit" name="upload" value="Upload file">
form>
Contribution
Feel free to submit an Issue or create a Pull Request if you find a bug or just want to propose an improvement suggestion.
In order to propose a new feature, the best way is to submit an Issue and discuss it first.