Dateisystem-Klasse
FileSystem class
Well, there is not much to say about it. Its mainly a wrapper for the normal file system functions of PHP, except that it works with exceptions and has recursive functions. Risk a glance what you can do with it. The usage example is here:
Nun, es gibt darüber eigentlich nicht viel zu sagen. Die Klasse ist hauptsächlich ein Wrapper für die PHP Dateisystem-Funktionen. Die Klasse arbeitet lediglich mit Exceptions und es sind ein paar rekursive Funktionen implementiert, die das Leben leichter machen. Der Beispiel-Quelltext ist hier:
Sample source code
Anwendungsbeispiel
<?php
require_once('swlib/swlib.class.php');
use sw\FileSystem as fs;
//
// Example function
//
function filesystem_example() {
// Get the temp directory
$tmp_dir = fs::getTempDirectory();
$my_dir = fs::getTempFileName();
// Boolean check methods
$tmp_dir_exists = (int) fs::exists($tmp_dir);
$tmp_dir_is_dir = (int) fs::isDirectory($tmp_dir);
$tmp_dir_is_file = (int) fs::isFile($tmp_dir);
$tmp_dir_is_link = (int) fs::isLink($tmp_dir);
$tmp_dir_readable = (int) fs::isReadable($tmp_dir);
$tmp_dir_writable = (int) fs::isWritable($tmp_dir);
$tmp_dir_executable = (int) fs::isExecutable($tmp_dir);
// The usual file system actions ...
fs::mkdir($my_dir);
fs::chdir($my_dir);
fs::chmod($my_dir, 0777);
//
// We expect exceptions here because we are not root when running this example.
//
try {
fs::chown($my_dir, 'root');
} catch(\Exception $e) {
$chown_exception = $e->getMessage();
unset($e);
}
try {
fs::chgroup($my_dir, 'root');
} catch(\Exception $e) {
$chgrp_exception = $e->getMessage();
unset($e);
}
//
// Let's work with a file:
//
$file = 'test.txt';
// Read / write
fs::writeFile($file, "HELLO");
$my_file_contents = fs::readFile($file);
// Mode, user, group, size, last modified
$file_mode = fs::getFileModeString($file);
$file_user = fs::getUserName($file);
$file_group = fs::getGroupName($file);
$file_size = fs::getFileSize($file);
$file_mtime_old = fs::getLastModified($file);
// Change last modified time
fs::touch($file, $file_mtime_old - 3600);
// Some file path related methods
$file_basename = fs::getBasename($file);
$file_extension = fs::getExtension($file);
$file_dirname = fs::getDirname($file);
$file_name_no_ext = fs::getFileNameWithoutExtension($file);
// Copy, rename, move, unlink, delete, find recursively
$file1 = "$file_name_no_ext.1.txt";
$file2 = "$file_name_no_ext.2.txt";
fs::copy($file, $file1);
fs::rename($file1, $file2);
$file_list = fs::find($my_dir, '*.txt');
fs::unlink($file2);
$file2_was_deleted = (int) !fs::exists($file2);
fs::move($file, $file1);
fs::delete($my_dir); // This is RECURSIVE
$my_dir_was_deleted = (int) !fs::exists($my_dir);
//
// Print out our results saved in variables.
//
$vars = get_defined_vars();
print_r($vars);
}
// Run the example
filesystem_example();
Output
Ausgabe
$ php filesystem.php
Array
(
[tmp_dir] => /tmp
[my_dir] => /tmp/xj0wh
[tmp_dir_exists] => 1
[tmp_dir_is_dir] => 1
[tmp_dir_is_file] => 0
[tmp_dir_is_link] => 1
[tmp_dir_readable] => 1
[tmp_dir_writable] => 1
[tmp_dir_executable] => 1
[chown_exception] => Failed to change file owner of "/tmp/xj0wh" to "root"
[chgrp_exception] => Failed to change file group of "/tmp/xj0wh" to "root"
[file] => test.txt
[my_file_contents] => HELLO
[file_mode] => -rw-r--r--
[file_user] => cerberos
[file_group] => wheel
[file_size] => 5
[file_mtime_old] => 1377857474
[file_basename] => test.txt
[file_extension] => txt
[file_dirname] => .
[file_name_no_ext] => test
[file1] => test.1.txt
[file2] => test.2.txt
[file_list] => Array
(
[0] => /tmp/xj0wh/test.2.txt
[1] => /tmp/xj0wh/test.txt
)
[file2_was_deleted] => 1
[my_dir_was_deleted] => 1
)
Class source code
Klassen-Quelltext
<?php
/**
* FileSystem operations (in general wrappers for already existing PHP functions)
* with exceptions. Implemented static functions are e.g. checking if a file,
* directory, link exists, recursively find files and folders with filtering,
* single command file I/O, file basename, dirname, extension, and retrieving
* system directories and temporary files.
* @gpackage de.atwillys.sw.php.swLib
* @author Stefan Wilhelm
* @copyright Stefan Wilhelm, 2007-2010
* @license GPL
* @version 1.0
* @uses FileSystemException
*/
namespace sw;
class FileSystem {
/**
* Searches for recursivly files and/or directories excluding defined whildcard
* patterns (separated with ";", e.g. "*.ignore1;*.ignore2".
* @param string $directory
* @param string $pattern
* @param string $ignore
* @param bool $onlyFiles
* @return array
*/
public static final function find($directory, $pattern='*', $ignore='', $onlyFiles=false) {
$ignore = (trim($ignore) == '') ? array() : explode(';', $ignore);
return self::glob_rr($directory, $pattern, $ignore, $onlyFiles);
}
private static final function glob_rr($directory, $pattern, $ignore, $onlyFiles) {
$result = array();
$directory = preg_replace('/(\*|\?|\[)/', '[$1]', rtrim($directory, '/'));
$files = @glob($directory . '/*', GLOB_NOSORT);
if (is_array($files) && !empty($files)) {
foreach ($files as $file) {
$doignore = false;
foreach ($ignore as $i) {
if (fnmatch($i, basename($file))) {
$doignore = true;
break;
}
}
if (!$doignore) {
if (is_dir($file)) {
if (!$onlyFiles && fnmatch($pattern, basename($file))) {
$result[] = $file;
}
$result = array_merge($result, self::glob_rr($file, $pattern, $ignore, $onlyFiles));
} else if (fnmatch($pattern, basename($file))) {
$result[] = $file;
}
}
}
}
return $result;
}
/**
* Returns the path for a temporary file. The file is already
* created when the method returns.
* @return string
*/
public static final function getTempFile() {
return tempnam('', 'php_fs_');
}
/**
* Returns the path for a possible temporary file. The file is NOT yet
* created when the method returns.
* @return string
*/
public static final function getTempFileName() {
return self::getTempDirectory() . '/php_fs_' . time() . '_' . uniqid() . '.tmp';
}
/**
* Returns the php temp directory
* @return string
*/
public static final function getTempDirectory() {
return rtrim(sys_get_temp_dir(), '/');
}
/**
* Creates a directory recursivly
* @param string $directory
* @param int $mode
*/
public static final function mkdir($directory, $mode=0770) {
$directory = trim($directory);
if (is_dir($directory)) {
throw new FileSystemException('The directory you want to create already exists');
} else if (is_file($directory)) {
throw new FileSystemException('The directory you want to create is already an existing file');
} else if (empty($directory)) {
throw new FileSystemException('You did not specify a directory to create (empty string given)');
} else if (!@mkdir($directory, $mode, true)) {
if (!is_writable(dirname($directory))) {
throw new FileSystemException('Failed to create directory (parent directory not writable for you)');
} else {
throw new FileSystemException('Failed to create directory');
}
}
}
/**
* Deletes a file or a directory. Directories are deleted RECURSIVLY.
* @param string $fileOrDirectory
*/
public static final function delete($fileOrDirectory) {
$fileOrDirectory = trim($fileOrDirectory);
if (is_file($fileOrDirectory)) {
if (!@unlink($fileOrDirectory)) {
if (!is_writable($fileOrDirectory)) {
throw new FileSystemException('Failed to delete file (not writable for you): ":file"', array(':file' => $fileOrDirectory));
} else {
throw new FileSystemException('Failed to delete file: ":file"', array(':file' => $fileOrDirectory));
}
}
} else if (is_dir($fileOrDirectory)) {
if (!is_writable($fileOrDirectory)) {
// This is to prevent deleting files and subfolders but not the
// directory itself. This can prevent mistakes.
throw new FileSystemException('Directory to delete is not writable for you: ":file"', array(':file' => $fileOrDirectory));
} else {
// Remove files
$list = self::find($fileOrDirectory, '*', '', true);
foreach ($list as $file) {
// use of this method again to get exceptions thrown
self::delete($file);
}
// Remove now empty subdirectories
$list = array_reverse(self::find($fileOrDirectory, '*', '', false));
foreach ($list as $dir) {
if (!rmdir($dir)) {
throw new FileSystemException('Failed to delete sub directory: ":dir"', array(':dir' => $dir));
}
}
// Remove the directory itself
if (!@rmdir($fileOrDirectory)) {
throw new FileSystemException('Failed to delete directory: ":dir"', array(':dir' => $fileOrDirectory));
}
}
} else {
throw new FileSystemException('File or directory to remove does not exist: ":dir"', array(':dir' => $fileOrDirectory));
}
}
/**
* Deletes a single file or link
* @param string $file
*/
public static function unlink($file) {
if (!@unlink($file)) {
$e = 'Failed to delete file ":file"';
if (!file_exists($file)) {
throw new FileSystemException("$e: No such file or link", array(':file' => $file));
} else if (!is_writable($file)) {
throw new FileSystemException("$e: Not writable.", array(':file' => $file));
} else {
throw new FileSystemException("$e", array(':file' => $file));
}
}
}
/**
* Renames a file or directory
* @param string $src
* @param string $dest
*/
public static function rename($src, $dest) {
if (!@rename($src, $dest)) {
$e = 'Failed to rename file ":src" to ":dest"';
if (!file_exists($src)) {
throw new FileSystemException("$e: No such file or directory.", array(':src' => $src, ':dest' => $dest));
} else if (!is_writable($src)) {
throw new FileSystemException("$e: Not writable.", array(':src' => $src, ':dest' => $dest));
} else {
throw new FileSystemException("$e", array(':src' => $src, ':dest' => $dest));
}
}
}
/**
* Moves a file or directory to another location (identical to rename, this
* function is only for the sake of nice exceptions.)
* @param string $src
* @param string $dest
*/
public static function move($src, $dest) {
if (!@rename($src, $dest)) {
$e = 'Failed to move file ":src" to ":dest"';
if (!file_exists($src)) {
throw new FileSystemException("$e: No such file or directory.", array(':src' => $src, ':dest' => $dest));
} else if (!is_writable($src)) {
throw new FileSystemException("$e: Not writable.", array(':src' => $src, ':dest' => $dest));
} else {
throw new FileSystemException("$e", array(':src' => $src, ':dest' => $dest));
}
}
}
/**
* Copies a file to another location
* @param string $src
* @param string $dest
* @param bool $recursive=false
*/
public static function copy($src, $dest, $recursive=false) {
$e = 'Failed to copy file ":src" to ":dest"';
if (is_file($src)) {
if (!@copy($src, $dest)) {
if (!is_readable($src)) {
throw new FileSystemException("$e: Source file not readable.", array(':src' => $src, ':dest' => $dest));
} else if (!is_dir(dirname($dest))) {
throw new FileSystemException("$e: Destination directory does not exist.", array(':src' => $src, ':dest' => $dest));
} else if (!file_exists($dest) && !is_writable($dest)) {
throw new FileSystemException("$e: Destination file cannot be overwritten because it is not writable for you.", array(':src' => $src, ':dest' => $dest));
} else {
throw new FileSystemException("$e", array(':src' => $src, ':dest' => $dest));
}
}
} else if (is_dir($src)) {
if (!$recursive) {
throw new FileSystemException("$e: Source is a directory and no recursive copying is specified", array(':src' => $src, ':dest' => $dest));
}
self::mkdir($dest);
foreach (scandir($src, SCANDIR_SORT_NONE) as $f) {
if ($f == '.' || $f == '..') {
continue;
} else if (is_dir($f)) {
self::copy("$src/$f", "$dest/$f", true);
} else {
self::copy("$src/$f", "$dest/$f", false);
}
}
} else {
throw new FileSystemException("$e: Source file/dirirectory does not exist.", array(':src' => $src, ':dest' => $dest));
}
}
/**
* Change directory
* @param string $path
*/
public static function chdir($path) {
if(!@chdir($path)) {
$e = "Could not change directory to :dir";
if(!is_dir($path)) {
throw new FileSystemException("$e: Does not exist.", array(':dir' => $path));
} else if(!is_readable($path)) {
throw new FileSystemException("$e: Not readable.", array(':dir' => $path));
} else {
throw new FileSystemException("$e", array(':dir' => $path));
}
}
}
/**
* Changes the file/directory mode
* @param string $fileOrDirectory
* @param int $mode
*/
public static function chmod($fileOrDirectory, $mode) {
if (!chmod($fileOrDirectory, $mode)) {
$e = 'Failed to change file mode of ":file" to ":mode"';
if (!file_exists($fileOrDirectory)) {
throw new FileSystemException("$e: No such file or directory.", array(':file' => $fileOrDirectory, ':mode' => decoct($mode)));
} else if (!is_writable($fileOrDirectory)) {
throw new FileSystemException("$e: Not writable.", array(':file' => $fileOrDirectory, ':mode' => decoct($mode)));
} else {
throw new FileSystemException("$e", array(':file' => $fileOrDirectory, ':mode' => decoct($mode)));
}
}
}
/**
* Changes the file/directory ower
* @param string $fileOrDirectory
* @param string $owner
*/
public static function chown($fileOrDirectory, $owner) {
if (!chown($fileOrDirectory, $owner)) {
$e = 'Failed to change file owner of ":file" to ":owner"';
if (!file_exists($fileOrDirectory)) {
throw new FileSystemException("$e: No such file or directory.", array(':file' => $fileOrDirectory, ':owner' => $owner));
} else if (!is_writable($fileOrDirectory)) {
throw new FileSystemException("$e: Not writable.", array(':file' => $fileOrDirectory, ':owner' => $owner));
} else {
throw new FileSystemException("$e", array(':file' => $fileOrDirectory, ':owner' => $owner));
}
}
}
/**
* Changes the file/directory group
* @param string $fileOrDirectory
* @param string $group
*/
public static function chgroup($fileOrDirectory, $group) {
if (!chgrp($fileOrDirectory, $group)) {
$e = 'Failed to change file group of ":file" to ":group"';
if (!file_exists($fileOrDirectory)) {
throw new FileSystemException("$e: No such file or directory.", array(':file' => $fileOrDirectory, ':group' => $group));
} else if (!is_writable($fileOrDirectory)) {
throw new FileSystemException("$e: Not writable.", array(':file' => $fileOrDirectory, ':group' => $group));
} else {
throw new FileSystemException("$e", array(':file' => $fileOrDirectory, ':group' => $group));
}
}
}
/**
* Modifies modification/access time of a file or directory
* @param string $fileOrDirectory
* @param int $mtime=null
* @param int $atime=null
*/
public static function touch($fileOrDirectory, $mtime=null, $atime=null) {
if($atime === null) $atime = fileatime($fileOrDirectory);
if($mtime === null) $mtime = filemtime($fileOrDirectory);
if(!@touch($fileOrDirectory, $time, $atime)) {
throw new FileSystemException("Failed to modify mtime/atime of file :file", array(':file' => $fileOrDirectory));
}
}
/**
* Returns if a file or directory exists
* @param string $fileOrDirectory
* @return bool
*/
public static function exists($fileOrDirectory) {
return file_exists($fileOrDirectory) ? true : false;
}
/**
* Returns if a directory exists
* @param string $fileOrDirectory
* @return bool
*/
public static function isDirectory($fileOrDirectory) {
return is_dir($fileOrDirectory) ? true : false;
}
/**
* Returns if a file exists
* @param string $fileOrDirectory
* @return bool
*/
public static function isFile($fileOrDirectory) {
return is_file($fileOrDirectory) ? true : false;
}
/**
* Returns if a file/directory is executable
* @param string $fileOrDirectory
* @return bool
*/
public static function isExecutable($fileOrDirectory) {
return is_executable($fileOrDirectory) ? true : false;
}
/**
* Returns if a file/directory is readable
* @param string $fileOrDirectory
* @return bool
*/
public static function isReadable($fileOrDirectory) {
return is_readable($fileOrDirectory) ? true : false;
}
/**
* Returns if a file/directory is writable
* @param string $fileOrDirectory
* @return bool
*/
public static function isWritable($fileOrDirectory) {
return is_writable($fileOrDirectory) ? true : false;
}
/**
* Returns if a file/directory path is a link
* @param string $fileOrDirectory
* @return bool
*/
public static function isLink($fileOrDirectory) {
return is_link($fileOrDirectory) ? true : false;
}
/**
* Returns the content of a file specified by its path.
* @param string $file
* @return string
*/
public static function & readFile($file) {
$hFile = @fopen($file, 'rb');
if (!$hFile) {
if (!is_string($file)) {
throw new FileSystemException('The name of the file to read is invalid (no text): :file', array(':file' => $file));
} else if (!is_file($file)) {
throw new FileSystemException('The file to read does not exist: :file', array(':file' => $file));
} else if (!is_readable($file)) {
throw new FileSystemException('The file to read is not readable for you: :file', array(':file' => $file));
} else {
throw new FileSystemException('Unknown error opening file: :file', array(':file' => $file));
}
} else {
$data = '';
while (!@feof($hFile)) {
$data .= @fread($hFile, 8192);
if ($data === false) {
@fclose($hFile);
throw new FileSystemException('Failed to read file: :file', array(':file' => $file));
}
}
fclose($hFile);
}
return $data;
}
/**
* Writes data in a file
* @param string $file
* @param mixed $data
*/
public static function writeFile($file, $data) {
if (@file_put_contents($file, $data, FILE_BINARY) === false) {
if (!is_writable($file)) {
throw new FileSystemException('File cannot be saved because it is not writable for you: :file', array(':file' => $file));
} else if (is_dir($file)) {
throw new FileSystemException('File cannot be saved because it already exists as a directory: :file', array(':file' => $file));
} else if (!is_dir(dirname($file))) {
throw new FileSystemException('File cannot be saved because the parent directory does not exist: :file', array(':file' => $file));
} else if (!is_writable(dirname($file)) || !is_executable(dirname($file))) {
throw new FileSystemException('File cannot be saved because the parent directory is not writable for you and the file would have to be created: :file', array(':file' => $file));
} else {
throw new FileSystemException('File could not be saved because an unknown error occurred: :file', array(':file' => $file));
}
}
}
/**
* Returns the extension of a file/directory
* @param string $filepath
* @return string
*/
public static function getExtension($filepath) {
return trim(pathinfo(basename($filepath), PATHINFO_EXTENSION));
}
/**
* Returns the basename of a file/directory and removed the suffix if specified
* @param string $filepath
* @param string $suffix
* @return string
*/
public static function getBasename($filepath, $suffix=null) {
return basename($filepath, $suffix);
}
/**
* Returns the directory name of a file/directory
* @param string $filepath
* @return string
*/
public static function getDirname($filepath) {
return dirname($filepath);
}
/**
* Returns the file size of a file in bytes
* @param string $filepath
* @return int
*/
public static function getFileSize($filepath) {
if (!is_file($filepath)) {
throw new FileSystemException('File to get the size of does not exist: :file', array(':file' => $filepath));
} else {
return filesize($filepath);
}
}
/**
* Returns the last modified timestamp of the file
* @param string $fileOrDorectory
* @return int
*/
public static function getLastModified($fileOrDorectory) {
$t = filemtime($fileOrDorectory);
if ($t === false) {
if (!file_exists($file)) {
throw new FileSystemException('The file to get last-modified from does not exist: :file', array(':file' => $fileOrDorectory));
} else if (!is_readable($file)) {
throw new FileSystemException('The file to get last-modified from is not readable for you: :file', array(':file' => $fileOrDorectory));
} else {
throw new FileSystemException('Unknown error getting last-modified: :file', array(':file' => $fileOrDorectory));
}
}
return $t;
}
/**
* Returns the file basename without the file extension / suffix
* @param string $filepath
* @return string
*/
public static function getFileNameWithoutExtension($filepath) {
$filepath = explode('.', trim($filepath));
if (count($filepath) == 2 && reset($filepath) == '') {
// This is a hidden .something file
} else if (count($filepath) > 1) {
array_pop($filepath);
}
$filepath = implode('.', $filepath);
return $filepath;
}
/**
* Get GID of a File or Directory
* @param string $fileOrDorectory
* @return int
* @throws FileSystemException
*/
public static function getUserId($fileOrDorectory) {
$stat = stat($fileOrDorectory);
if ($stat === false) {
if (!file_exists($file)) {
throw new FileSystemException('The file to get user id from does not exist: :file', array(':file' => $fileOrDorectory));
} else if (!is_readable($file)) {
throw new FileSystemException('The file to get user id from is not readable for you: :file', array(':file' => $fileOrDorectory));
} else {
throw new FileSystemException('Unknown error getting user id: :file', array(':file' => $fileOrDorectory));
}
}
return $stat[4];
}
/**
* Get GID of a File or Directory
* @param string $fileOrDorectory
* @return int
* @throws FileSystemException
*/
public static function getGroupId($fileOrDorectory) {
$stat = stat($fileOrDorectory);
if ($stat === false) {
if (!file_exists($file)) {
throw new FileSystemException('The file to get group from does not exist: :file', array(':file' => $fileOrDorectory));
} else if (!is_readable($file)) {
throw new FileSystemException('The file to get group from is not readable for you: :file', array(':file' => $fileOrDorectory));
} else {
throw new FileSystemException('Unknown error getting group: :file', array(':file' => $fileOrDorectory));
}
}
return $stat[5];
}
/**
* Returns the name of the group given by int $gid
* @staticvar array $grps
* @param int $path
* @return string
*/
public static function getGroupName($path) {
static $grps = array();
$path = self::getGroupId($path);
if(!isset($grps[$path])) $grps[$path] = ($g = @posix_getgrgid($path))===false ? $path : trim($g['name']);
return $grps[$path];
}
/**
* Returns the name of the user given by int $uid
* @staticvar array $usrs
* @param int $path
* @return string
*/
public static function getUserName($path) {
static $usrs = array();
$path = self::getUserId($path);
if(!isset($usrs[$path])) $usrs[$path] = ($u = @posix_getpwuid($path))===false ? $path : trim($u['name']);
return $usrs[$path];
}
/**
* Returns the file mode/permissions as string, e.g. '-rw-r--r--', 'lrwx-r--r--',
* 'drwxr-xr-x'.
* @staticvar string $tp
* @param string $file
* @return string
*/
public static function getFileModeString($file) {
static $tp = "upcddbb--llssuu";
$p = fileperms($file);
return $tp[($p>>12) & 0xf] .
(($p & 0x0100) ? 'r' : '-') . (($p & 0x0080) ? 'w' : '-') . (($p & 0x0040) ? (($p & 0x0800) ? 's' : 'x' ) : (($p & 0x0800) ? 'S' : '-')) .
(($p & 0x0020) ? 'r' : '-') . (($p & 0x0010) ? 'w' : '-') . (($p & 0x0008) ? (($p & 0x0400) ? 's' : 'x' ) : (($p & 0x0400) ? 'S' : '-')) .
(($p & 0x0004) ? 'r' : '-') . (($p & 0x0002) ? 'w' : '-') . (($p & 0x0001) ? (($p & 0x0200) ? 't' : 'x' ) : (($p & 0x0200) ? 'T' : '-'));
}
/**
* Returns the first file found defined by the array $fnmatch_patterns (which
* contains wildcard patterns). As the array is processed sequentially this
* method allows to priorise which file is returned. If no files are matched
* or on invalid input the method returns an empty string.
* NOTE: This method does NOT recurse through directories to save time,
* patterns like "/tmp/my-config/*.d/Makefile" will NOT work,
* but e.g. "/tmp/my-config/*.am" will work.
* @param array $fnmatch_patterns
* @param bool $case_insensitive
* @return string
*/
public static function getOneOf($fnmatch_patterns, $case_sensitive=true) {
if(is_string($fnmatch_patterns)) $fnmatch_patterns = array(0=>$fnmatch_patterns);
if(!is_array($fnmatch_patterns)) return '';
$flags = $case_sensitive ? 0 : FNM_CASEFOLD;
foreach($fnmatch_patterns as $pattern) {
$pt = basename($pattern);
$dn = strpos($pattern, '/') !== false ? @dirname($pattern) : @getcwd();
if(($dh = @opendir($dn)) !== false) {
while (($file = readdir($dh)) !== false) {
if($file == '.' || $file == '..') continue;
if(@fnmatch($pt, $file, $flags)) {
@closedir($dh); return "$dn/$file";
}
}
@closedir($dh);
}
}
return '';
}
}