Using PHP to SSH

I needed the ability to fire off some ssh commands today testing out how to do sftp through php for a client. Turns out there's a bit of a layering process here. This should get you started, and going on from here will require a bit more customization. It's important to note that many sftp services do not allow you shell access. Just because you have an ssh connection, does not mean you can issue commands. And just because you can not issue commands, does not mean you can't do scp or use regular sftp commands. It's a bit of a bother.

Start by getting the libraries you need. On a debian Linux system, the install commands below worked for me. Be sure to use the current version of the pecl ssh library.

  1. sudo apt-get install libssh2-1-dev libssh2-php
  2. sudo pecl install channel://pecl.php.net/ssh2-0.12

On Macs, you can try using ports:

  1. sudo port install php5-ssh2

After that, you just need a class do a bit of the logic for you:
  1. class FileRetriever {
  2.  
  3.   private $connection;
  4.   public $connected;
  5.  
  6.   /**
  7.    * Constructor
  8.    */
  9.   public function __construct(){
  10.     $this->connected = false;
  11.   }
  12.  
  13.   /**
  14.    * Creates a connection to the ssh server
  15.    * @param array $parameters
  16.    */
  17.   public function connect($parameters){
  18.     // Merge in connection defaults
  19.     $parameters += array(
  20.       'port' => 22,
  21.     );
  22.    
  23.     try {
  24.       $this->connection = ssh2_connect($parameters['host'], $parameters['port']);
  25.       if( !$this->connection ){
  26.         return false;
  27.       }
  28.      
  29.       if( !ssh2_auth_password($this->connection, $parameters['user'], $parameters['password']) ){
  30.         return false;
  31.       }
  32.      
  33.       $this->connected = true;
  34.       return true;
  35.     } catch(Exception $e) {
  36.       return false;
  37.     }
  38.   }
  39.  
  40.   /**
  41.    * Downloads a file
  42.    * @param string $source
  43.    * @param string $destination
  44.    * @return mixed string destination or false
  45.    */
  46.   public function scp($source, $destination){
  47.     if( !$this->connected ){
  48.       return false;
  49.     }
  50.     try {
  51.       if( ssh2_scp_recv($this->connection, $source, $destination) ){
  52.         return $destination;
  53.       }
  54.     } catch (Exception $e) {}
  55.     return false;
  56.   }
  57.  
  58.   /**
  59.    * Sends a command through the ssh connection
  60.    * @param string
  61.    */
  62.   public function execute($command){
  63.     if( !$this->connected ){
  64.       return false;
  65.     }
  66.    
  67.     try {
  68.      
  69.       if (!($stream = ssh2_exec($this->connection, $command))) {
  70.         throw new Exception('SSH command failed');
  71.       }
  72.      
  73.       // This affects calls like fgets() and fread() that read from the stream.
  74.       // In non-blocking mode an fgets() call will always return right away while
  75.       // in blocking mode it will wait for data to become available on the stream.
  76.       stream_set_blocking($stream, true);
  77.       $data = "";
  78.       while ($buffer = fread($stream, 4096)) {
  79.         $data .= $buffer;
  80.       }
  81.       fclose($stream);
  82.       return $data;
  83.      
  84.     } catch(Exception $e){}
  85.    
  86.     return false;    
  87.   }
  88.  
  89.   /**
  90.    * Streams a file through fopen and fread on the ssh.sftp stream, in case the above method fails
  91.    */
  92.   private function receiveFile($remote_file, $destination){
  93.     $sftp = $this->sftp;
  94.     if ( !file_exists("ssh2.sftp://$sftp$remote_file") ){
  95.       return false;
  96.     }
  97.     if ( !$stream = fopen("ssh2.sftp://$sftp$remote_file", 'rt') ){
  98.       return false;
  99.     }
  100.     $filesize = filesize("ssh2.sftp://$sftp$remote_file");
  101.     if( !$filesize ){
  102.       return false;
  103.     }
  104.  
  105.     // Create our temp file and open a handle to write to it
  106.     $out = fopen($destination, 'w');
  107.  
  108.     $contents = '';
  109.     $read = 0;
  110.     while ($read < $filesize && ($buffer = fread($stream, $filesize - $read))) {
  111.       // Increase our bytes read
  112.       $read += strlen($buffer);
  113.       // Write to our local file
  114.       if (fwrite($out, $buffer) === FALSE) {
  115.           throw new Exception("Unable to write to local file: $destination");
  116.       }
  117.     }
  118.     unset($buffer);
  119.  
  120.     fclose($stream);
  121.     fclose($out);
  122.     return true;
  123.   }
  124.  
  125. }

From there you can call simple commands doing the following:

  1. $retriever = new FileRetriever();
  2.       if( !$retriever->connect(array(
  3.         'host' => '####',
  4.         'user' => '####',
  5.         'password' => '####',
  6.       )) ){
  7.         die('connect failed');
  8.         return false;
  9.       }
  10.      
  11.       $output = $retriever->execute('ls -halF');
  12.      
  13.       echo "Output: " . $output . "<br />\n";

If the system you're connected to allows it, you'll get preformated text that looks similar to:

Output: total 452K
drwxr-xr-x  50 lance www-data 4.0K Nov 19 08:19 ./
drwxr-xr-x   3 root  root     4.0K Jul 12  2012 ../
drwx------   3 lance www-data 4.0K Jul 17  2012 .adobe/
drwxr-xr-x   3 lance www-data 4.0K Mar 15  2013 .android/
-rw-------   1 lance www-data  42K Nov 19 08:22 .bash_history
-rw-r--r--   1 lance www-data  220 Jul  2  2012 .bash_logout
-rw-r--r--   1 lance www-data 3.6K Oct 23  2012 .bashrc
drwxr-xr-x   2 lance www-data 4.0K Mar 13  2013 bin/
drwx------  21 lance www-data 4.0K Nov 18 13:36 .cache/
drwxr-xr-x   3 lance www-data 4.0K Oct 23  2012 .codeintel/
drwxr-xr-x   3 lance www-data 4.0K Jul  3  2012 .compiz-1/
drwxr-xr-x   3 lance www-data 4.0K Nov 18 13:35 .composer/
drwx------  22 lance www-data 4.0K Oct 28 15:54 .config/
drwxr-xr-x   3 lance www-data 4.0K Sep 24 16:04 .cordova/
drwx------   3 lance www-data 4.0K Jul  2  2012 .dbus/
...
Tags: 

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.