FTP Class for you

chatmasta

Well-known member
Jan 7, 2007
2,614
68
48
NYC
Thought some of you might find this helpful. Pretty self explanatory for the most part. Also included my own logging classes. It's all kind of messy/undocumented because it's a work in progress, but it is totally functional. I would post in the treasure chest thread but nobody actually checks that.

Code:
<?php 
$cfg['hostname'] = 'yourdomain.com';
$cfg['username'] = 'chatmasta';
$cfg['username'] = 'pass';

echo '<pre>';
try {
    $ftp = new Ftp($cfg['hostname'], $cfg['username'], $cfg['password']);
    $ftp->cd('public_html');
    $ftp->cd('fortesting');
    /*
        .... do FTP stuff here. If any of it throws an exception, it goes to the catch block */
    */
} catch (Exception $e) {
    echo $e->getMessage();
}
echo "\n";
print_r($ftp->log->getAllItems());
echo '<hr>Done.';


class Log {
    private $stack = array();
    public function __construct(Log $base_log = null) {
        if(!is_null($base_log))
            $this->stack = $base_log->getAllItems();
    }
    
    public function message($item) {
        $this->add($item, 'message');
    }
    
    public function error($item, $throw_exception = false) {
        $this->add($item, 'error');
        
        if($throw_exception)
            throw new Exception($this->getLastItem()->message);
    }
    
    private function add($item, $type = null) {
        if(!is_a($item, 'LogItem') && $type == null) 
            throw new Exception('$item passed to Log::add() must be LogItem or $type must be defined');
        if(!is_a($item, 'LogItem')) $item = new LogItem($item, $type);
        $this->stack[] = $item;
    }
    
    public function getLastItem() {
        return end($this->stack);
    }
    
    public function getAllItems() {
        return $this->stack;
    }
}

class LogItem {
    public $message, $type;
    public function __construct($message, $type = 'error') {
        $this->message = $message;
        $this->type = $type;
    }
    public function isType($type) {
        return ($this->type == $type);
    }
}

class Util {
    public static function parseConstant($constant) {
        // If $constant is not defined, we assume $constant is a string value to be parsed
        // otherwise, we parse the value of the constant with the name $constant
        if(!defined($constant))
            $constant_value = $constant;
        else 
            $constant_value = constant($constant);
        if(preg_match_all("/\{([A-za-z0-9]+)\}/i", $constant_value, $matches)) {
            foreach($matches[1] as $needle) {
                global ${$needle};
                $constant_value = str_replace('{' . $needle . '}', ${$needle}, $constant_value);
            }
        }
        return $constant_value;
    }
}

class Ftp {
    private $conn, $current_dir;
    public $log;
        
    public function __construct($host, $username, $password, $passive = false) {
        $this->log = new Log;
        
        $this->connect($host, $username, $password, $passive);
    }
    
    private function connect($host, $username, $password, $passive = false) {
        $this->conn = ftp_connect($host);
        $login = ftp_login($this->conn, $username, $password);
        ftp_pasv($this->conn, $passive);
        
        if(!$this->conn || !$login)
            $this->log->error('There was a login problem.', true);
        $this->log->message('Sucessfully connected.');
    }
    
    public function ls($dir = '.', $raw = false) {
        $ls = ($raw) ? ftp_rawlist($this->conn, $dir) : ftp_nlist($this->conn, $dir);
        if(!$ls)
            $this->log->error("Could not list contents of $dir", true);
        return $ls;
    }
    
    public function isDir($dir) {
        $original_dir = ftp_pwd($this->conn);
        $result = @ftp_chdir($this->conn, $dir);
        if($result) {
            ftp_chdir($this->conn, $original_dir);
            return true;
        }    
        return false;
    }
        
    public function isFile($file) {
        return in_array($file, $this->ls());
    }
    
    public function chmod($path, $mode_dec) {
        $mode_oct = octdec(str_pad($mode_dec, 4, '0' ,STR_PAD_LEFT));
        $result = ftp_chmod($this->conn, $mode_oct, $path);
        if(!$result)
            $this->log->error("Error changing permissions for $path to $mode_dec", true);
        $this->log->message("Permissions for $path are now $mode_dec");
    }
        
