*/
class PMXI_Helper {
const GLOB_MARK = 1;
const GLOB_NOSORT = 2;
const GLOB_ONLYDIR = 4;
const GLOB_NODIR = 256;
const GLOB_PATH = 512;
const GLOB_NODOTS = 1024;
const GLOB_RECURSE = 2048;
const FNM_PATHNAME = 1;
const FNM_NOESCAPE = 2;
const FNM_PERIOD = 4;
const FNM_CASEFOLD = 16;
/**
* A safe empowered glob().
*
* Function glob() is prohibited on some server (probably in safe mode)
* (Message "Warning: glob() has been disabled for security reasons in
* (script) on line (line)") for security reasons as stated on:
* http://seclists.org/fulldisclosure/2005/Sep/0001.html
*
* safe_glob() intends to replace glob() using readdir() & fnmatch() instead.
* Supported flags: self::GLOB_MARK, self::GLOB_NOSORT, self::GLOB_ONLYDIR
* Additional flags: self::GLOB_NODIR, self::GLOB_PATH, self::GLOB_NODOTS, self::GLOB_RECURSE
* (not original glob() flags)
* @author BigueNique AT yahoo DOT ca
* @updates
* - 080324 Added support for additional flags: self::GLOB_NODIR, self::GLOB_PATH,
* self::GLOB_NODOTS, self::GLOB_RECURSE
* - 100607 Recurse is_dir check fixed by Pavel Kulbakin
*/
public static function safe_glob($pattern, $flags=0) {
$split = explode('/', str_replace('\\', '/', $pattern));
$mask = array_pop($split);
$path = implode('/', $split);
if (($dir = @opendir($path . '/')) !== false or ($dir = @opendir($path)) !== false) {
$glob = array();
while(($file = readdir($dir)) !== false) {
// Recurse subdirectories (self::GLOB_RECURSE)
if (($flags & self::GLOB_RECURSE) && is_dir($path . '/' . $file) && ( ! in_array($file, array('.', '..')))) {
$glob = array_merge($glob, self::array_prepend(self::safe_glob($path . '/' . $file . '/' . $mask, $flags), ($flags & self::GLOB_PATH ? '' : $file . '/')));
}
// Match file mask
if (self::fnmatch($mask, $file, self::FNM_CASEFOLD)) {
if ((( ! ($flags & self::GLOB_ONLYDIR)) || is_dir("$path/$file"))
&& (( ! ($flags & self::GLOB_NODIR)) || ( ! is_dir($path . '/' . $file)))
&& (( ! ($flags & self::GLOB_NODOTS)) || ( ! in_array($file, array('.', '..'))))
) {
$glob[] = ($flags & self::GLOB_PATH ? $path . '/' : '') . $file . ($flags & self::GLOB_MARK ? '/' : '');
}
}
}
closedir($dir);
if ( ! ($flags & self::GLOB_NOSORT)) sort($glob);
return $glob;
} else {
return (strpos($pattern, "*") === false) ? array($pattern) : false;
}
}
/**
* Prepends $string to each element of $array
* If $deep is true, will indeed also apply to sub-arrays
* @author BigueNique AT yahoo DOT ca
* @since 080324
*/
public static function array_prepend($array, $string, $deep=false) {
if(empty($array)||empty($string)) {
return $array;
}
foreach ($array as $key => $element) {
if (is_array($element)) {
if ($deep) {
$array[$key] = self::array_prepend($element,$string,$deep);
} else {
trigger_error(__METHOD__ . ': array element', E_USER_WARNING);
}
} else {
$array[$key] = $string.$element;
}
}
return $array;
}
/**
* non-POSIX complient remplacement for the fnmatch
*/
public static function fnmatch($pattern, $string, $flags = 0) {
$modifiers = null;
$transforms = array(
'\*' => '.*',
'\?' => '.',
'\[\!' => '[^',
'\[' => '[',
'\]' => ']',
'\.' => '\.',
'\\' => '\\\\',
'\-' => '-',
);
// Forward slash in string must be in pattern:
if ($flags & self::FNM_PATHNAME) {
$transforms['\*'] = '[^/]*';
}
// Back slash should not be escaped:
if ($flags & self::FNM_NOESCAPE) {
unset($transforms['\\']);
}
// Perform case insensitive match:
if ($flags & self::FNM_CASEFOLD) {
$modifiers .= 'i';
}
// Period at start must be the same as pattern:
if ($flags & self::FNM_PERIOD) {
if (strpos($string, '.') === 0 && strpos($pattern, '.') !== 0) return false;
}
$pattern = '#^'
.strtr(preg_quote($pattern, '#'), $transforms)
.'$#'
.$modifiers;
return (boolean)preg_match($pattern, $string);
}
}