Source for file FTP.php
Documentation is available at FTP.php
// +----------------------------------------------------------------------+
// | Net_FTP Version 1.3 |
// +----------------------------------------------------------------------+
// | Copyright (c) 2001-2004 Tobias Schlitt |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license, |
// | that is available at through the world-wide-web at |
// | http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Tobias Schlitt <toby@php.net> |
// +----------------------------------------------------------------------+
// $Id: FTP.php,v 1.19 2004/05/25 18:38:44 toby Exp $
define("NET_FTP_FILES_ONLY", 0 , true );
define("NET_FTP_DIRS_ONLY", 1 , true );
define("NET_FTP_DIRS_FILES", 2 , true );
define("NET_FTP_RAWLIST", 3 , true );
* Class for comfortable FTP-communication
* This class provides comfortable communication with FTP-servers. You may do everything
* enabled by the PHP-FTP-extension and further functionalities, like recursive-deletion,
* -up- and -download. Another feature is to create directories recursively.
* @author Tobias Schlitt <toby@php.net>
* @see http://www.schlitt.info
* @license http://www.php.net/license/3_0.txt PHP License 3.0
class Net_FTP extends PEAR
* The port for ftp-connection (standard is 21)
* Determine whether to use passive-mode (true) or active-mode (false)
* The standard mode for ftp-transfer
* This holds the handle for the ftp-connection
* Saves file-extensions for ascii- and binary-mode
* The array contains 2 sub-arrays ("ascii" and "binary"), which both contain
* file-extensions without the "." (".php" = "php").
* Matches the ls entries against a regex and maps the resulting array to speaking names
'pattern' => '/(?:(d)|.)([rwxt-]+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\S+\s+\S+\s+\S+)\s+(.+)/',
'map' => array ('name'=>8 ,'size'=>6 ,'rights'=>2 ,'user'=>4 ,'group'=>5 ,
'files_inside'=>3 ,'date'=>7 ,'is_dir'=>1 )
'pattern' => '/(.+)\s+(.+)\s+((<DIR>)|[0-9]+)\s+(.+)/',
'map' => array ('name'=>5 ,'date'=>1 ,'is_dir'=>3 )
* Holds all Net_FTP_Observer objects
* that wish to be notified of new messages.
var $_listeners = array ();
* This generates a new FTP-Object. The FTP-connection will not be established, yet.
* You can leave $host and $port blank, if you want. The $host will not be set
* and the $port will be left at 21. You have to set the $host manualy before
* @param string $host (optional) The hostname
* @param int $port (optional) The port
function Net_FTP ($host = null , $port = null )
$this->setHostname ($host);
$this->_file_extensions[FTP_ASCII ] = array ();
$this->_file_extensions[FTP_BINARY ] = array ();
* This function generates the FTP-connection. You can optionally define a
* hostname and/or a port. If you do so, this data is stored inside the object.
* @param string $host (optional) The Hostname
* @param int $port (optional) The Port
* @return mixed True on success, otherwise PEAR::Error
function connect ($host = null , $port = null )
$this->setHostname ($host);
$handle = @ftp_connect($this->getHostname (), $this->getPort ());
return $this->raiseError ("Connection to host failed", 0 );
$this->_handle = & $handle;
* This function close the FTP-connection
* This logges you into the ftp-server. You are free to specify username and password
* in this method. If you specify it, the values will be taken into the corresponding
* attributes, if do not specify, the attributes are taken.
* @param string $username (optional) The username to use
* @param string $password (optional) The password to use
* @return mixed True on success, otherwise PEAR::Error
function login ($username = null , $password = null )
$username = $this->getUsername ();
$this->setUsername ($username);
$password = $this->getPassword ();
$this->setPassword ($password);
$res = @ftp_login($this->_handle, $username, $password);
return $this->raiseError ("Unable to login", 0 );
* This changes the currently used directory. You can use either an absolute
* directory-path (e.g. "/home/blah") or a relative one (e.g. "../test").
* @param string $dir The directory to go to.
* @return mixed True on success, otherwise PEAR::Error
return $this->raiseError ("Directory change failed", 2 );
* Show's you the actual path on the server
* This function questions the ftp-handle for the actual selected path and returns it.
* @return mixed The actual path or PEAR::Error
return $this->raiseError ("Could not determine the actual path.", 0 );
* This works similar to the mkdir-command on your local machine. You can either give
* it an absolute or relative path. The relative path will be completed with the actual
* selected server-path. (see: pwd())
* @param string $dir Absolute or relative dir-path
* @param bool $recursive (optional) Create all needed directories
* @return mixed True on success, otherwise PEAR::Error
function mkdir ($dir, $recursive = false )
$dir = $this->_construct_path ($dir);
if ($recursive === false ){
return $this->raiseError (" Creation of '$dir' failed" , 0 );
if(strpos($dir, '/') === false ) {
return $this->mkdir ($dir,false );
while (false !== ($pos = strpos($dir, '/', $pos + 1 ))){
$elements[] = substr($dir, 0 , $pos);
foreach ($elements as $element){
$res = $this->mkdir ($element, false );
* This method tries executing a command on the ftp, using SITE EXEC.
* @param string $command The command to execute
* @return mixed The result of the command (if successfull), otherwise PEAR::Error
function execute ($command)
$res = @ftp_exec($this->_handle, $command);
return $this->raiseError (" Execution of command '$command' failed." , 0 );
* Execute a SITE command on the server
* This method tries to execute a SITE command on the ftp server.
* @param string $command The command with parameters to execute
* @return mixed True if successful, otherwise PEAR::Error
$res = @ftp_site($this->_handle, $command);
return $this->raiseError (" Execution of SITE command '$command' failed." , 0 );
* This method will try to chmod the file specified on the server
* Currently, you must give a number as the the permission argument (777 or
* similar). The file can be either a relative or absolute path.
* NOTE: Some servers do not support this feature. In that case, you will
* get a PEAR error object returned. If successful, the method returns true
* @param mixed $target The file or array of files to set permissions for
* @param integer $permissions The mode to set the file permissions to
* @return mixed True if successful, otherwise PEAR::Error
function chmod ($target, $permissions)
// If $target is an array: Loop through it.
for ($i = 0; $i < count($target); $i++ ) {
$res = $this->chmod ($target[$i], $permissions);
if (PEAR ::isError ($res)) {
} // end for i < count($target)
$res = $this->site ("CHMOD " . $permissions . " " . $target);
return PEAR ::raiseError ("CHMOD " . $permissions . " " . $target . " failed", 0 , PEAR_ERROR_RETURN );
* This method will try to chmod a folder and all of its contents
* on the server. The target argument must be a folder or an array of folders
* and the permissions argument have to be an integer (i.e. 777).
* The file can be either a relative or absolute path.
* NOTE: Some servers do not support this feature. In that case, you
* will get a PEAR error object returned. If successful, the method
* @param mixed $target The folder or array of folders to
* @param integer $permissions The mode to set the folder
* and file permissions to
* @return mixed True if successful, otherwise PEAR::Error
function chmodRecursive ($target, $permissions)
if(!isset ($dir_permissions)){ // Making directory specific permissions
$dir_permissions = $this->makeDirPermissions ($permissions);
// If $target is an array: Loop through it
for ($i = 0; $i < count($target); $i++ ) {
$res = $this->chmodRecursive ($target[$i], $permissions);
if (PEAR ::isError ($res)) {
} // end for i < count($target)
$remote_path = $this->_construct_path ($target);
// Chmod the directory itself
$result = $this->chmod ($remote_path, $dir_permissions);
if (PEAR ::isError ($result)) {
// If $remote_path last character is not a slash, add one
$dir_list = $this->ls ($remote_path, $mode);
foreach ($dir_list as $dir_entry) {
$remote_path_new = $remote_path. $dir_entry["name"]. "/";
// Chmod the directory we're about to enter
$result = $this->chmod ($remote_path_new, $dir_permissions);
if (PEAR ::isError ($result)) {
$result = $this->chmodRecursive ($remote_path_new, $permissions);
if (PEAR ::isError ($result)) {
} // end foreach dir_list as dir_entry
$file_list = $this->ls ($remote_path, $mode);
foreach ($file_list as $file_entry) {
$remote_file = $remote_path. $file_entry["name"];
$result = $this->chmod ($remote_file, $permissions);
if (PEAR ::isError ($result)) {
} // end foreach $file_list
return true; // No errors
} // end method chmodRecursive
* Rename or move a file or a directory from the ftp-server
* @param string $remote_from The remote file or directory original to rename or move
* @param string $remote_to The remote file or directory final to rename or move
* @return bool $res True on success, otherwise PEAR::Error
function rename ($remote_from, $remote_to)
$res = @ftp_rename($this->_handle, $remote_from, $remote_to);
return $this->raiseError ("Could not rename ". $remote_from. " to ". $remote_to. " !", 0 );
* This will return logical permissions mask for directory.
* if directory have to be writeable it have also be executable
* @param string $permissions File permissions in digits for file (i.e. 666)
* @return string File permissions in digits for directory (i.e. 777)
function makeDirPermissions ($permissions){
$permissions = (string) $permissions;
for($i = 0; $i < strlen($permissions); $i++ ){ // going through (user, group, world)
if((int) $permissions{$i} & 4 and !((int) $permissions{$i} & 1 )){ // Read permission is set
(int) $permissions{$i} = (int) $permissions{$i} + 1; // Adding execute flag
return (string) $permissions;
* This will return the last modification-time of a file. You can either give this
* function a relative or an absolute path to the file to check.
* NOTE: Some servers will not support this feature and the function works
* only on files, not directories! When successful,
* it will return the last modification-time as a unix-timestamp or, when $format is
* specified, a preformated timestring.
* @param string $file The file to check
* @param string $format (optional) The format to give the date back
* if not set, it will return a Unix timestamp
* @return mixed Unix timestamp, a preformated date-string or PEAR::Error
function mdtm ($file, $format = null )
$file = $this->_construct_path ($file);
if ($this->_check_dir ($file)) {
return $this->raiseError (" Filename '$file' seems to be a directory." , 0 );
$res = @ftp_mdtm($this->_handle, $file);
return $this->raiseError (" Could not get last-modification-date of '$file'." , 0 );
$res = date($format, $res);
return $this->raiseError (" Date-format failed on timestamp '$res'." , 0 );
* This will return the size of a given file in bytes. You can either give this function
* a relative or an absolute file-path. NOTE: Some servers do not support this feature!
* @param string $file The file to check
* @return mixed Size in bytes or PEAR::Error
$file = $this->_construct_path ($file);
$res = @ftp_size($this->_handle, $file);
return $this->raiseError (" Could not determine filesize of '$file'." , 0 );
* This method returns a directory-list of the current directory or given one.
* To display the current selected directory, simply set the first parameter to null
* or leave it blank, if you do not want to use any other parameters.
* There are 4 different modes of listing directories. Either to list only
* the files (using NET_FTP_FILES_ONLY), to list only directories (using
* NET_FTP_DIRS_ONLY) or to show both (using NET_FTP_DIRS_FILES, which is default).
* The 4th one is the NET_FTP_RAWLIST, which returns just the array created by the
* ftp_rawlist()-function build into PHP.
* The other function-modes will return an array containing the requested data.
* The files and dirs are listed in human-sorted order, but if you select
* NET_FTP_DIRS_FILES the directories will be added above the files,
* but although both sorted.
* All elements in the arrays are associative arrays themselves. The have the following
* ["name"] => string The name of the directory<BR>
* ["rights"] => string The rights of the directory (in style "rwxr-xr-x")<BR>
* ["user"] => string The owner of the directory<BR>
* ["group"] => string The group-owner of the directory<BR>
* ["files_inside"]=> string The number of files/dirs inside the directory
* excluding "." and ".."<BR>
* ["date"] => int The creation-date as Unix timestamp<BR>
* ["is_dir"] => bool true, cause this is a dir<BR>
* ["name"] => string The name of the file<BR>
* ["size"] => int Size in bytes<BR>
* ["rights"] => string The rights of the file (in style "rwxr-xr-x")<BR>
* ["user"] => string The owner of the file<BR>
* ["group"] => string The group-owner of the file<BR>
* ["date"] => int The creation-date as Unix timestamp<BR>
* ["is_dir"] => bool false, cause this is a file<BR>
* @param string $dir (optional) The directory to list or null, when listing the current directory.
* @param int $mode (optional) The mode which types to list (files, directories or both).
* @return mixed The directory list as described above or PEAR::Error on failure.
function ls ($dir = null , $mode = NET_FTP_DIRS_FILES )
return $this->raiseError ("Could not retrieve current directory", 4 );
* This method will delete the given file or directory ($path) from the server
* Whether the given string is a file or directory is only determined by the last
* sign inside the string ("/" or not).
* If you specify a directory, you can optionally specify $recursive as true,
* to let the directory be deleted recursive (with all sub-directories and files
* You can either give a absolute or relative path for the file / dir. If you choose to
* use the relative path, it will be automatically completed with the actual
* @param string $path The absolute or relative path to the file / directory.
* @param bool $recursive (optional)
* @return mixed True on success, otherwise PEAR::Error
function rm ($path, $recursive = false )
$path = $this->_construct_path ($path);
if ($this->_check_dir ($path)) {
return $this->_rm_dir_recursive ($path);
return $this->_rm_dir ($path);
return $this->_rm_file ($path);
* This function will download a file from the ftp-server. You can either spcify a absolute
* path to the file (beginning with "/") or a relative one, which will be completed
* with the actual directory you selected on the server. You can specify
* the path to which the file will be downloaded on the local
* maschine, if the file should be overwritten if it exists (optionally, default is
* no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
* downloaded (if you do not specify this, the method tries to determine it automatically
* from the mode-directory or uses the default-mode, set by you). If you give a relative
* path to the local-file, the script-path is used as basepath.
* @param string $remote_file The absolute or relative path to the file to download
* @param string $local_file The local file to put the downloaded in
* @param bool $overwrite (optional) Whether to overwrite existing file
* @param int $mode (optional) Either FTP_ASCII or FTP_BINARY
* @return mixed True on success, otherwise PEAR::Error
function get ($remote_file, $local_file, $overwrite = false , $mode = null )
$mode = $this->checkFileExtension ($remote_file);
$remote_file = $this->_construct_path ($remote_file);
return $this->raiseError (" Local file '$local_file' exists and may not be overwriten." , 0 );
return $this->raiseError (" Local file '$local_file' is not writeable. Can not overwrite." , 0 );
$res = @ftp_nb_get($this->_handle, $local_file, $remote_file, $mode);
while ($res == FTP_MOREDATA ) {
$this->_announce ('nb_get');
$res = @ftp_get($this->_handle, $local_file, $remote_file, $mode);
return $this->raiseError (" File '$remote_file' could not be downloaded to '$local_file'." , 0 );
* This function will upload a file to the ftp-server. You can either specify a absolute
* path to the remote-file (beginning with "/") or a relative one, which will be completed
* with the actual directory you selected on the server. You can specify
* the path from which the file will be uploaded on the local
* maschine, if the file should be overwritten if it exists (optionally, default is
* no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
* downloaded (if you do not specify this, the method tries to determine it automatically
* from the mode-directory or uses the default-mode, set by you). If you give a relative
* path to the local-file, the script-path is used as basepath.
* @param string $local_file The local file to upload
* @param string $remote_file The absolute or relative path to the file to upload to
* @param bool $overwrite (optional) Whether to overwrite existing file
* @param int $mode (optional) Either FTP_ASCII or FTP_BINARY
* @return mixed True on success, otherwise PEAR::Error
function put ($local_file, $remote_file, $overwrite = false , $mode = null )
$mode = $this->checkFileExtension ($local_file);
$remote_file = $this->_construct_path ($remote_file);
return $this->raiseError (" Local file '$local_file' does not exist." , 0 );
if ((@ftp_size($this->_handle, $remote_file) != -1 ) && !$overwrite) {
return $this->raiseError (" Remote file '$remote_file' exists and may not be overwriten." , 0 );
$res = @ftp_nb_put($this->_handle, $remote_file, $local_file, $mode);
while ($res == FTP_MOREDATA ) {
$this->_announce ('nb_put');
$res = @ftp_put($this->_handle, $remote_file, $local_file, $mode);
return $this->raiseError (" File '$local_file' could not be uploaded to '$remote_file'." , 0 );
* This functionality allows you to transfer a whole directory-structure from the
* remote-ftp to your local host. You have to give a remote-directory (ending with
* '/') and the local directory (ending with '/') where to put the files you download.
* The remote path is automatically completed with the current-remote-dir, if you give
* a relative path to this function. You can give a relative path for the $local_path,
* too. Then the script-basedir will be used for comletion of the path.
* The parameter $overwrite will determine, whether to overwrite existing files or not.
* Standard for this is false. Fourth you can explicitly set a mode for all transfer-
* actions done. If you do not set this, the method tries to determine the transfer-
* mode by checking your mode-directory for the file-extension. If the extension is not
* inside the mode-directory, it will get your default-mode.
* @param string $remote_path The path to download
* @param string $local_path The path to download to
* @param bool $overwrite (optional) Whether to overwrite existing files (true) or not (false, standard).
* @param int $mode (optional) The transfermode (either FTP_ASCII or FTP_BINARY).
* @return mixed True on succes, otherwise PEAR::Error
function getRecursive ($remote_path, $local_path, $overwrite = false , $mode = null )
$remote_path = $this->_construct_path ($remote_path);
if (!$this->_check_dir ($remote_path)) {
return $this->raiseError (" Given remote-path '$remote_path' seems not to be a directory." , 0 );
if (!$this->_check_dir ($local_path)) {
return $this->raiseError (" Given local-path '$local_path' seems not to be a directory." , 0 );
$res = @mkdir($local_path);
return $this->raiseError (" Could not create dir '$local_path'" , 0 );
foreach ($dir_list as $dir_entry) {
$remote_path_new = $remote_path. $dir_entry["name"]. "/";
$local_path_new = $local_path. $dir_entry["name"]. "/";
$result = $this->getRecursive ($remote_path_new, $local_path_new, $overwrite, $mode);
if ($this->isError ($result)) {
foreach ($file_list as $file_entry) {
$remote_file = $remote_path. $file_entry["name"];
$local_file = $local_path. $file_entry["name"];
$result = $this->get ($remote_file, $local_file, $overwrite, $mode);
if ($this->isError ($result)) {
* This functionality allows you to transfer a whole directory-structure from your
* local host to the remote-ftp. You have to give a remote-directory (ending with
* '/') and the local directory (ending with '/') where to put the files you download.
* The remote path is automatically completed with the current-remote-dir, if you give
* a relative path to this function. You can give a relative path for the $local_path,
* too. Then the script-basedir will be used for comletion of the path.
* The parameter $overwrite will determine, whether to overwrite existing files or not.
* Standard for this is false. Fourth you can explicitly set a mode for all transfer-
* actions done. If you do not set this, the method tries to determine the transfer-
* mode by checking your mode-directory for the file-extension. If the extension is not
* inside the mode-directory, it will get your default-mode.
* @param string $remote_path The path to download
* @param string $local_path The path to download to
* @param bool $overwrite (optional) Whether to overwrite existing files (true) or not (false, standard).
* @param int $mode (optional) The transfermode (either FTP_ASCII or FTP_BINARY).
* @return mixed True on succes, otherwise PEAR::Error
function putRecursive ($local_path, $remote_path, $overwrite = false , $mode = null )
$remote_path = $this->_construct_path ($remote_path);
if (!$this->_check_dir ($local_path) || !is_dir($local_path)) {
return $this->raiseError (" Given local-path '$local_path' seems not to be a directory." , 0 );
if (!$this->_check_dir ($remote_path)) {
return $this->raiseError (" Given remote-path '$remote_path' seems not to be a directory." , 0 );
$old_path = $this->pwd ();
if ($this->isError ($this->cd ($remote_path))) {
$res = $this->mkdir ($remote_path);
if ($this->isError ($res)) {
$dir_list = $this->_ls_local ($local_path);
foreach ($dir_list["dirs"] as $dir_entry) {
$remote_path_new = $remote_path. $dir_entry. "/";
$local_path_new = $local_path. $dir_entry. "/";
$result = $this->putRecursive ($local_path_new, $remote_path_new, $overwrite, $mode);
if ($this->isError ($result)) {
foreach ($dir_list["files"] as $file_entry) {
$remote_file = $remote_path. $file_entry;
$local_file = $local_path. $file_entry;
$result = $this->put ($local_file, $remote_file, $overwrite, $mode);
/*if ($this->isError($result)) {
* This checks, whether a file should be transfered in ascii- or binary-mode
* by it's file-extension. If the file-extension is not set or
* the extension is not inside one of the extension-dirs, the actual set
* transfer-mode is returned.
* @param string $filename The filename to be checked
* @return int Either FTP_ASCII or FTP_BINARY
function checkFileExtension ($filename)
$has_extension = preg_match($pattern, $filename, $eregs);
if (!empty ($this->_file_extensions[$ext])) {
return $this->_file_extensions[$ext];
* @param string $host The Hostname to set
* @return bool True on success, otherwise PEAR::Error
function setHostname ($host)
return PEAR ::raiseError ("Hostname must be a string.", 0 );
$this->_hostname = $host;
* @param int $port The Port to set
* @return bool True on success, otherwise PEAR::Error
if (!is_int($port) || ($port < 0 )) {
PEAR ::raiseError ("Invalid port. Has to be integer >= 0", 0 );
* @param string $user The Username to set
function setUsername ($user)
$this->_username = $user;
* @param string $password The Password to set
function setPassword ($password)
$this->_password = $password;
* Set the transfer-mode. You can use the predefined constants
* FTP_ASCII or FTP_BINARY. The mode will be stored for any further transfers.
* @param int $mode The mode to set
* @return mixed True on success, otherwise PEAR::Error
if (($mode == FTP_ASCII ) || ($mode == FTP_BINARY )) {
return $this->raiseError ('FTP-Mode has either to be FTP_ASCII or FTP_BINARY', 1 );
* Set the transfer-method to passive mode
* Set the transfer-method to active mode
* Set the timeout for FTP operations
* Use this method to set a timeout for FTP operation. Timeout has to be an integer.
* @param int $timeout the timeout to use
* @return bool True on success, otherwise PEAR::Error
function setTimeout ( $timeout = 0 )
if (!is_int($timeout) || ($timeout < 0 )) {
return PEAR ::raiseError (" Timeout $timeout is invalid, has to be an integer >= 0" );
return PEAR ::raiseError ("Set timeout failed.");
* Adds an extension to a mode-directory
* The mode-directory saves file-extensions coresponding to filetypes
* (ascii e.g.: 'php', 'txt', 'htm',...; binary e.g.: 'jpg', 'gif', 'exe',...).
* The extensions have to be saved without the '.'. And
* can be predefined in an external file (see: getExtensionsFile()).
* The array is build like this: 'php' => FTP_ASCII, 'png' => FTP_BINARY
* To change the mode of an extension, just add it again with the new mode!
* @param int $mode Either FTP_ASCII or FTP_BINARY
* @param string $ext Extension
function addExtension ($mode, $ext)
$this->_file_extensions[$ext] = $mode;
* This function removes an extension from the mode-directories
* @param string $ext The extension to remove
function removeExtension ($ext)
unset ($this->_file_extensions[$ext]);
* This get's both (ascii- and binary-mode-directories) from the given file.
* Beware, if you read a file into the mode-directory, all former set values
* @param string $filename The file to get from
* @return mixed True on success, otherwise PEAR::Error
function getExtensionsFile ($filename)
return $this->raiseError (" Extensions-file '$filename' does not exist" , 0 );
return $this->raiseError (" Extensions-file '$filename' is not readable" , 0 );
* @return string The Hostname
* @return string The Username
* @return string The Password
* Returns the Transfermode
* @return int The transfermode, either FTP_ASCII or FTP_BINARY.
* Returns, whether the connection is set to passive mode or not
* @return bool True if passive-, false if active-mode
* Returns the mode set for a file-extension
* @param string $ext The extension you wanna ask for
* @return int Either FTP_ASCII, FTP_BINARY or NULL (if not set a mode for it)
function getExtensionMode ($ext)
return @$this->_file_extensions[$ext];
* Get the currently set timeout.
* Returns the actual timeout set.
* @return int The actual timeout
* Adds a Net_FTP_Observer instance to the list of observers
* that are listening for messages emitted by this Net_FTP instance.
* @param object $observer The Net_FTP_Observer instance to attach
* @return boolean True if the observer is successfully attached.
function attach (&$observer)
if (!is_a($observer, 'Net_FTP_Observer')) {
$this->_listeners[$observer->getId ()] = &$observer;
* Removes a Net_FTP_Observer instance from the list of observers.
* @param object $observer The Net_FTP_Observer instance to detach
* from the list of listeners.
* @return boolean True if the observer is successfully detached.
function detach ($observer)
if (!is_a($observer, 'Net_FTP_Observer') ||
!isset ($this->_listeners[$observer->getId ()])) {
unset ($this->_listeners[$observer->getId ()]);
* Informs each registered observer instance that a new message has been
* @param mixed $event A hash describing the net event.
function _announce ($event)
foreach ($this->_listeners as $id => $listener) {
$this->_listeners[$id]->notify ($event);
* Rebuild the path, if given relative
* @param string $path The path to check and construct
* @return string The build path
function _construct_path ($path)
if (substr($path, 0 , 1 ) != "/") {
$actual_dir = @ftp_pwd($this->_handle);
if (substr($actual_dir, (strlen($actual_dir) - 2 ), 1 ) != "/") {
$path = $actual_dir. $path;
* Checks, whether a given string is a directory-path (ends with "/") or not.
* @param string $path Path to check
* @return bool True if $path is a directory, otherwise false
function _check_dir ($path)
* This will remove a file
* @param string $file The file to delete
* @return mixed True on success, otherwise PEAR::Error
if (substr($file, 0 , 1 ) == "/") {
$actual_dir = @ftp_pwd($this->_handle);
if (substr($actual_dir, (strlen($actual_dir) - 2 ), 1 ) != "/") {
$file = $actual_dir. $file;
return $this->raiseError (" Could not delete file '$file'." , 0 );
* @param string $dir The dir to delete
* @return mixed True on success, otherwise PEAR::Error
return $this->raiseError (" Directory name '$dir' is invalid, has to end with '/'" , 0 );
return $this->raiseError (" Could not delete directory '$dir'." , 0 );
* This will remove a dir and all subdirs and -files
* @param string $file The dir to delete recursively
* @return mixed True on success, otherwise PEAR::Error
function _rm_dir_recursive ($dir)
return $this->raiseError (" Directory name '$dir' is invalid, has to end with '/'" , 0 );
$file_list = $this->_ls_files ($dir);
foreach ($file_list as $file) {
$file = $dir. $file["name"];
if ($this->isError ($res)) {
$dir_list = $this->_ls_dirs ($dir);
foreach ($dir_list as $new_dir) {
$new_dir = $dir. $new_dir["name"]. "/";
$res = $this->_rm_dir_recursive ($new_dir);
if ($this->isError ($res)) {
$res = $this->_rm_dir ($dir);
* Lists up files and directories
* @param string $dir The directory to list up
* @return array An array of dirs and files
$list_splitted = $this->_list_and_parse ($dir);
if (!is_array($list_splitted["files"])) {
$list_splitted["files"] = array ();
if (!is_array($list_splitted["dirs"])) {
$list_splitted["dirs"] = array ();
* @param string $dir The directory to list up
* @return array An array of dirs
$list = $this->_list_and_parse ($dir);
* @param string $dir The directory to list up
* @return array An array of files
$list = $this->_list_and_parse ($dir);
$list["files"] = array ();
* This lists up the directory-content and parses the items into well-formated arrays
* The results of this array are sorted (dirs on top, sorted by name;
* files below, sorted by name).
* @param string $dir The directory to parse
* @return array Lists of dirs and files
function _list_and_parse ($dir)
$dir_list = @ftp_rawlist ($this->_handle , $dir);
foreach ($dir_list as $entry) {
$matcher = $this->_determine_os_match ($entry);
if (PEAR ::isError ($matcher)) {
if (!preg_match($matcher['pattern'], $entry, $m)) {
foreach ($matcher['map'] as $key=> $val) {
$entry['stamp'] = $this->_parse_Date ($entry['date']);
@usort($dirs_list, array ("Net_FTP", "_nat_sort"));
@usort($files_list, array ("Net_FTP", "_nat_sort"));
$res["dirs"] = $dirs_list;
$res["files"] = $files_list;
* This determines the server OS and returns a valid regex to parse
* @param string $entry The ls entry to parse
* @return mixed An array of 'pattern' and 'map' on success, otherwise PEAR::Error
function _determine_os_match ($entry) {
foreach ($this->_ls_match as $os => $match) {
$error = 'The list style of your server seems not to be supported. Please email a "$ftp->ls(NET_FTP_RAWLIST);" output plus info on the server to the maintainer of this package to get it supported! Thanks for your help!';
return PEAR ::raiseError ($error);
* Lists a local directory
* @param string $dir_path The dir to list
* @return array The list of dirs and files
function _ls_local ($dir_path)
while (false !== ($entry = $dir->read ())) {
if (($entry != '.') && ($entry != '..')) {
if (is_dir($dir_path. $entry)) {
$res['dirs'] = $dir_list;
$res['files'] = $file_list;
* Function for use with usort().
* Compares the list-array-elements by name.
function _nat_sort ($item_1, $item_2)
return strnatcmp($item_1['name'], $item_2['name']);
* Parse dates to timestamps
* @param string $date Date
function _parse_Date ($date)
// Sep 10 22:06 => Sep 10, <year> 22:06
if (preg_match('/([A-Za-z]+)[ ]+([0-9]+)[ ]+([0-9]+):([0-9]+)/', $date, $res)) {
$date = " $month $day, $year $hour:$minute";
return $this->raiseError ('Dateconversion failed.', 0 );
Documentation generated on Mon, 11 Mar 2019 10:16:52 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|