Browse Source

Versions working together with today's bo/up/ingram importer

Frederic G. Marand 17 years ago
parent
commit
ba4a23d06a
5 changed files with 730 additions and 2 deletions
  1. 32 0
      boxed_scalars.php
  2. 51 0
      misc.php
  3. 3 2
      u_date_code.php
  4. 167 0
      u_fsm.php
  5. 477 0
      u_ftp.php

+ 32 - 0
boxed_scalars.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * This class enables use of scalar values with classes implementing iComparable
+ * $Id: boxed_scalars.php,v 1.1 2006-12-03 23:20:08 marand Exp $
+ */
+
+require_once('misc.php'); // for iComparable
+
+class boxed_int implements iComparable
+  {
+  protected $value;
+
+  function __construct($n)
+    {
+    $this->value = $n;
+    }
+
+  function cmp(iComparable $other)
+    {
+    if ($this->value < $other->value)
+      return -1;
+    elseif ($this->value > $other->value)
+      return 1;
+    else
+      return 0;
+    }
+
+  function as_int()
+    {
+    return $this->value;
+    }
+  }

+ 51 - 0
misc.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * The Zoo
+ * $Id: misc.php,v 1.1 2006-12-03 23:20:08 marand Exp $
+ */
+interface iComparable
+  {
+  /**
+   * Compare the current instance
+   *
+   * @param iComparable $other
+   */
+  public function cmp(iComparable $other);
+  }
+
+function get_temp_dir()
+  {
+  return 'e:/src/OsinetOffice/tmp';
+  }
+
+function output_encoder($s)
+  {
+  return iconv('UTF-8', 'IBM850', $s);
+  }
+
+// Activate the OB handler:
+ob_start("output_encoder", 2);
+
+/**
+ * automatically find the glade file for a class file
+ *
+ */
+function load_glade()
+  {
+  return new GladeXML(basename($_SERVER['PHP_SELF'], 'php') . 'glade');
+  }
+
+/**
+ * returns the name of the invoking function/method
+ * if it's a method, it is prefixed by the class name
+ *
+ * @return string
+ */
+function func_name()
+  {
+  $trace = debug_backtrace();
+  $func = $trace[1]['function'];
+  if (isset($trace[1]['class']))
+    $func = $trace[1]['class'] . '::' . $func;
+  return $func;
+  }

+ 3 - 2
u_date_code.php

@@ -4,7 +4,7 @@
  * format: [y]ymdd
  * 4-characters until 2009-12-31, 5 afterwards
  *
- * $Id: u_date_code.php,v 1.1 2006-11-26 15:48:35 marand Exp $
+ * $Id: u_date_code.php,v 1.2 2006-12-03 23:20:08 marand Exp $
  */
 
 require_once('misc.php');          // needed for "iComparable" interface
@@ -84,7 +84,7 @@ class osinet_date_code implements iComparable
 
     $month = chr(ord('@') + $month);
 
-    return sprintf('%d%s%02d', $year, $month, $day);
+    $this->f_value = sprintf('%d%s%02d', $year, $month, $day);
     }
 
   private function set_value($date_code)
