PHP: Creating a PHP-Based FTP Client Example

Saturday, February 21, 2009

The best way to accomplish tasks like getting directory listings, making directories, deleting files, putting multiple files at once on FTP in PHP is to use the FTP extension that is built into it. (Note that the FTP extension is enabled by default in Windows builds of PHP but must be specifically compiled into other versions.)

The following script is designed to be run from the command line, using the command-line version of PHP to execute it instead of through a web server.

<?php
$stdin = fopen('php://stdin', 'r');
$server = false;
$mode = FTP_ASCII;
$commands = array('open', 'close', 'quit', 'exit', 'cd', 'ls', 'dir',
    'pwd', 'get', 'put', 'ascii', 'binary', 'bin', 'delete', 'del');

if ($argc > 1) {
    // Remove the filename from the line
    array_shift($argv);
    // Now call the open function to handle this:
    _open($argv);
}

while (true) {
    echo 'phpftp> ';

    $line = trim(fgets($stdin));

    if ($line) {
        // Split the line into an array of space separated entries:
        $opts = explode(' ', $line);
        $cmd = strtolower(array_shift($opts));

        if (!in_array($cmd, $commands)) {
            echo "! Command not supported.\n";
        }
        // If server is false, only allow 'open'
        elseif (($server xor ($cmd != 'open')) && ($cmd != 'quit') && ($cmd != 'exit')) {
            echo "! Invalid server status for this command.\n";
        } else {
            // Otherwise, execute whatever command they gave 
            $func = "_{$cmd}";
            $func($opts);
        }
    }
}


// Open a connection:
function _open($opts) {
    global $server, $stdin;

    if (!isset($opts[0])) {
        echo '? Host: ';
        $opts[0] = trim(fgets($stdin));
    }


    if (!isset($opts[1])) {
        echo '? User: ';
        $opts[1] = trim(fgets($stdin));
    }


    if (!isset($opts[2])) {
        echo '? Pass: ';
        $opts[2] = trim(fgets($stdin));
    }


    if (!($server = @ftp_connect($opts[0]))) {
        echo "! Error, cannot connect to host!\n";
        $server = false;
    } else {
        if (!(@ftp_login($server, $opts[1], $opts[2]))) {
            echo "! Error, Username/Password not accepted!\n";
            ftp_close($server);
            $server = false;
        } else {

            echo "- Connected to {$opts[0]}\n";

            if ($type = ftp_systype($server)) {
                echo "- Server type: {$type}\n";
            }

            _ascii(0);
            _pwd(0);
        }
    }
}

// Close a connection:
function _close($opts) {
    @ftp_close($GLOBALS['server']);
    $GLOBALS['server'] = false;
    echo "- Connection Closed.\n";
}

// Change directories
function _cd($opts) {
    if (!(@ftp_chdir($GLOBALS['server'], @$opts[0]))) {
        echo "! Error, Failed to change directory\n";
    }
    _pwd(0);
}

// Quit the program!
function _exit($opts) {
    if ($GLOBALS['server']) {
        _close(0);
    }
    echo "- Goodbye!\n";
    exit();
}
function _quit($opts) {
    _exit($opts);
}

// To get a directory listing:
function _ls($opts) {
    $optstring = implode(' ', $opts);
    if ($res = ftp_rawlist($GLOBALS['server'], $optstring)) {
        foreach ($res as $r) { echo "{$r}\n"; }
    } else {
        echo "! Error, could not generate directory listing\n";
    }
}
function _dir($opts) {
    _ls($opts);
}

// Figure out what directory you are in:
function _pwd($opts) {
    $cwd = ftp_pwd($GLOBALS['server']);
    echo "- Current directory: {$cwd}\n";
}

// Get a file from the remote host, and save it locally.
function _get($opts) {
    // If we don't have any options, give an error
    if (!($opts)) {
        echo "! Error, no file specified\n";
    } else {
        // If we don't have a second option, assume a desire to
        // save the file into the current directory, with the same name
        if (!isset($opts[1])) {
            $opts[1] = basename($opts[0]);
        }

        // Now, attempt to save the file.
        if (!@ftp_get($GLOBALS['server'], $opts[1], $opts[0],
                $GLOBALS['mode'])) {
            echo "! Error - Could not download file\n";
        } else {
            echo "- Data saved to file: {$opts[1]}\n";
        }
    }
}

// Put a local file to the remote host:
function _put($opts) {
    if (!($opts)) {
        echo "! Error, no file specified\n";
    } else {
        if (!isset($opts[1])) {
            $opts[1] = basename($opts[0]);
        }

        if (!@ftp_put($GLOBALS['server'], $opts[1], $opts[0],
                $GLOBALS['mode'])) {
            echo "! Error - Could not upload file\n";
        } else {
            echo "- Data uploaded to file: {$opts[1]}\n";
        }
    }
}

// To Change the transfer mode to ASCII
function _ascii($opts) {
    $GLOBALS['mode'] = FTP_ASCII;
    echo "- Transfer mode: ASCII\n";
}

// Change the transfer mode to Binary
function _binary($opts) {
    $GLOBALS['mode'] = FTP_BINARY;
    echo "- Transfer mode: Binary\n";
}
function _bin($opts) {
    _binary($opts);
}

// Allow for deleting of files
function _delete($opts) {
    if (!($opts)) {
        echo "! Error, no file specified\n";
    } else {
        if (!@ftp_delete($GLOBALS['server'], $opts[0])) {
            echo "! Error - Could not delete file\n";
        } else {
            echo "- File Deleted: {$opts[0]}\n";
        }
    }
}
function _del($opts) {
    _delete($opts);
}
?>


To accomplish the task of creating a fully functional (though basic) FTP client in PHP, start by beginning to converse with the operating system's input by opening php://stdin. This is the equivalent in other languages of reading from stdin, and is how you accept input from the user. It then enters a loop waiting for input and doing some basic sanity checks on the command it was given, and then proceeds to perform the task requested of it.

It creates a set of its own internal functions to map to the various commands and is almost a direct one-for-one mapping for the functions provided in the FTP extension. The only difference is that the client does some extra error checking to give useful responses back to the user. Of note about the code itself: The use of a variable to hold function names, as discussed in Chapter 6, "Functions," is implemented.

In the end this creates a functional FTP client that has been written completely in PHP. It is missing some more advanced features, and even some basic ones (such as the creation and removal of directories), but gives a fairly complete example of how powerful the FTP extension for PHP is.

Hope it helps.

0 comments: