<?php /** * @package framework * @subpackage filesystem */ /** * 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 Filename 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() . '.' . $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 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; } /** * @var SS_Transliterator */ protected $transliterator; /** * @return SS_Transliterator|NULL */ public function getTransliterator() { if($this->transliterator === null && $this->config()->default_use_transliterator) { $this->transliterator = SS_Transliterator::create(); } return $this->transliterator; } /** * @param SS_Transliterator|FALSE */ public function setTransliterator($t) { $this->transliterator = $t; } /** * @return String File name without extension */ public function getDefaultName() { return (string)uniqid(); } }