    public function cd($dir) {
        $result = ftp_chdir($this->conn, $dir);
        if(!$result)
            $this->log->error("Could not change directory to $dir", true);
        $this->log->message('Current directory is now' . ftp_pwd($this->conn));
    }
    
    public function mkdir($dir, $mode_dec = null) {
        $current_dir = ftp_pwd($this->conn);
        $result = ftp_mkdir($this->conn, $dir);
        if(!$result)
            $this->log->error("Could not make directory $dir in " . ftp_pwd($this->conn), true);
        $this->log->message("Made directory $dir in " . ftp_pwd($this->conn));
        if(!is_null($mode_dec))
            $this->chmod($dir, $mode_dec);
    }
    
    public function rmdir($dir) {
        if($dir == '.' || $dir == '..')
            return false; // quietly return
        $result = ftp_rmdir($this->conn, $dir);
        if(!$result)
            $this->log->error("Could not remove directory $dir - you should ensure that it is empty.", true);
        $this->log->message("Removed directory $dir");
    }
    
    public function delete($path) {
        $result = ftp_delete($this->conn, $path);
        if(!$result)
            $this->log->error("Could not delete file $path", true);
        $this->log->message("Deleted file $path");
    }
    
    public function upload($from_path, $to_path, $mode = null) {
        if($mode != 'ascii' || $mode != 'binary')
            $mode = self::determineUploadMode($from_path);
        $mode = ($mode == 'ascii' || $mode == FTP_ASCII) ? FTP_ASCII : FTP_BINARY;
        
        $result = ftp_put($this->conn, $from_path, $to_path, $mode);
        
        if(!$result)
            $this->log->error("Error uploading $from_path to $to_path", true);
        $this->log->message("Successfully uploaded $from_path to $to_path");            
    }
    
    public function download($from_path, $to_path) {
        $mode = self::determineUploadMode($to_path);
        $result = ftp_get($this->conn, $to_path, $from_path, $mode, 0);
        if(!$result)
            $this->log->error('Error downloading $from_path to $to_path');
        $this->log->message('Successfully downloaded $from_path to $to_path');
    }
    
    public static function determineUploadMode($path) {
        $ascii_always = array('.htm', '.html', '.shtml', '.php', '.pl', '.cgi', '.js', '.py', '.cnf', '.css', '.forward', '.htaccess', '.map', '.pwd', '.txt', '.grp', '.ctl');
        $extension = array_pop(explode('.', $from_path));
        if(in_array($extension, $ascii_always))
            return FTP_ASCII;
        return FTP_BINARY;
    }
    
    public function __destruct() {
        if($this->conn) ftp_close($this->conn);
    }    
}
?>

PHP:
<?php echo 'Why does this code highlighting suck so much dick?'; ?>
 


OOPS.

Change Ftp::upload to:

Code:
    public function upload($from_path, $to_path, $mode = null) {
        if($mode != 'ascii' || $mode != 'binary' || $mode != FTP_ASCII || $mode != FTP_BINARY)
            $mode = self::determineUploadMode($from_path);
        else if(!defined($mode))
            $mode = constant('FTP_' . strtoupper($mode));
        
        $result = ftp_put($this->conn, $to_path, $from_path, $mode);
        
        if(!$result)
            $this->log->error("Error uploading $from_path to $to_path", true);
        $this->log->message("Successfully uploaded $from_path to $to_path");            
    }
 
I really don't get a lot of phper's obsession with doing everything through a web server, so making it output html. What's wrong with the command line? No hate though, code sharing is good.
 
I really don't get a lot of phper's obsession with doing everything through a web server
It's not obsession, just the only way they know so they make it do what they need. Command line php scripts are becomming more common now though.
 
Last edited:
Nah I admit this is probably one of the things PHP is worst at. But it's necessary for what I'm working on. For the record, currently learning python ;) It is quite nice..
 
I don't think regular expressions, which that lib uses are a good idea on HTML. You seen other people's HTML? It's a mess. I want all the links on a page with xpath? //a/@href, works for every page out there.
 
xpath is fantastic. Once you start scraping random targets (rather than big, consistent targets) you'll see how fucky html can get. Shit like:

<html>
<Html>
<htmL >
<HTML>

it's a nightmare