FileNamingResolver
A lightweight library which helps to resolve a file/directory naming of uploaded files
using various naming strategies., (*1)
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., (*2)
, (*3)
, (*4)
Contents
Requirements
This library is standalone without any dependencies. To use it in your project
ensure you correspond to the next requirements:, (*5)
Installation
The preferred way to install this package is to use Composer:, (*6)
$ 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:, (*7)
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., (*8)
// 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
., (*9)
FileInfo
The FileNamingResolver\FileInfo
class extends \SplFileInfo
object. For more info
check the [SplFileInfo][10]. Below shown list of frequently used getters of this class:, (*10)
$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., (*11)
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:, (*12)
$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., (*13)
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., (*14)
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 an InvalidArgumentException
will be thrown. You probably need to use a isFile()
method on FileInfo
object to be sure that file exists. It's necessary because this naming
strategy try to hash a real file content., (*15)
DatetimeNamingStrategy
The naming behavior of datetime naming strategy looks like WordPress naming
of uploaded media files., (*16)
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., (*17)
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., (*18)
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
);
}
}
?>
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., (*19)
In order to propose a new feature, the best way is to submit an Issue
and discuss it first., (*20)
Move UP, (*21)