- new OSInet/Class_Grapher directory - moved misc.php and other miscellaneous PHP files to misc/ - split the PSR0 autoloader from misc.php to psr0.php - moved BackgroundApplication to FSM directory - moved FSM samples and docs to FSM help directory - new "apps" directory for the demo source - new Class_Grapher demo app - moved the files of the FSM FTP demo app to "apps". Likely broken.
401 lines
9.2 KiB
PHP
401 lines
9.2 KiB
PHP
<?php
|
|
/**
|
|
* An FTP transfer wrapper using asynchronous transfer
|
|
* (c) 2006 Ouest Systèmes Informatiques (OSI)
|
|
* Licensed under the CeCILL 2.0 license
|
|
*
|
|
* $Id: u_ftp.php,v 1.3 2007-04-28 20:03:40 marand Exp $
|
|
*
|
|
* @todo redo to take advantage of the new FSM 1.2 features to reduce the code
|
|
*/
|
|
|
|
require_once('u_fsm.php');
|
|
|
|
/**
|
|
* Class implements a finite-state-machine-based
|
|
* FTP client with *very* limited functionality,
|
|
* but that can display progress information
|
|
* states are:
|
|
* - init: not yet set up
|
|
* - offline: no link established
|
|
* - online: client connected to server
|
|
* - live: client connected and logged in
|
|
* - active: a data transfer operation is under way
|
|
* - unsafe: something failed, disconnect can happen out of our control
|
|
*/
|
|
class ftp_client extends fsm {
|
|
private $f_host;
|
|
private $f_user;
|
|
private $f_pass;
|
|
private $f_pwd;
|
|
private $f_file;
|
|
private $f_localwd;
|
|
private $f_localfile;
|
|
private $f_fp; // file pointer to local file, used in get/put/continue
|
|
private $f_goal; // bytes to be transferred
|
|
private $f_conn; // connection
|
|
private $f_callback; // name of callback function for progress information
|
|
|
|
/**
|
|
* @param string $url
|
|
* @return void
|
|
*/
|
|
public function __construct(string $url = null) {
|
|
if ($url)
|
|
$this->set_url($url);
|
|
$this->fTransitions = array(
|
|
'init' => array(
|
|
'check_params' => array(
|
|
true => 'offline',
|
|
false => 'init',
|
|
),
|
|
'set_progress' => array(
|
|
true => 'offline',
|
|
false => 'offline',
|
|
),
|
|
),
|
|
'offline' => array(
|
|
'check_params' => array(
|
|
true => 'offline',
|
|
false => 'init',
|
|
),
|
|
'connect' => array
|
|
(
|
|
true => 'online',
|
|
false => 'unsafe',
|
|
),
|
|
'set_progress' => array(
|
|
true => 'offline',
|
|
false => 'offline',
|
|
),
|
|
),
|
|
'online' => array(
|
|
'close' => array(
|
|
true => 'offline',
|
|
false => 'unsafe',
|
|
),
|
|
'login' => array(
|
|
true => 'live',
|
|
false => 'unsafe',
|
|
),
|
|
),
|
|
'live' => array(
|
|
'close' => array(
|
|
true => 'offline',
|
|
false => 'unsafe',
|
|
),
|
|
'chdir' => array(
|
|
true => 'live',
|
|
false => 'unsafe',
|
|
),
|
|
'get' => array(
|
|
FTP_FINISHED => 'live',
|
|
FTP_FAILED => 'unsafe',
|
|
FTP_MOREDATA => 'active',
|
|
),
|
|
'put' => array(
|
|
FTP_FINISHED => 'live',
|
|
FTP_FAILED => 'unsafe',
|
|
FTP_MOREDATA => 'active',
|
|
),
|
|
),
|
|
'active' => array(
|
|
'close' => array(
|
|
true => 'offline',
|
|
false => 'unsafe',
|
|
),
|
|
'continue' => array(
|
|
FTP_FINISHED => 'live',
|
|
FTP_FAILED => 'unsafe',
|
|
FTP_MOREDATA => 'active',
|
|
),
|
|
),
|
|
'unsafe' => array(),
|
|
/**
|
|
* no transition allowed
|
|
* Even retrying connect would not be safe
|
|
* Must destruct
|
|
*/
|
|
);
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* close connection if it hasn't been done, to prevent connection
|
|
* lingering on the server if avoidable
|
|
* @return void
|
|
*/
|
|
public function __destruct() {
|
|
if ($this->is_event_allowed('close'))
|
|
$this->f_close();
|
|
|
|
if (is_resource($this->f_fp)) {
|
|
try {
|
|
fclose($this->f_fp);
|
|
}
|
|
catch (Exception $e) {
|
|
print_r($e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* setter for f_host. Make sur name can be resolved
|
|
*
|
|
* @param string $host
|
|
* @return ftp_client
|
|
*/
|
|
public function set_host($host = null) {
|
|
/**
|
|
* ignore hosts that don't resolve in DNS
|
|
*/
|
|
if (is_array(gethostbynamel($host)))
|
|
$this->f_host = $host;
|
|
else
|
|
throw new Exception(func_name() . ": cannot resolve host name \"$host\"");
|
|
|
|
$this->applyEvent('check_params');
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* setter for f_user
|
|
*
|
|
* @param string $user
|
|
* @return ftp_client
|
|
*/
|
|
public function set_user($user = 'anonymous') {
|
|
$this->f_user = $user;
|
|
$this->applyEvent('check_params');
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* setter for f_pass
|
|
*
|
|
* @param string $pass
|
|
* @return ftp_client
|
|
*/
|
|
public function set_pass($pass = null) {
|
|
$this->f_pass = $pass;
|
|
$this->applyEvent('check_params');
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* callback is invoked at the end of get, put
|
|
* and before and after continue
|
|
*
|
|
* @param string $callback
|
|
*/
|
|
public function set_callback($callback) {
|
|
if ($callback && !function_exists($callback))
|
|
throw new Exception(func_name() . ": cannot use undefined function $callback as callback");
|
|
else
|
|
$this->f_callback = $callback;
|
|
/**
|
|
* this setter does not cause a state change, so no call to applyEvent
|
|
*/
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* implement change remote directory
|
|
* @return boolean
|
|
*/
|
|
protected function f_chdir() {
|
|
$ret = ftp_chdir($this->f_conn, $this->f_pwd);
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* change remote directory
|
|
*
|
|
* @param string $pwd
|
|
* @return boolean
|
|
*/
|
|
public function chdir($pwd = null) {
|
|
$this->f_pwd = $pwd;
|
|
$ret = $this->applyEvent('chdir');
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* setter for f_file
|
|
*
|
|
* @param string $file
|
|
* @return ftp_client
|
|
*/
|
|
public function set_file($file = null) {
|
|
$this->f_file = $file;
|
|
if (!isset($this->f_localfile))
|
|
$this->f_localfile = $file;
|
|
|
|
$this->applyEvent('check_params');
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* setter for f_localfile
|
|
*
|
|
* @param string $file
|
|
* @return ftp_client
|
|
*/
|
|
public function set_localfile($file = null) {
|
|
$this->f_localfile = $file;
|
|
$this->applyEvent('check_params');
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* does the instance have all necessary info for a FTP transfer ?
|
|
*
|
|
* @return boolean
|
|
*/
|
|
protected function f_check_params() {
|
|
$ret = isset($this->f_host)
|
|
&& isset($this->f_user)
|
|
&& isset($this->f_pass)
|
|
&& isset($this->f_file)
|
|
&& isset($this->f_localfile)
|
|
;
|
|
// echo func_name() . ", ret = " . ($ret ? 'TRUE' : 'FALSE') . PHP_EOL;
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* implementation of connect
|
|
*
|
|
* @return boolean
|
|
*/
|
|
protected function f_connect() {
|
|
// echo func_name() . "\n";
|
|
$this->f_conn = ftp_connect($this->f_host); // default port, default timeout
|
|
$ret = is_resource($this->f_conn);
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* implementation of close
|
|
* @return boolean
|
|
*/
|
|
protected function f_close() {
|
|
// echo func_name() . "\n";
|
|
$ret = ftp_close($this->f_conn);
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* implementation of login
|
|
* @return boolean
|
|
*/
|
|
protected function f_login() {
|
|
// echo func_name() . "\n";
|
|
$ret = ftp_login($this->f_conn, $this->f_user, $this->f_pass);
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* implementation of get
|
|
*
|
|
* @return int FTP_FINISHED | FTP_MOREDATA | FTP_FAILED
|
|
*/
|
|
protected function f_get() {
|
|
// echo func_name() . "\n";
|
|
$this->f_fp = fopen($this->f_localfile, "wb");
|
|
if (!is_resource($this->f_fp)) {
|
|
$ret = FTP_FAILED;
|
|
throw new Exception(func_name() . ": could not create local file $this->f_file");
|
|
}
|
|
|
|
$this->f_goal = ftp_size($this->f_conn, $this->f_file);
|
|
|
|
$ret = ftp_nb_fget($this->f_conn, $this->f_fp, $this->f_file, FTP_BINARY);
|
|
if ($ret == FTP_FINISHED)
|
|
fclose($this->f_fp);
|
|
call_user_func($this->f_callback, $this, 'post');
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* implementation of continue
|
|
* @return int FTP_FINISHED | FTP_MOREDATA | FTP_FAILED
|
|
*/
|
|
protected function f_continue() {
|
|
if ($this->f_callback)
|
|
call_user_func($this->f_callback, $this, 'pre');
|
|
$ret = ftp_nb_continue($this->f_conn);
|
|
if ($ret == FTP_FINISHED)
|
|
fclose($this->f_fp);
|
|
if ($this->f_callback)
|
|
call_user_func($this->f_callback, $this, 'post');
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* interface to connect
|
|
* @return void
|
|
*/
|
|
public function connect() {
|
|
// echo func_name() . "\n";
|
|
return $this->applyEvent('connect');
|
|
}
|
|
|
|
/**
|
|
* interface to login
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function login() {
|
|
// echo func_name() . "\n";
|
|
return $this->applyEvent('login');
|
|
}
|
|
|
|
/**
|
|
* interface to close
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function close() {
|
|
// echo func_name() . "\n";
|
|
return $this->applyEvent('close');
|
|
}
|
|
|
|
/**
|
|
* get a file using previously defined parameters
|
|
* @return int FTP_FAILED | FTP_MOREDATA | FTP_FINISHED
|
|
*/
|
|
public function get() {
|
|
// echo func_name() . "\n";
|
|
return $this->applyEvent('get');
|
|
}
|
|
|
|
/**
|
|
* continue a current transfer
|
|
*
|
|
* @param string $callback name of function to be called before and after
|
|
* @return int FTP_FINISHED | FTP_MOREDATA | FTP_FAILED
|
|
*/
|
|
// continue is a php reserved word
|
|
public function cont() {
|
|
// echo func_name() . "\n";
|
|
$ret = $this->applyEvent('continue');
|
|
$ret = $ret->fsmState;
|
|
return $ret;
|
|
}
|
|
|
|
public function get_progress() {
|
|
if ((!$this->fState == 'active') || (!is_resource($this->f_fp)))
|
|
$ret = 0;
|
|
else {
|
|
$pos = ftell($this->f_fp);
|
|
$ret = $pos / $this->f_goal;
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/* missing: put*/
|
|
}
|