: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
const ERROR_EXTRACT_EXEC = 'errExtractExec';
const ERROR_FILE_NOT_FOUND = 'errFileNotFound'; // 'File not found.'
const ERROR_FTP_DOWNLOAD_FILE = 'errFtpDownloadFile';
const ERROR_FTP_MKDIR = 'errFtpMkdir';
const ERROR_FTP_UPLOAD_FILE = 'errFtpUploadFile';
const ERROR_INV_PARAMS = 'errCmdParams';
const ERROR_INVALID_DIRNAME = 'errInvDirname'; // 'Invalid folder name.'
const ERROR_INVALID_NAME = 'errInvName'; // 'Invalid file name.'
const ERROR_LOCKED = 'errLocked'; // '"$1" is locked and can not be renamed, moved or removed.'
const ERROR_MAX_TARGTES = 'errMaxTargets'; // 'Max number of selectable items is $1.'
const ERROR_MKDIR = 'errMkdir';
const ERROR_MKFILE = 'errMkfile';
const ERROR_MKOUTLINK = 'errMkOutLink'; // 'Unable to create a link to outside the volume root.'
const ERROR_MOVE = 'errMove';
const ERROR_NETMOUNT = 'errNetMount';
const ERROR_NETMOUNT_FAILED = 'errNetMountFailed';
const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver';
const ERROR_NETUNMOUNT = 'errNetUnMount';
const ERROR_NOT_ARCHIVE = 'errNoArchive';
const ERROR_NOT_DIR = 'errNotFolder';
const ERROR_NOT_FILE = 'errNotFile';
const ERROR_NOT_REPLACE = 'errNotReplace'; // Object "$1" already exists at this location and can not be replaced with object of another type.
const ERROR_NOT_UTF8_CONTENT = 'errNotUTF8Content';
const ERROR_OPEN = 'errOpen';
const ERROR_PERM_DENIED = 'errPerm';
const ERROR_REAUTH_REQUIRE = 'errReauthRequire'; // 'Re-authorization is required.'
const ERROR_RENAME = 'errRename';
const ERROR_REPLACE = 'errReplace'; // 'Unable to replace "$1".'
const ERROR_RESIZE = 'errResize';
const ERROR_RESIZESIZE = 'errResizeSize';
const ERROR_RM = 'errRm'; // 'Unable to remove "$1".'
const ERROR_RM_SRC = 'errRmSrc'; // 'Unable remove source file(s)'
const ERROR_SAVE = 'errSave';
const ERROR_SEARCH_TIMEOUT = 'errSearchTimeout'; // 'Timed out while searching "$1". Search result is partial.'
const ERROR_SESSION_EXPIRES = 'errSessionExpires';
const ERROR_TRGDIR_NOT_FOUND = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.'
const ERROR_UNKNOWN = 'errUnknown';
const ERROR_UNKNOWN_CMD = 'errUnknownCmd';
const ERROR_UNSUPPORT_TYPE = 'errUsupportType';
const ERROR_UPLOAD = 'errUpload'; // 'Upload error.'
const ERROR_UPLOAD_FILE = 'errUploadFile'; // 'Unable to upload "$1".'
const ERROR_UPLOAD_FILE_MIME = 'errUploadMime'; // 'File type not allowed.'
const ERROR_UPLOAD_FILE_SIZE = 'errUploadFileSize'; // 'File exceeds maximum allowed size.'
const ERROR_UPLOAD_NO_FILES = 'errUploadNoFiles'; // 'No files found for upload.'
const ERROR_UPLOAD_TEMP = 'errUploadTemp'; // 'Unable to make temporary file for upload.'
const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize'; // 'Data exceeds the maximum allowed size.'
const ERROR_UPLOAD_TRANSFER = 'errUploadTransfer'; // '"$1" transfer error.'
const ERROR_MAX_MKDIRS = 'errMaxMkdirs'; // 'You can create up to $1 folders at one time.'
* @param array elFinder and roots configurations
* @author Dmitry (dio) Levashov
public function __construct($opts)
if (version_compare(PHP_VERSION, '5.6', '>=')) {
if (($_val = ini_get('iconv.internal_encoding')) && strtoupper($_val) !== 'UTF-8') {
ini_set('iconv.internal_encoding', '');
if (($_val = ini_get('mbstring.internal_encoding')) && strtoupper($_val) !== 'UTF-8') {
ini_set('mbstring.internal_encoding', '');
if (($_val = ini_get('internal_encoding')) && strtoupper($_val) !== 'UTF-8') {
ini_set('internal_encoding', '');
if (function_exists('iconv_set_encoding') && strtoupper(iconv_get_encoding('internal_encoding')) !== 'UTF-8') {
iconv_set_encoding('internal_encoding', 'UTF-8');
if (function_exists('mb_internal_encoding') && strtoupper(mb_internal_encoding()) !== 'UTF-8') {
mb_internal_encoding('UTF-8');
ini_set('default_charset', 'UTF-8');
// define accept constant of server commands path
!defined('ELFINDER_TAR_PATH') && define('ELFINDER_TAR_PATH', 'tar');
!defined('ELFINDER_GZIP_PATH') && define('ELFINDER_GZIP_PATH', 'gzip');
!defined('ELFINDER_BZIP2_PATH') && define('ELFINDER_BZIP2_PATH', 'bzip2');
!defined('ELFINDER_XZ_PATH') && define('ELFINDER_XZ_PATH', 'xz');
!defined('ELFINDER_ZIP_PATH') && define('ELFINDER_ZIP_PATH', 'zip');
!defined('ELFINDER_UNZIP_PATH') && define('ELFINDER_UNZIP_PATH', 'unzip');
!defined('ELFINDER_RAR_PATH') && define('ELFINDER_RAR_PATH', 'rar');
// Create archive in RAR4 format even when using RAR5 library (true or false)
!defined('ELFINDER_RAR_MA4') && define('ELFINDER_RAR_MA4', false);
!defined('ELFINDER_UNRAR_PATH') && define('ELFINDER_UNRAR_PATH', 'unrar');
!defined('ELFINDER_7Z_PATH') && define('ELFINDER_7Z_PATH', (substr(PHP_OS, 0, 3) === 'WIN') ? '7z' : '7za');
!defined('ELFINDER_CONVERT_PATH') && define('ELFINDER_CONVERT_PATH', 'convert');
!defined('ELFINDER_IDENTIFY_PATH') && define('ELFINDER_IDENTIFY_PATH', 'identify');
!defined('ELFINDER_EXIFTRAN_PATH') && define('ELFINDER_EXIFTRAN_PATH', 'exiftran');
!defined('ELFINDER_JPEGTRAN_PATH') && define('ELFINDER_JPEGTRAN_PATH', 'jpegtran');
!defined('ELFINDER_FFMPEG_PATH') && define('ELFINDER_FFMPEG_PATH', 'ffmpeg');
!defined('ELFINDER_DISABLE_ZIPEDITOR') && define('ELFINDER_DISABLE_ZIPEDITOR', false);
// enable(true)/disable(false) handling postscript on ImageMagick
// Should be `false` as long as there is a Ghostscript vulnerability
// see https://artifex.com/news/ghostscript-security-resolved/
!defined('ELFINDER_IMAGEMAGICK_PS') && define('ELFINDER_IMAGEMAGICK_PS', false);
$this->version = (string)self::$ApiVersion;
// set error handler of WARNING, NOTICE
$errLevel = E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_STRICT | E_RECOVERABLE_ERROR;
if (defined('E_DEPRECATED')) {
$errLevel |= E_DEPRECATED | E_USER_DEPRECATED;
set_error_handler('elFinder::phpErrorHandler', $errLevel);
// Associative array of file pointers to close at the end of script: ['temp file pointer' => true]
$GLOBALS['elFinderTempFps'] = array();
// Associative array of files to delete at the end of script: ['temp file path' => true]
$GLOBALS['elFinderTempFiles'] = array();
// regist Shutdown function
register_shutdown_function(array('elFinder', 'onShutdown'));
// convert PATH_INFO to GET query
if (!empty($_SERVER['PATH_INFO'])) {
$_ps = explode('/', trim($_SERVER['PATH_INFO'], '/'));
if (!isset($_GET['cmd'])) {
if (isset($this->commands[$_cmd])) {
foreach (array_keys($this->commands[$_cmd]) as $_k) {
if (!isset($_GET[$_k])) {
$_GET[$_k] = $_ps[$_i++];
elFinder::$instance = $this;
$this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false);
error_reporting(defined('ELFINDER_DEBUG_ERRORLEVEL') ? ELFINDER_DEBUG_ERRORLEVEL : -1);
ini_set('display_errors', '1');
// clear output buffer and stop output filters
while (ob_get_level() && ob_end_clean()) {
if (!interface_exists('elFinderSessionInterface')) {
include_once dirname(__FILE__) . '/elFinderSessionInterface.php';
if (!empty($opts['session']) && $opts['session'] instanceof elFinderSessionInterface) {
$this->session = $opts['session'];
'base64encode' => !empty($opts['base64encodeSessionData']),
'default' => !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches',
'netvolume' => !empty($opts['netVolumesSessionKey']) ? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes'
if (!class_exists('elFinderSession')) {
include_once dirname(__FILE__) . '/elFinderSession.php';
$this->session = new elFinderSession($sessionOpts);
// try session start | restart
// 'netmount' added to handle requests synchronously on unmount
$sessionUseCmds = array('netmount');
if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) {
$sessionUseCmds = array_merge($sessionUseCmds, $opts['sessionUseCmds']);
// set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart"
if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) {
self::$volumesCnt = $volumesCntStart;
$this->time = $this->utime();
$this->sessionCloseEarlier = isset($opts['sessionCloseEarlier']) ? (bool)$opts['sessionCloseEarlier'] : true;
$this->sessionUseCmds = array_flip($sessionUseCmds);
$this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0);
$this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : '');
$this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : '');
$this->maxTargets = (isset($opts['maxTargets']) ? intval($opts['maxTargets']) : $this->maxTargets);
elFinder::$commonTempPath = (isset($opts['commonTempPath']) ? realpath($opts['commonTempPath']) : dirname(__FILE__) . '/.tmp');
if (!is_writable(elFinder::$commonTempPath)) {
elFinder::$commonTempPath = sys_get_temp_dir();
if (!is_writable(elFinder::$commonTempPath)) {
elFinder::$commonTempPath = '';
if (isset($opts['connectionFlagsPath']) && is_writable($opts['connectionFlagsPath'] = realpath($opts['connectionFlagsPath']))) {
elFinder::$connectionFlagsPath = $opts['connectionFlagsPath'];
elFinder::$connectionFlagsPath = elFinder::$commonTempPath;
if (!empty($opts['tmpLinkPath'])) {
elFinder::$tmpLinkPath = realpath($opts['tmpLinkPath']);
if (!empty($opts['tmpLinkUrl'])) {
elFinder::$tmpLinkUrl = $opts['tmpLinkUrl'];
if (!empty($opts['tmpLinkLifeTime'])) {
elFinder::$tmpLinkLifeTime = $opts['tmpLinkLifeTime'];
if (!empty($opts['textMimes']) && is_array($opts['textMimes'])) {
elfinder::$textMimes = $opts['textMimes'];
if (!empty($opts['urlUploadFilter'])) {
$this->urlUploadFilter = $opts['urlUploadFilter'];
$this->maxArcFilesSize = isset($opts['maxArcFilesSize']) ? intval($opts['maxArcFilesSize']) : 0;
$this->optionsNetVolumes = (isset($opts['optionsNetVolumes']) && is_array($opts['optionsNetVolumes'])) ? $opts['optionsNetVolumes'] : array();
if (isset($opts['itemLockExpire'])) {
$this->itemLockExpire = intval($opts['itemLockExpire']);
if (!empty($opts['uploadAllowedLanIpClasses'])) {
$this->uploadAllowedLanIpClasses = array_flip($opts['uploadAllowedLanIpClasses']);
$this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey']) ? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes';
self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches';
$_optsMD5 = md5(json_encode($opts['roots']));
if ($this->session->get('_optsMD5') !== $_optsMD5) {
$this->session->set('_optsMD5', $_optsMD5);
// setlocale and global locale regists to elFinder::locale
self::$locale = !empty($opts['locale']) ? $opts['locale'] : (substr(PHP_OS, 0, 3) === 'WIN' ? 'C' : 'en_US.UTF-8');
if (false === setlocale(LC_ALL, self::$locale)) {
self::$locale = setlocale(LC_ALL, '0');
elFinder::$defaultMimefile = isset($opts['defaultMimefile']) ? $opts['defaultMimefile'] : '';
elFinder::$memoryLimitGD = isset($opts['memoryLimitGD']) ? $opts['memoryLimitGD'] : 0;
// set flag of throwErrorOnExec
// `true` need `try{}` block for `$connector->run();`
$this->throwErrorOnExec = !empty($opts['throwErrorOnExec']);
elFinder::$archivers = isset($opts['archivers']) && is_array($opts['archivers']) ? $opts['archivers'] : array();
if (isset($opts['utf8Encoder']) && is_callable($opts['utf8Encoder'])) {
$this->utf8Encoder = $opts['utf8Encoder'];
// for LocalFileSystem driver on Windows server
if (DIRECTORY_SEPARATOR !== '/') {
if (empty($opts['bind'])) {
$_key = 'upload.pre mkdir.pre mkfile.pre rename.pre archive.pre ls.pre';
if (!isset($opts['bind'][$_key])) {
$opts['bind'][$_key] = array();
array_push($opts['bind'][$_key], 'Plugin.WinRemoveTailDots.cmdPreprocess');
$_key = 'upload.presave paste.copyfrom';
if (!isset($opts['bind'][$_key])) {
$opts['bind'][$_key] = array();
array_push($opts['bind'][$_key], 'Plugin.WinRemoveTailDots.onUpLoadPreSave');
if (!empty($opts['bind']) && is_array($opts['bind'])) {
$_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET;
$_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : '';
foreach ($opts['bind'] as $cmd => $handlers) {
$doRegist = (strpos($cmd, '*') !== false);
$doRegist = ($_reqCmd && in_array($_reqCmd, array_map('elFinder::getCmdOfBind', explode(' ', $cmd))));
// for backward compatibility
if (!is_array($handlers)) {
$handlers = array($handlers);
if (count($handlers) === 2 && is_callable($handlers)) {
$handlers = array($handlers);
foreach ($handlers as $handler) {
if (is_string($handler) && strpos($handler, '.')) {
list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, '');
if (strcasecmp($_domain, 'plugin') === 0) {
if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name]) ? $opts['plugin'][$_name] : array())
and method_exists($plugin, $_method)) {
$this->bind($cmd, array($plugin, $_method));
$this->bind($cmd, $handler);
if (!isset($opts['roots']) || !is_array($opts['roots'])) {
$opts['roots'] = array();
// try to enable elFinderVolumeFlysystemZipArchiveNetmount to zip editing
if (empty(elFinder::$netDrivers['ziparchive'])) {
elFinder::$netDrivers['ziparchive'] = 'FlysystemZipArchiveNetmount';
// check for net volumes stored in session
$netVolumes = $this->getNetVolumes();
foreach ($netVolumes as $key => $root) {
if (!isset($root['id'])) {
if (!$root['id'] = $this->getNetVolumeUniqueId($netVolumes)) {
$this->mountErrors[] = 'Netmount Driver "' . $root['driver'] . '" : Could\'t given volume id.';
$root['_isNetVolume'] = true;
$opts['roots'][$key] = $root;
foreach ($opts['roots'] as $i => $o) {
$class = 'elFinderVolume' . (isset($o['driver']) ? $o['driver'] : '');
if (class_exists($class)) {
/* @var elFinderVolumeDriver $volume */
if ($this->maxArcFilesSize && (empty($o['maxArcFilesSize']) || $this->maxArcFilesSize < $o['maxArcFilesSize'])) {
$o['maxArcFilesSize'] = $this->maxArcFilesSize;
$volume->setSession($this->session);
$volume->setNeedOnline(true);
if ($volume->mount($o)) {
// unique volume id (ends on "_") - used as prefix to files hash
$this->volumes[$id] = $volume;
if ((!$this->default || $volume->root() !== $volume->defaultPath()) && $volume->isReadable()) {
$this->default = $volume;
if (!empty($o['_isNetVolume'])) {
$this->removeNetVolume($i, $volume);
$this->mountErrors[] = 'Driver "' . $class . '" : ' . implode(' ', $volume->error());
if (!empty($o['_isNetVolume'])) {
$this->removeNetVolume($i, $volume);
$this->mountErrors[] = 'Driver "' . $class . '" : ' . $e->getMessage();
if (!empty($o['_isNetVolume'])) {
$this->removeNetVolume($i, $volume);
$this->mountErrors[] = 'Driver "' . $class . '" does not exist';
// if at least one readable volume - ii desu >_<
$this->loaded = !empty($this->default);
// restore error handler for now
* Return elFinder session wrapper instance
* @return elFinderSessionInterface
public function getSession()
* Return true if fm init correctly
* @author Dmitry (dio) Levashov
* Return version (api) number
* @author Dmitry (dio) Levashov
public function version()
return self::$ApiVersion;
* Return revision (api) number
public function revision()
return self::$ApiRevision;
* Add handler to elFinder command
* @param string command name
* @param string|array callback name or array(object, method)
* @author Dmitry (dio) Levashov
public function bind($cmd, $handler)
$allCmds = array_keys($this->commands);
foreach (explode(' ', $cmd) as $_cmd) {
if ($all = strpos($_cmd, '*') !== false) {
list(, $sub) = array_pad(explode('.', $_cmd), 2, '');
$sub = str_replace('\'', '\\\'', $sub);
$subs = array_fill(0, count($allCmds), $sub);
$cmds = array_merge($cmds, array_map(array('elFinder', 'addSubToBindName'), $allCmds, $subs));
$cmds = array_merge($cmds, $allCmds);
$cmds = array_unique($cmds);
foreach ($cmds as $cmd) {
if (!isset($this->listeners[$cmd])) {
$this->listeners[$cmd] = array();
if (is_callable($handler)) {
$this->listeners[$cmd][] = $handler;
* Remove event (command exec) handler
* @param string command name
* @param string|array callback name or array(object, method)
* @author Dmitry (dio) Levashov
public function unbind($cmd, $handler)
if (!empty($this->listeners[$cmd])) {
foreach ($this->listeners[$cmd] as $i => $h) {