silverstripe-framework/src/Assets/FileNameFilter.php

141 lines
4.0 KiB
PHP

<?php
namespace SilverStripe\Assets;
use SilverStripe\Core\Object;
use SilverStripe\View\Parsers\Transliterator;
/**
* Filter certain characters from file name, for nicer (more SEO-friendly) URLs
* as well as better filesystem compatibility. Can be used for files and folders.
*
* Caution: Does not take care of full filename sanitization in regards to directory traversal etc.,
* please use PHP's built-in basename() for this purpose.
*
* The default sanitizer is quite conservative regarding non-ASCII characters,
* in order to achieve maximum filesystem compatibility.
* In case your filesystem supports a wider character set,
* or is case sensitive, you might want to relax these rules
* via overriding {@link FileNameFilter_DefaultFilter::$default_replacements}.
*
* To leave uploaded filenames as they are (being aware of filesystem restrictions),
* add the following code to your YAML config:
* <code>
* FileNameFilter:
* default_use_transliterator: false
* default_replacements:
* </code>
*
* See {@link URLSegmentFilter} for a more generic implementation.
*/
class FileNameFilter extends Object
{
/**
* @config
* @var Boolean
*/
private static $default_use_transliterator = true;
/**
* @config
* @var array See {@link setReplacements()}.
*/
private static $default_replacements = array(
'/\s/' => '-', // remove whitespace
'/_/' => '-', // underscores to dashes
'/[^A-Za-z0-9+.\-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[\-]{2,}/' => '-', // remove duplicate dashes
'/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores
);
/**
* @var array See {@link setReplacements()}
*/
public $replacements = array();
/**
* Depending on the applied replacement rules, this method
* might result in an empty string. In this case, {@link getDefaultName()}
* will be used to return a randomly generated file name, while retaining its extension.
*
* @param string $name including extension (not path).
* @return string A filtered filename
*/
public function filter($name)
{
$ext = pathinfo($name, PATHINFO_EXTENSION);
$transliterator = $this->getTransliterator();
if ($transliterator) {
$name = $transliterator->toASCII($name);
}
foreach ($this->getReplacements() as $regex => $replace) {
$name = preg_replace($regex, $replace, $name);
}
// Safeguard against empty file names
$nameWithoutExt = pathinfo($name, PATHINFO_FILENAME);
if (empty($nameWithoutExt)) {
$name = $this->getDefaultName();
$name .= $ext ? '.' . $ext : '';
}
return $name;
}
/**
* Take care not to add replacements which might invalidate the file structure,
* e.g. removing dots will remove file extension information.
*
* @param array $r Map of find/replace used for preg_replace().
*/
public function setReplacements($r)
{
$this->replacements = $r;
}
/**
* @return array
*/
public function getReplacements()
{
return ($this->replacements) ? $this->replacements : (array)$this->config()->default_replacements;
}
/**
* Transliterator instance, or false to disable.
* If null will use default.
*
* @var Transliterator|false
*/
protected $transliterator;
/**
* @return Transliterator
*/
public function getTransliterator()
{
if ($this->transliterator === null && $this->config()->default_use_transliterator) {
$this->transliterator = Transliterator::create();
}
return $this->transliterator;
}
/**
* @param Transliterator|false $t
*/
public function setTransliterator($t)
{
$this->transliterator = $t;
}
/**
* @return String File name without extension
*/
public function getDefaultName()
{
return (string)uniqid();
}
}