How to Connect to an FTP Server with Implicit SSL/TLS using PHP

The PHP manual lacks good documentation on how to use cURL to connect to an FTP server with Implicit SSL/TLS, so here’s an example from a recent project. It’s very basic and is only designed to connect and upload an ASCII file, but it should help you get started. The libcurl Guide is very helpful in learning exactly what options actually do, as sometimes the PHP manual can be a bit light on specifics.

Here’s some things to note:

  • cURL will try to use passive mode by default – this can be disabled by forcing active mode with CURLOPT_FTPPORT
  • You can relax the SSL requirement using CURLOPT_FTP_SSL, which controls whether SSL is used on either the control or data connections, or both.
  • This class allows cURL to choose which authentication method to use, either SSL or TLS. You can force either using CURLOPT_FTPSSLAUTH.

20 responses

  1. Eric Linberg Avatar
    Eric Linberg

    I refactored this code a little bit and added ftplist, download and delete functionality to it.

    [code]
    user = $username;
    $this->pass = $password;
    $this->path = $initial_path;

    // check for blank username
    if ( ! $username )
    throw new Exception( ‘FTP Username is blank.’ );

    // don’t check for blank password (highly-questionable use case, but still)

    // check for blank server
    if ( ! $server )
    throw new Exception( ‘FTP Server is blank.’ );

    // check for blank port
    if ( ! $port )
    throw new Exception ( ‘FTP Port is blank.’, WC_XML_Suite::$text_domain );

    // set host/initial path
    $this->url = “ftps://{$server}/{$initial_path}”;

    // setup connection
    $this->curl_handle = curl_init();

    // check for successful connection
    if ( ! $this->curl_handle )
    throw new Exception( ‘Could not initialize cURL.’ );

    // connection options
    $options = array(
    CURLOPT_USERPWD => $username . ‘:’ . $password,
    CURLOPT_SSL_VERIFYPEER => false, // don’t verify SSL
    CURLOPT_SSL_VERIFYHOST => false,
    CURLOPT_FTP_SSL => CURLFTPSSL_ALL, // require SSL For both control and data connections
    CURLOPT_FTPSSLAUTH => CURLFTPAUTH_DEFAULT, // let cURL choose the FTP authentication method (either SSL or TLS)
    CURLOPT_PORT => $port,
    CURLOPT_TIMEOUT => 60,
    );

    // cURL FTP enables passive mode by default, so disable it by enabling the PORT command and allowing cURL to select the IP address for the data connection
    if ( ! $passive_mode )
    $options[ CURLOPT_FTPPORT ] = ‘-‘;

    // set connection options, use foreach so useful errors can be caught instead of a generic “cannot set options” error with curl_setopt_array()
    foreach ( $options as $option_name => $option_value ) {

    if ( ! curl_setopt( $this->curl_handle, $option_name, $option_value ) )
    throw new Exception( sprintf( ‘Could not set cURL option: %s’, $option_name ) );
    }

    }

    /**
    * Write file into temporary memory and upload stream to remote file
    *
    * @access public
    * @since 1.0
    * @param string $file_name – remote file name to create
    * @param string $file – file content to upload
    * @throws Exception – Open remote file failure or write data failure
    */
    public function upload( $file_name, $file ) {

    curl_setopt( $this->curl_handle, CURLOPT_UPLOAD, true);
    // set file name
    if ( ! curl_setopt( $this->curl_handle, CURLOPT_URL, $this->url . $file_name ))
    throw new Exception ( “Could not set cURL file name: $file_name” );

    // open memory stream for writing
    $stream = fopen( ‘php://temp’, ‘w+’ );

    // check for valid stream handle
    if ( ! $stream )
    throw new Exception( ‘Could not open php://temp for writing.’ );

    // write file into the temporary stream
    fwrite( $stream, $file );

    // rewind the stream pointer
    rewind( $stream );

    // set the file to be uploaded
    if ( ! curl_setopt( $this->curl_handle, CURLOPT_INFILE, $stream ) )
    throw new Exception( “Could not load file $file_name” );

    // upload file
    if ( ! curl_exec( $this->curl_handle ) )
    throw new Exception( sprintf( ‘Could not upload file. cURL Error: [%s] – %s’, curl_errno( $this->curl_handle ), curl_error( $this->curl_handle ) ) );

    // close the stream handle
    fclose( $stream );

    return TRUE;
    }

    /**
    * Download file from FTPS default directory
    *
    * @param $file_name
    * @return string
    */

    public function download($file_name){

    curl_setopt( $this->curl_handle, CURLOPT_URL, $this->url . $file_name);

    curl_setopt( $this->curl_handle, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($this->curl_handle);
    if( strlen($result) ){
    return $result;
    } else {
    return ”;

    }

    }

    /**
    * Returns curl handle in case you want to capture additional information about the connection
    *
    * @return resource
    */
    public function getHandle(){

    return $this->curl_handle;

    }

    /**
    * @return array array of file names
    * @throws Exception
    */
    public function ftplist(){

    if ( ! curl_setopt( $this->curl_handle, CURLOPT_URL, $this->url))
    throw new Exception ( “Could not set cURL directory: $this->url” );

    curl_setopt( $this->curl_handle,CURLOPT_FTPLISTONLY,1);
    curl_setopt( $this->curl_handle, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($this->curl_handle);
    $files = explode(“\n”,trim($result));
    if( count($files) ){
    return $files;
    } else {
    return array();
    }

    }

    /**
    * Delete specified file
    *
    * @param $file_name
    * @return string
    * @throws Exception
    */
    public function delete($file_name){

    if ( ! curl_setopt( $this->curl_handle, CURLOPT_URL, $this->url))
    throw new Exception ( “Could not set cURL directory: $this->url” );

    if( !in_array($file_name, $this->ftplist()) ){
    throw new Exception ( $file_name . ‘ not found’ );
    }

    curl_setopt( $this->curl_handle,CURLOPT_QUOTE,array(‘DELE ‘ . $this->path . $file_name ));
    curl_setopt( $this->curl_handle, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($this->curl_handle);
    //print_r( curl_getinfo($this->curl_handle) ) ;
    $files = explode(“\n”,trim($result));

    if( ! in_array( $file_name, $files) ){
    return ‘SUCCESS’;
    } else {
    return ‘FAILED’;
    }

    }

    /**
    * Attempt to close cURL handle
    * Note – errors suppressed here as they are not useful
    *
    * @access public
    * @since 1.0
    */
    public function __destruct() {

    @curl_close( $this->curl_handle );
    }

    }
    [/code]

    1. Hey Eric, this looks pretty cool – can you fork the original gist and add these changes so I can link to it from the article?

  2. Hey Max,

    Thanks this code!!

    Any chance of pointing me in thee right direction to use this code to upload video files instead of text? I have established a connection using this code but stuck uploading any othe file except for text.

    Cheer Damien

    1. welcome 🙂 I’m not sure about binary files, as I’ve not worked with them but my guess would be to read the file into the stream and set that as the CURLOPT_INFILE.

  3. Abhishek Jain Avatar
    Abhishek Jain

    Hi Max,
    I am getting this error, i update CURLOPT_TIMEOUT from 30 to 500.
    PHP Fatal error: Uncaught exception ‘Exception’ with message ‘Could not upload file. cURL Error: [7]
    Thanks

    1. Not sure what Error 7 is, but you’ll want to catch that exception and dig into it to find out why it can’t upload it 🙂

  4. Please make a documentation of How to use. It will be very helpful

  5. Thanks for posting this. Agree that the PHP Manual is a bit light on cURL documentation so this is a big help.
    Question: how would your approach need to change for Explicit encryption? Having a real challenge finding much on FTPES using PHP (with or without cURL) implementations.

    1. Glad it was useful 🙂 I didn’t work with FTPES at all, so not sure I can help much there. My only recommendation would be to look into the cURL manual (which is pretty verbose) for how it’s done through native cURL, as that tends to translate back to PHP pretty cleanly. good luck!

  6. Hi Max;

    Nice code. im new to this. can you please put a sample how to use the class.

    thanks

    jeff

  7. Can it work with explicit mode?

    I know the class is built specifically to work with implicit mode, but I need to send files using sometimes explicit and other times implicit mode. I would love to use only FTP_Implicit_SSL instead of two different pieces of code.

    1. I think cURL can also handle explicit mode so sure, it could be modified to do both. I think the built-in PHP functions for handling FTPS are generally easier to use though, so you could just put both in a single class.

  8. caijianhui Avatar
    caijianhui

    return : could not upload file.curl error:[35]-SSL connect error
    what is that problem? thank you

    1. caijianhui Avatar
      caijianhui

      i change
      $this->url = “ftps://{$server}/{$initial_path}”;
      to
      $this->url = “ftp://{$server}/{$initial_path}”;

      problem solved

      is it because ftps specifies the port 990??so i can’t use the port(54000) i set?

      1. Yeah, most likely. cURL can be configured to connect via a different port though, if you need that.

    2. Probably an issue with the SSL cert for the host you’re connecting to.

    1. The SO link says the same thing as this post 🙂 Explicit TLS can use the built-in PHP functions, but implicit TLS requires the use of cURL, as is shown in the class above.

  9. Bonjour je voudrais intégrer votre classe pour télécharger un fichier zippé.
    Est ce que c’est possible.

  10. Hello I would like to integrate your class to download a zipped file.
    Is it possible.