: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* +-----------------> owner
* The isowner parameter is computed by the caller.
* If the owner parameter in the options is true, the user is the actual owner of all objects even if che user used in the ftp Login
* is different from the file owner id.
* If the owner parameter is false to understand if the user is the file owner we compare the ftp user with the file owner id.
* @param Boolean $isowner . Tell if the current user is the owner of the object.
* @author Dmitry (dio) Levashov
protected function parsePermissions($perm, $isowner = true)
for ($i = 0, $l = strlen($perm); $i < $l; $i++) {
$parts[] = substr($perm, $i, 1);
$read = ($isowner && $parts[1] == 'r') || $parts[4] == 'r' || $parts[7] == 'r';
'read' => $parts[0] == 'd' ? $read && (($isowner && $parts[3] == 'x') || $parts[6] == 'x' || $parts[9] == 'x') : $read,
'write' => ($isowner && $parts[2] == 'w') || $parts[5] == 'w' || $parts[8] == 'w'
* @param string $path dir path
* @author Dmitry Levashov
protected function cacheDir($path)
$this->dirsCache[$path] = array();
$encPath = $this->convEncIn($path);
foreach ($this->ftpRawList($encPath) as $raw) {
if (($stat = $this->parseRaw($raw, $encPath))) {
$list = $this->convEncOut($list);
$prefix = ($path === $this->separator) ? $this->separator : $path . $this->separator;
foreach ($list as $stat) {
$p = $prefix . $stat['name'];
if (isset($stat['target'])) {
$targets[$stat['name']] = $stat['target'];
$stat = $this->updateCache($p, $stat);
if (empty($stat['hidden'])) {
if (!$hasDir && $stat['mime'] === 'directory') {
$this->dirsCache[$path][] = $p;
foreach ($targets as $name => $target) {
$cacheDirTarget = $this->cacheDirTarget;
$this->cacheDirTarget = $this->convEncIn($target, true);
if ($tstat = $this->stat($target)) {
$stat['size'] = $tstat['size'];
$stat['alias'] = $target;
$stat['thash'] = $tstat['hash'];
$stat['mime'] = $tstat['mime'];
$stat['read'] = $tstat['read'];
$stat['write'] = $tstat['write'];
if (isset($tstat['ts'])) {
$stat['ts'] = $tstat['ts'];
if (isset($tstat['owner'])) {
$stat['owner'] = $tstat['owner'];
if (isset($tstat['group'])) {
$stat['group'] = $tstat['group'];
if (isset($tstat['perm'])) {
$stat['perm'] = $tstat['perm'];
if (isset($tstat['isowner'])) {
$stat['isowner'] = $tstat['isowner'];
$stat['mime'] = 'symlink-broken';
$this->cacheDirTarget = $cacheDirTarget;
$stat = $this->updateCache($p, $stat);
if (empty($stat['hidden'])) {
if (!$hasDir && $stat['mime'] === 'directory') {
$this->dirsCache[$path][] = $p;
if (isset($this->sessionCache['subdirs'])) {
$this->sessionCache['subdirs'][$path] = $hasDir;
* Return ftp transfer mode for file
* @param string $path file path
* @author Dmitry (dio) Levashov
protected function ftpMode($path)
return strpos($this->mimetype($path), 'text/') === 0 ? FTP_ASCII : FTP_BINARY;
/*********************** paths/urls *************************/
* Return parent directory path
* @param string $path file path
protected function _dirname($path)
$parts = explode($this->separator, trim($path, $this->separator));
return $this->separator . join($this->separator, $parts);
* @param string $path file path
protected function _basename($path)
$parts = explode($this->separator, trim($path, $this->separator));
return array_pop($parts);
* Join dir name and file name and retur full path
* @author Dmitry (dio) Levashov
protected function _joinPath($dir, $name)
return rtrim($dir, $this->separator) . $this->separator . $name;
* Return normalized path, this works the same as os.path.normpath() in Python
* @param string $path path
protected function _normpath($path)
// path must be start with /
$path = preg_replace('|^\.\/?|', $this->separator, $path);
$path = preg_replace('/^([^\/])/', "/$1", $path);
if ($path[0] === $this->separator) {
$initial_slashes = false;
&& (strpos($path, '//') === 0)
&& (strpos($path, '///') === false)) {
$initial_slashes = (int)$initial_slashes;
$comps = explode($this->separator, $path);
foreach ($comps as $comp) {
if (in_array($comp, array('', '.'))) {
|| (!$initial_slashes && !$new_comps)
|| ($new_comps && (end($new_comps) == '..'))) {
array_push($new_comps, $comp);
$path = implode($this->separator, $comps);
$path = str_repeat($this->separator, $initial_slashes) . $path;
return $path ? $path : '.';
* Return file path related to root dir
* @param string $path file path
* @author Dmitry (dio) Levashov
protected function _relpath($path)
if ($path === $this->root) {
if (strpos($path, $this->root) === 0) {
return ltrim(substr($path, strlen($this->root)), $this->separator);
* Convert path related to root dir into real path
* @param string $path file path
* @author Dmitry (dio) Levashov
protected function _abspath($path)
if ($path === $this->separator) {
if ($path[0] === $this->separator) {
return $this->_joinPath($this->root, $path);
* Return fake path started from root dir
* @param string $path file path
* @author Dmitry (dio) Levashov
protected function _path($path)
return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path));
* Return true if $path is children of $parent
* @param string $path path to check
* @param string $parent parent path
* @author Dmitry (dio) Levashov
protected function _inpath($path, $parent)
return $path == $parent || strpos($path, rtrim($parent, $this->separator) . $this->separator) === 0;
/***************** file stat ********************/
* Return stat for given path.
* Stat contains following fields:
* - (int) size file size in b. required
* - (int) ts file modification time in unix time. required
* - (string) mime mimetype. required for folders, others - optionally
* - (bool) read read permissions. required
* - (bool) write write permissions. required
* - (bool) locked is object locked. optionally
* - (bool) hidden is object hidden. optionally
* - (string) alias for symlinks - link target path relative to root path. optionally
* - (string) target for symlinks - link target path. optionally
* If file does not exists - returns empty array or false.
* @param string $path file path
* @author Dmitry (dio) Levashov
protected function _stat($path)
$outPath = $this->convEncOut($path);
if (isset($this->cache[$outPath])) {
return $this->convEncIn($this->cache[$outPath]);
if (!$this->MLSTsupprt) {
if ($path === $this->root) {
if ($this->needOnline && (($this->ARGS['cmd'] === 'open' && $this->ARGS['target'] === $this->encode($this->root)) || $this->isMyReload())) {
foreach ($this->ftpRawList($path) as $str) {
$info = preg_split('/\s+/', $str, 9);
if ($stat = $this->parseRaw(join(' ', $info), $path)) {
$res = array_merge($res, $stat);
if ($check && ($stat = $this->parseRaw($str, $path))) {
if (isset($stat['ts']) && !empty($stat['ts'])) {
$ts = max($ts, $stat['ts']);
if (isset($stat['dirs']) && $stat['mime'] === 'directory') {
$this->cache[$outPath] = $res;
$pPath = $this->_dirname($path);
if ($this->_inPath($pPath, $this->root)) {
$outPPpath = $this->convEncOut($pPath);
if (!isset($this->dirsCache[$outPPpath])) {
if (isset($this->sessionCache['subdirs']) && isset($this->sessionCache['subdirs'][$outPPpath])) {
$parentSubdirs = $this->sessionCache['subdirs'][$outPPpath];
$this->cacheDir($outPPpath);
$this->sessionCache['subdirs'][$outPPpath] = $parentSubdirs;
$stat = $this->convEncIn(isset($this->cache[$outPath]) ? $this->cache[$outPath] : array());
// dispose incomplete cache made by calling `stat` by 'startPath' option
$raw = ftp_raw($this->connect, 'MLST ' . $path);
if (is_array($raw) && count($raw) > 1 && substr(trim($raw[0]), 0, 1) == 2) {
$parts = explode(';', trim($raw[1]));
$parts = array_map('strtolower', $parts);
foreach ($parts as $part) {
list($key, $val) = explode('=', $part, 2);
if (strpos($val, 'dir') !== false) {
$stat['mime'] = 'directory';
} else if (strpos($val, 'link') !== false) {
$stat['mime'] = 'symlink';
$stat['mime'] = $this->mimetype($path);
$ts = mktime(intval(substr($val, 8, 2)), intval(substr($val, 10, 2)), intval(substr($val, 12, 2)), intval(substr($val, 4, 2)), intval(substr($val, 6, 2)), substr($val, 0, 4));
$stat['read'] = (int)preg_match('/e|l|r/', $val);
$stat['write'] = (int)preg_match('/w|m|c/', $val);
if (!preg_match('/f|d/', $val)) {
if (empty($stat['mime'])) {
// do not use MLST to get stat of symlink
if ($stat['mime'] === 'symlink') {
$this->MLSTsupprt = false;
$res = $this->_stat($path);
$this->MLSTsupprt = true;
if ($stat['mime'] === 'directory') {
$mode = substr($mode, 1);
for ($i = 0; $i <= 2; $i++) {
$perm[$i] = array(false, false, false);
$n = isset($mode[$i]) ? $mode[$i] : 0;