@@ -102,6 +102,7 @@ class osinet_date_code implements iComparable
 
   protected function __get($nm)
     {
+    // echo "osinet_date_code::__get($nm)\n";
     if ($nm <> 'value')
       throw new Exception("$nm: undefined property for " . get_class($this));
 

+ 167 - 0
u_fsm.php

@@ -0,0 +1,167 @@
+<?php
+/**
+ * Finite state machine skeleton
+ * embodying a Moore FSM
+ * (c) 2006 Ouest Systèmes Informatiques (OSI)
+ * Licensed under the CeCILL 2.0 license
+ *
+ * $Id: u_fsm.php,v 1.1 2006-12-03 23:20:08 marand Exp $
+ */
+require_once('misc.php'); // for func_name()
+
+abstract class fsm
+  {
+  /**
+   * the current state of the object
+   * @var string
+   */
+  protected $f_state;
+
+  /**
+   * Transitions holds the transitions table
+   * state1
+   *   event1
+   *     result1
+   *     action1
+   *   event2
+   *     ...
+   *   ..
+   * ..
+   * @var array
+   */
+  protected $f_transitions = null;
+
+  /**
+   * constructor initializes the FSM to the first
+   * state in the transitions table
+   * @return void
+   */
+  public function __construct()
+    {
+    $this->_check_transitions();
+
+    reset($this->f_transitions);
+    $x = each($this->f_transitions);
+    $x = $x[0];
+    $this->f_state = $x;
+    }
+
+  /**
+   * make sure a transitions graph has been defined
+   * @return void
+   */
+  private function _check_transitions()
+    {
+    if (!isset($this->f_transitions))
+      throw new Exception('No FSM processing is allowed without a transitions table');
+    }
+
+  /**
+   * getter for f_state
+   */
+  public function get_state()
+    {
+    return $this->f_state;
+    }
+
+  /**
+   * return the list of events accepted in the current state
+   * @return array
+   */
+  public function get_accepted_events()
+    {
+    $this->_check_transitions();
+
+    $events = array_keys($this->f_transitions[$this->f_state]);
+    // echo func_name() . ": " . print_r($events, true). "\n";
+    return $events;
+    }
+
+  /**
+   * return the list of outcome accepted in the current state for the give event
+   * @param string $event_name
+   * @param mixed $outcome
+   * @return array
+   */
+  public function get_accepted_outcomes($event_name)
+    {
+    // echo func_name() . "\n";
+    $this->_check_transitions();
+
+    if (!$this->is_event_allowed($event_name))
+      throw new Exception(func_name() . ": event \"$event_name\" not allowed in state \"$this->f_state\"\n");
+
+    $outcomes = array_keys($this->f_transitions[$this->f_state][$event_name]);
+    //echo "outcomes for event $event_name: " . print_r($outcomes, true) . "\n";
+    return $outcomes;
+    }
+
+  /**
+   * is this event accepted in the current state
+   * the FSM is in ?
+   *
+   * @param string $event_name
+   * @return boolean
+   */
+  public function is_event_allowed($event_name)
+    {
+    // echo func_name() . "($event_name)";
+    $this->_check_transitions();
+
+    $ret = in_array($event_name, $this->get_accepted_events());
+    // echo ", result = <$ret>\n";
+    return $ret;
+    }
+
+  /**
+   * is a given outcome available for a given event,
+   * considering the current context ?
+   *
+   * @param string $event_name
+   * @param mixed $outcome
+   * @return boolean
+   */
+  public function is_outcome_allowed($event_name, $outcome)
+    {
+    $this->_check_transitions();
+
+    if (!$this->is_event_allowed($event_name))
+      return false;
+
+    $ret = array_key_exists($outcome, $this->get_accepted_outcomes($event_name));
+    return $ret;
+    }
+
+  /**
+   * apply an event
+   *
+   * @param string $event_name
+   * @param array $params the
+   * @return string resulting state
+   */
+  public function apply_event($event_name)
+    {
+    //echo func_name() . "\n";
+
+    if (! $this->is_event_allowed($event_name))
+      throw new Exception(func_name()
+        . ":  Event \"$event_name\" not accepted in current state \"$this->f_state\"");
+
+    $method_name = "f_$event_name";
+    $outcomes = $this->get_accepted_outcomes($event_name);
+
+    $result = $this->$method_name();
+
+    if (!in_array($result, $outcomes))
+      throw new Exception(func_name()
+        . ": event guard. Transition on \"$event_name\" return invalid result: "
+        . var_dump($result)
+        . "\n");
+
+    $this->f_state = $this->f_transitions[$this->f_state][$event_name][$result];
+    // echo "new state: $this->f_state\n";
+
+    return $result;
+    }
+
+  }

+ 477 - 0
u_ftp.php

@@ -0,0 +1,477 @@
+<?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.1 2006-12-03 23:20:08 marand Exp $
+ */
+
+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->f_transitions = 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->apply_event('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->apply_event('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->apply_event('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 apply_event
+     */
+
+    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->apply_event('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->apply_event('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->apply_event('check_params');
+
+    return $this;
+    }
+
+
+  /**
+   * does the instance have all necessary info for a FTP transfer ?
+   *
+   * @return boolean
+   */
+  protected function f_check_params()
+    {
+    // echo func_name() . "\n";
+    $ret = isset($this->f_host)
+      && isset($this->f_user)
+      && isset($this->f_pass)
+      && isset($this->f_file)
+      && isset($this->f_localfile)
+      ;
+    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->apply_event('connect');
+    }
+
+  /**
+   * interface to login
+   *
+   * @return boolean
+   */
+  public function login()
+    {
+    // echo func_name() . "\n";
+    return $this->apply_event('login');
+    }
+
+  /**
+   * interface to close
+   *
+   * @return boolean
+   */
+  public function close()
+    {
+    echo func_name() . "\n";
+    return $this->apply_event('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->apply_event('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
+   */
+  public function cont() // continue is a php reserved word
+    {
+    // echo func_name() . "\n";
+    return $this->apply_event('continue');
+    }
+
+  public function get_progress()
+    {
+    if (!$this->f_state == 'active')
+      $ret = 0;
+    else
+      {
+      $pos = ftell($this->f_fp);
+      $ret = $pos / $this->f_goal;
+      }
+    return $ret;
+    }
+
+/*put*/
+  }
+
+/**
+ * test script
+ */
+/*
+ob_end_flush();
+
+$ftp = new ftp_client();
+$ftp->set_host('osinet.typhon.org')
+    ->set_user('osinet')
+    ->set_pass('eev6ohbahX')
+    ->set_file('price.xml')
+    ->set_callback('progress');
+
+if ($ftp->is_event_allowed('connect'))
+  {
+  echo "Connecting...";
+  $ftp->connect();
+  }
+else
+  die('connect not allowed');
+
+if($ftp->is_event_allowed('login'))
+  {
+  echo "success\nLogging in...";
+  $ftp->login();
+  }
+else
+  die("failure\n");
+
+if ($ftp->is_event_allowed('get'))
+  {
+  echo "success\nsetting directory...";
+  $ftp->chdir('/osinet.fr/www/code');
+  }
+else
+  die("failure\n");
+
+if ($ftp->is_event_allowed('get'))
+  {
+  echo "success\nfetching";
+  $ftp->get();
+  }
+else
+  die("failure\n");
+
+do
+  {
+  $sts = $ftp->cont();
+  if ($sts == FTP_FAILED)
+    throw new Exception("main: continue failed\n");
+  } while ($sts == FTP_MOREDATA);
+*/