Browse Source

Restructured file layout to incorporate a PSR0 version of Class_Grapher.

- 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.
Frederic G. MARAND 12 years ago
parent
commit
7d0f0d44ea

+ 2 - 0
Makefile

@@ -0,0 +1,2 @@
+clean:
+	find . -name "*.gv" -o -name "*.svg" -o -name php_errors.log -delete

+ 28 - 0
OSInet/Class_Grapher/ClassInstance.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace OSInet\Class_Grapher;
+
+/**
+ * Representation of a "class" symbol in source code.
+ */
+class ClassInstance extends InterfaceInstance {
+  public $interfaces = array();
+
+  /**
+   * @param \PGPClass $symbol
+   *   The symbol returned by grammar_parser.
+   * @param int $implicit
+   * @param Logger $logger
+   */
+  public function __construct(\PGPClass $symbol, $implicit = 0, Logger $logger) {
+    parent::__construct($symbol, $implicit, $logger);
+    $this->interfaces = $symbol->implements;
+  }
+
+  public function basicAttributes() {
+    $ret = array(
+      'shape' => 'rect',
+    );
+    return $ret;
+  }
+}

+ 262 - 0
OSInet/Class_Grapher/Graph.php

@@ -0,0 +1,262 @@
+<?php
+
+namespace OSInet\Class_Grapher;
+
+/**
+ * Graph builder.
+ */
+class Graph {
+  /**
+   * A PEAR class.
+   *
+   * @var Image_GraphViz
+   */
+  public $g;
+
+  /**
+   * @var Logger
+   */
+  public $logger = NULL;
+
+  var $runtimeClasses = array();
+  var $runtimeInterfaces = array();
+  var $classes = array();
+  var $interfaces = array();
+  var $registeredClasses = array();
+  var $registeredInterfaces = array();
+
+  /**
+   * Represent attributes as a string in GraphViz format.
+   *
+   * @param array $attributes
+   */
+  public function attributes(array $attributes) {
+    $attributes = $this->g->_escapeArray($attributes);
+    $ret = array();
+    foreach ($attributes as $name => $value) {
+      $ret[] = "$name=$value";
+    }
+    $ret = implode(',', $ret);
+    return $ret;
+  }
+
+  /**
+   * Shortcut to invoke logger->message().
+   *
+   * @param string $message
+   * @param int $level
+   */
+  public function debug($message = '\n', $level = LOG_DEBUG) {
+    $this->logger->debug($message, $level);
+  }
+
+  /**
+   * Build a list of files to scan below a given base.
+   *
+   * TODO support base as a file instead of a directory.
+   *
+   * @param string $base
+   *   A starting path for file lookup.
+   * @param array $blackList
+   *   An array of file names to reject, whatever their path.
+   * @param string $pattern
+   *   A regexp for file paths to match.
+   *
+   * @return array
+   *   An array of compliant file paths.
+   */
+  public function getFiles($base, $blackList = array(),
+    $pattern = '/.*\.(inc|module|php)$/') {
+
+    $dir = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($base,
+      \FilesystemIterator::KEY_AS_PATHNAME  | \FilesystemIterator::SKIP_DOTS));
+    $files = array();
+    while ($dir->valid()) {
+      $name = $dir->key();
+      if (preg_match($pattern, $name) && !in_array(basename($name), $blackList)) {
+        $files[] = $name;
+      }
+      elseif ($this->logger->debugLevel >= LOG_INFO) {
+        if (in_array(basename($name), $blackList)) {
+          $this->debug("$name blacklisted.\n", LOG_INFO);
+        }
+        else {
+          $this->debug("$name does not match source paths.\n", LOG_DEBUG);
+        }
+      }
+      $dir->next();
+    }
+
+    $files = array_unique($files);
+    sort($files);
+    return $files;
+  }
+
+  /**
+   * Constructor helper: initialize GraphViz settings.
+   */
+  public function initGraph() {
+    $this->g = new \Image_Graphviz();
+    $this->g->setDirected(TRUE);
+    $this->g->setAttributes(array(
+        'rankdir' => 'LR',
+        'overlap' => FALSE,
+    ));
+  }
+
+  public function registerClass(ClassInstance $class) {
+    $this->debug('  Registering class '. $class->name . "\n");
+    if (!empty($class->parent)) {
+      if (!isset($this->registeredClasses[$class->parent])) {
+        $this->debug("    Registering parent ". $class->parent . " for " . $class->name . "\n");
+        $gpParent = new \PGPClass($class->parent);
+        $cpParent = new ClassInstance($gpParent, 1, $this->logger);
+        $this->registerClass($cpParent);
+      }
+    }
+    foreach ($class->interfaces as $interface) {
+      if (!isset($this->registeredClasses[$interface])) {
+        $this->debug("    Registering interface $interface for {$class->name}\n");
+        $gpInterface = new \PGPClass($interface);
+        $cpInterface = new InterfaceInstance($gpInterface, 1, $this->logger);
+        $this->registerInterface($cpInterface);
+      }
+    }
+
+    // At this point, all ascendants are registered.
+    if (!isset($this->registeredClasses[$class->name])) {
+      $this->registeredClasses[$class->name] = $class;
+    }
+    // Override implicit status of a previously declared class if needed.
+    if (!$class->implicit) {
+      $this->registeredClasses[$class->name]->implicit = 0;
+    }
+  }
+
+  public function registerInterface(InterfaceInstance $interface) {
+    $this->debug("Registering interface {$interface->name}\n");
+    if (!empty($interface->parent)) {
+      if (!isset($this->registeredInterfaces[$interface->parent])) {
+        $this->debug("    Registering parent ". $interface->parent . " for " . $interface->name . "\n");
+        $gpParent = new \PGPClass($interface->parent);
+        $cpParent = new InterfaceInstance($gpParent, 1, $this->logger);
+        $this->registerInterface($cpParent);
+      }
+    }
+    // At this point, all ascendants are registered.
+    if (!isset($this->registeredInterfaces[$interface->name])) {
+      $this->registeredInterfaces[$interface->name] = $interface;
+    }
+    // Override implicit status of a previously declared interface if needed.
+    if (!$interface->implicit) {
+      $this->registeredInterfaces[$interface->name]->implicit = 0;
+    }
+  }
+
+  /**
+   * Symbol extractor using grammar_parser.
+   *
+   * @param string $base
+   */
+  public function extractGrammar($base) {
+    // These files cause a segfault during parse, so we skip them for now.
+    // Possible patch at http://drupal.org/node/1485370
+    $blackList = array(
+      'RouteCollection.php',
+      'HeaderBag.php',
+      'NativeSessionHandler.php',
+      'SimpleXMLElement.php',
+      'ContainerBuilderTest.php',
+      'YamlFileLoaderTest.php',
+      'ResponseTest.php',
+      'ConfigDataCollectorTest.php',
+      'KernelTest.php',
+      'UrlMatcherTest.php',
+    );
+    $files = $this->getFiles($base, $blackList);
+
+    foreach ($files as $filename) {
+      $source = file_get_contents($filename, FALSE);
+      $reader = new \PGPReader($source);
+      unset($source);
+      $this->debug("Parsing $filename\n", LOG_INFO);
+      $reader->buildGrammar();
+
+      foreach ($reader->getInterfaces() as $interface) {
+        $this->registerInterface(new InterfaceInstance($interface->data, 0, $this->logger));
+      }
+
+      foreach ($reader->getClasses() as $class) {
+        $this->registerClass(new ClassInstance($class->data, 0, $this->logger));
+      }
+
+      $reader->reset();
+      unset($reader);
+    }
+  }
+
+  public function __construct($base, Logger $logger) {
+    $this->logger = $logger;
+    $this->extractGrammar($base);
+    $this->initGraph();
+  }
+
+  public function implementsAttributes() {
+    $attributes = array(
+      'arrowhead' => 'empty',
+      'arrowtail' => 'none',
+      'style' => 'dotted',
+    );
+    $attributes = $this->g->_escapeArray($attributes);
+    return $attributes;
+  }
+
+  public function buildSymbols($collection = array(), $kind = NULL) {
+    foreach ($collection as $name => $symbol) {
+      $this->debug("Building $kind $name\n");
+      $this->g->addNode($name, $symbol->attributes());
+      if ($symbol->parent) {
+        $this->g->addEdge(array($name => $symbol->parent));
+      }
+
+      if (empty($symbol->interfaces)) {
+        continue;
+      }
+
+      foreach ($symbol->interfaces as $interface_name) {
+        $this->g->addEdge(array($name => $interface_name), $this->implementsAttributes());
+      }
+    }
+  }
+
+  /**
+   * Build the graph once extraction has been performed.
+   */
+  public function build($imager = 'dot', $format = 'svg') {
+    $this->debug("Starting build for "
+      . count($this->registeredInterfaces) . " interfaces and "
+      . count($this->registeredClasses) . " classes.\n",
+      WATCHDOG_INFO);
+
+    $this->buildSymbols($this->registeredInterfaces, 'interface');
+    $this->buildSymbols($this->registeredClasses, 'class');
+
+    if (empty($imager) || $imager == 'dump') {
+      // $this->debug(print_r($g->_getGroups(), TRUE));
+      $this->debug("Interfaces:\n");
+      $this->debug(print_r($this->registeredInterfaces, TRUE));
+      $this->debug("Classes\n");
+      $this->debug(print_r($this->registeredClasses, TRUE));
+      //$this->debug(print_r($this->g));
+      $ret = $this->g->parse();
+    }
+    else {
+      ob_start();
+      $this->g->image($format, $imager);
+      $ret = ob_get_contents();
+      ob_end_clean();
+    }
+
+    return $ret;
+  }
+}

+ 110 - 0
OSInet/Class_Grapher/InterfaceInstance.php

@@ -0,0 +1,110 @@
+<?php
+
+namespace OSInet\Class_Grapher;
+
+/**
+ * Representation of an "interface" symbol in source code.
+ */
+class InterfaceInstance {
+  const ORIGIN_UNKNOWN = NULL;
+  const ORIGIN_PHP = 'php';
+  const ORIGIN_EXTENSION = 'extension';
+  const ORIGIN_SCRIPT = 'script';
+
+  /**
+   * Was instance created just because it was referenced by another symbol ?
+   *
+   * @var int
+   *   0 (No) or 1 (Yes)
+   */
+  public $implicit;
+
+  /**
+   * Origin of symbol. Use the self::ORIGIN_* constants.
+   *
+   * @var int
+   */
+  public $origin;
+
+  /**
+   * @var Logger
+   */
+  public $logger = NULL;
+
+  public $name = '(none)';
+  public $parent = NULL;
+
+  /**
+   * @param \PGPClass $symbol
+   *   The interface symbole from grammar_parser.
+   * @param int $implicit
+   * @param Logger $logger
+   *
+   * @throws \Exception
+   */
+  public function __construct(\PGPClass $symbol, $implicit = 0, Logger $logger = NULL) {
+    $this->implicit = $implicit;
+    $this->logger = $logger;
+    $this->name = $symbol->name;
+    $this->parent = reset($symbol->extends);
+    if (empty($this->logger)) {
+      throw new \Exception('No logger in constructor.\n');
+    }
+    $this->origin = $this->getSymbolOrigin($symbol);
+  }
+
+  /**
+   * Shortcut for logger->message().
+   *
+   * @param string $message
+   * @param int $level
+   */
+  public function debug($message, $level = LOG_INFO) {
+    $this->logger->debug($message, $level);
+  }
+
+  public function getSymbolOrigin() {
+    $ret = self::ORIGIN_UNKNOWN;
+    try {
+      $r = new \ReflectionClass($this->name);
+      $this->debug("$this->name is introspectable.\n");
+    } catch (\ReflectionException $e) {
+      $this->debug("{$this->name} is userland, unshared with parser.\n");
+      $ret = self::ORIGIN_SCRIPT;
+    }
+
+    if (!isset($ret)) {
+      $extension = $r->getExtensionName();
+      if ($extension === FALSE) {
+        $ret = self::ORIGIN_SCRIPT;
+      }
+      else {
+        $this->implicit = 0;
+        $ret = ($extension == 'Core')
+          ? self::ORIGIN_PHP
+          : self::ORIGIN_EXTENSION . ':' . $extension;
+      }
+    }
+    return $ret;
+  }
+
+  public function basicAttributes() {
+    $ret = array(
+      'shape' => 'box',
+      'style' => 'rounded',
+    );
+    return $ret;
+  }
+
+  public function attributes() {
+    $ret = $this->basicAttributes();
+    if ($this->implicit) {
+      $ret['style'] = 'filled';
+      $ret['fillcolor'] = '#e0e0e0';
+    }
+    if ($this->origin != self::ORIGIN_SCRIPT) {
+      $ret['label'] = $this->name ."\n". $this->origin;
+    }
+    return $ret;
+  }
+}

+ 31 - 0
OSInet/Class_Grapher/Logger.php

@@ -0,0 +1,31 @@
+<?php
+/**
+ * @file
+ * Trivial logger for easier debug.
+ */
+
+namespace OSInet\Class_Grapher;
+
+class Logger {
+  var $debugLevel;
+
+  public function __construct($debugLevel) {
+    $this->debugLevel = $debugLevel;
+  }
+
+  /**
+   * Output a message on STDERR if its relevance is above minimum level.
+   *
+   * @param string $message
+   * @param int $level
+   *   Defined in RFC 3164, section 4.1.1 "Severity". But compare with Drupal 7
+   *   WATCHDOG_* constants in includes/bootstrap.inc for an explanation about
+   *   syslog constants in PHP.
+   */
+  public function debug($message = "\n", $level = LOG_INFO) {
+    if ($level <= $this->debugLevel) {
+      fputs(STDERR, $message);
+      fflush(STDERR);
+    }
+  }
+}

+ 110 - 0
OSInet/Class_Grapher/classgrapher.drush.inc

@@ -0,0 +1,110 @@
+<?php
+/**
+ * @file
+ * Drush commands for Class Grapher
+ *
+ * - hier: plot class inheritance on a site
+ *
+ * - Idea: use Javascript Infovis Toolkit instead of GraphViz for nicer
+ *   graphs, less server load, and user interaction.
+ */
+
+/**
+ * Implement hook_drush_command().
+ *
+ * Declare classgraph (hier) command.
+ */
+function classgrapher_drush_command() {
+  $items = array();
+
+  $items['classgraph'] = array(
+    'description' => 'Graph PHP entities',
+    'aliases' => array('hier'),
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
+    'core' => array('7+'),
+    'drupal dependencies' => array(),
+    'arguments' => array(
+      'base' => 'The base directory below which to parse source files.',
+    ),
+    'options' => array(
+      'cgdebug' => array(
+        'description' => 'Debug level: 0 to 7. Default to 6.',
+      ),
+      'cgimager' => array(
+        'description' => 'Imager to use. Default to "dot". Use "dump" for a debug dump without GraphViz generation. Alternate imagers like circo, (s)fdp or twopi are converted to dot.',
+        'example-value' => 'dump,dot,neato',
+      ),
+      'cgformat' => array(
+        'description' => 'Image format. Default to "svg". Not available with the dump imager.',
+        'example-value' => implode(',', _drush_classgrapher_get_formats()),
+      ),
+    ),
+  );
+
+  return $items;
+}
+
+/**
+ * Helper to enumerate GraphViz format filters on the current system.
+ *
+ * @return array
+ *   An array of format names.
+ */
+function _drush_classgrapher_get_formats() {
+  $dotCommand = 'dot -Tinvalid';
+  $descriptorSpec = array(
+      0 => array('pipe', 'r'),
+      1 => array('pipe', 'w'),
+      2 => array('pipe', 'w'),
+  );
+
+  $process = proc_open($dotCommand, $descriptorSpec, $pipes, NULL, NULL);
+  if (!is_resource($process)) {
+    drush_set_error('classgrapher', 'GraphViz not found.');
+  }
+
+  fclose($pipes[0]);
+  fclose($pipes[1]);
+  $stderr = stream_get_contents($pipes[2]);
+  proc_close($process);
+
+  $sts = preg_match('/(.+):( .* )*/', $stderr, $matches);
+  if (!$sts || count($matches) != 3) {
+    drush_set_error('classgrapher', 'GraphViz did not return a usable formats list.');
+  }
+  $formats = explode(' ', trim($matches[2]));
+  return $formats;
+}
+
+/**
+ * Validation callback for classgraph.
+ */
+function drush_classgrapher_classgraph_validate() {
+  if (!@include_once 'Image/GraphViz.php') {
+    drush_set_error('classgrapher', 'PEAR Image_Graphviz not found.');
+  }
+}
+
+/**
+ * Command callback for classgraph.
+ */
+function drush_classgrapher_classgraph($base) {
+  $logger = new ClassGrapherLogger();
+
+  $imager = drush_get_option('cgimager');
+  if (!isset($imager) || $imager === FALSE) {
+    $debug = 'dot';
+  }
+
+  $debug = drush_get_option('cgdebug');
+  if (!isset($debug) || $debug === FALSE) {
+    $this->debugLevel = WATCHDOG_INFO;
+  }
+  else {
+    $this->debugLevel = (int) $debug;
+  }
+
+  $info = libraries_load('grammar_parser');
+  $graph = new ClassGrapherGraph($base, $logger);
+  echo $graph->build($imager);
+}

+ 1 - 1
Background_Application.php → OSInet/Finite_State_Machine/BackgroundApplication.php

@@ -19,7 +19,7 @@ error_reporting(E_ALL|E_STRICT);
  * Concrete implementations should include a constructor defining the
  * backgroundGoals array along the FSM graph.
  */
-abstract class Background_Application {
+abstract class BackgroundApplication {
   /**
    * Trace operation to stdout
    *

+ 288 - 0
OSInet/Finite_State_Machine/help/fsm_sample.dia

@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Arrière-plan" visible="true" active="true">
+    <dia:object type="UML - State" version="0" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.7,7.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.65,7.85;7.97,12.15"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.7,7.9"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="5.2200000000000006"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="4.2000000000000002"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#State 1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.31,8.995"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="entry_action">
+        <dia:string>#Entry1#</dia:string>
+      </dia:attribute>
+      <dia:attribute name="do_action">
+        <dia:string>#Action1#</dia:string>
+      </dia:attribute>
+      <dia:attribute name="exit_action">
+        <dia:string>#Exit1#</dia:string>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="UML - State Term" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="9.6,2"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.55,1.95;10.65,3.05"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="9.6,2"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="is_final">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="UML - State Term" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.2,20.4"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.15,20.35;9.75,21.95"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="8.2,20.4"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="1.5"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="1.5"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffff"/>
+      </dia:attribute>
+      <dia:attribute name="is_final">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="UML - Transition" version="2" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10.1,3.05027"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="4.81,3.00027;10.6,7.95"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="10.1,3.05027"/>
+        <dia:point val="10.1,5.47513"/>
+        <dia:point val="5.31,5.47513"/>
+        <dia:point val="5.31,7.9"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="orth_autoroute">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="text_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="trigger">
+        <dia:string>##</dia:string>
+      </dia:attribute>
+      <dia:attribute name="action">
+        <dia:string>##</dia:string>
+      </dia:attribute>
+      <dia:attribute name="guard">
+        <dia:string>##</dia:string>
+      </dia:attribute>
+      <dia:attribute name="trigger_text_pos">
+        <dia:point val="7.205,3.97513"/>
+      </dia:attribute>
+      <dia:attribute name="guard_text_pos">
+        <dia:point val="7.205,4.97513"/>
+      </dia:attribute>
+      <dia:attribute name="direction_inverted">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O1" connection="8"/>
+        <dia:connection handle="1" to="O0" connection="1"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="UML - Transition" version="2" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.31,12.1"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="4.81,12.05;9.45,20.45"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="5.31,12.1"/>
+        <dia:point val="5.31,16.25"/>
+        <dia:point val="8.95,16.25"/>
+        <dia:point val="8.95,20.4"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+      </dia:attribute>
+      <dia:attribute name="orth_autoroute">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="text_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000"/>
+      </dia:attribute>
+      <dia:attribute name="trigger">
+        <dia:string>##</dia:string>
+      </dia:attribute>
+      <dia:attribute name="action">
+        <dia:string>##</dia:string>
+      </dia:attribute>
+      <dia:attribute name="guard">
+        <dia:string>##</dia:string>
+      </dia:attribute>
+      <dia:attribute name="trigger_text_pos">
+        <dia:point val="6.63,14.75"/>
+      </dia:attribute>
+      <dia:attribute name="guard_text_pos">
+        <dia:point val="6.63,15.75"/>
+      </dia:attribute>
+      <dia:attribute name="direction_inverted">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O0" connection="6"/>
+        <dia:connection handle="1" to="O2" connection="1"/>
+      </dia:connections>
+    </dia:object>
+  </dia:layer>
+</dia:diagram>

+ 297 - 0
OSInet/Finite_State_Machine/help/trans.ods

@@ -0,0 +1,297 @@
+Array
+(
+    [init] => Array
+        (
+            [CheckParameters] => Array
+                (
+                    [true] => Array
+                        (
+                            [0] => offline
+                            [1] => 
+                        )
+
+                    [false] => Array
+                        (
+                            [0] => init
+                            [1] => 
+                        )
+
+                )
+
+        )
+
+    [offline] => Array
+        (
+            [CheckParameters] => Array
+                (
+                    [true] => Array
+                        (
+                            [0] => offline
+                            [1] => 
+                        )
+
+                    [false] => Array
+                        (
+                            [0] => init
+                            [1] => 
+                        )
+
+                )
+
+            [Connect] => Array
+                (
+                    [true] => Array
+                        (
+                            [0] => online
+                            [1] => 
+                        )
+
+                    [false] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                )
+
+            [Progress] => Array
+                (
+                    [] => Array
+                        (
+                            [0] => offline
+                            [1] => Connect
+                        )
+
+                )
+
+        )
+
+    [online] => Array
+        (
+            [Close] => Array
+                (
+                    [true] => Array
+                        (
+                            [0] => offline
+                            [1] => 
+                        )
+
+                    [false] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                )
+
+            [Login] => Array
+                (
+                    [true] => Array
+                        (
+                            [0] => live
+                            [1] => 
+                        )
+
+                    [false] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                )
+
+            [Progress] => Array
+                (
+                    [] => Array
+                        (
+                            [0] => online
+                            [1] => Login
+                        )
+
+                )
+
+        )
+
+    [live] => Array
+        (
+            [Close] => Array
+                (
+                    [true] => Array
+                        (
+                            [0] => offline
+                            [1] => 
+                        )
+
+                    [false] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                )
+
+            [Chdir] => Array
+                (
+                    [true] => Array
+                        (
+                            [0] => ready
+                            [1] => 
+                        )
+
+                    [false] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                )
+
+            [Get] => Array
+                (
+                    [FTP_FINISHED] => Array
+                        (
+                            [0] => live
+                            [1] => 
+                        )
+
+                    [FTP_FAILED] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                    [FTP_MOREDATA] => Array
+                        (
+                            [0] => active
+                            [1] => 
+                        )
+
+                )
+
+            [Put] => Array
+                (
+                    [FTP_FINISHED] => Array
+                        (
+                            [0] => live
+                            [1] => 
+                        )
+
+                    [FTP_FAILED] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                    [FTP_MOREDATA] => Array
+                        (
+                            [0] => active
+                            [1] => 
+                        )
+
+                )
+
+            [Progress] => Array
+                (
+                    [] => Array
+                        (
+                            [0] => live
+                            [1] => Chdir
+                        )
+
+                )
+
+        )
+
+    [ready] => Array
+        (
+            [Get] => Array
+                (
+                    [FTP_FINISHED] => Array
+                        (
+                            [0] => live
+                            [1] => Close
+                        )
+
+                    [FTP_MOREDATA] => Array
+                        (
+                            [0] => active
+                            [1] => 
+                        )
+
+                    [FTP_FAILED] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                )
+
+            [Progress] => Array
+                (
+                    [] => Array
+                        (
+                            [0] => ready
+                            [1] => Get
+                        )
+
+                )
+
+        )
+
+    [active] => Array
+        (
+            [close] => Array
+                (
+                    [true] => Array
+                        (
+                            [0] => offline
+                            [1] => 
+                        )
+
+                    [false] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                )
+
+            [Continue] => Array
+                (
+                    [FTP_FINISHED] => Array
+                        (
+                            [0] => live
+                            [1] => Close
+                        )
+
+                    [FTP_FAILED] => Array
+                        (
+                            [0] => unsafe
+                            [1] => 
+                        )
+
+                    [FTP_MOREDATA] => Array
+                        (
+                            [0] => active
+                            [1] => 
+                        )
+
+                )
+
+            [Progress] => Array
+                (
+                    [] => Array
+                        (
+                            [0] => active
+                            [1] => Continue
+                        )
+
+                )
+
+        )
+
+    [unsafe] => Array
+        (
+        )
+
+)

BIN
OSInet/Finite_State_Machine/help/trans.odt


+ 52 - 0
apps/class_grapher/class_grapher_demo.php

@@ -0,0 +1,52 @@
+<?php
+/**
+ * This code uses several external classes:
+ *
+ * - PSR0-compliant
+ *   - OSInet Class_Grapher package.
+ * - PEAR
+ *   - Image_GraphViz
+ * - Miscellaneous
+ *   - grammar_parser package 7.x -
+ *     - http://drupal.org/project/grammar_parser
+ *     - does not depend on Drupal, but can be used within Drupal/Drush either
+ *       as a module or a Libraries-compatible PHP library.
+ *
+ * It also depends on
+ * - GraphViz - http://www.graphviz.org/
+ * - PHP 5.3
+ */
+// To use, adjust the grammar parser path to your deployment.
+$gpPath = '../../d7/sites/all/libraries/grammar_parser/engine';
+
+use \OSInet\Class_Grapher\Logger;
+use \OSInet\Class_Grapher\Graph;
+
+// Image_GraphViz throws notices, so keep it quiet.
+error_reporting(E_WARNING);
+
+// Include the OSInet PSR0 autoloader.
+require __DIR__ .'/../../misc/psr0.php';
+
+// Load Image_GraphViz: PEAR, not PSR0.
+require 'Image/GraphViz.php';
+
+// Load grammar_parser. Not autoloadable.
+$gpFiles = array('parser', 'editor', 'list', 'object', 'reader', 'writer');
+foreach ($gpFiles as $file) {
+  require "$gpPath/$file.inc";
+}
+
+// Initialize PSR0 autoloader for remaining classes.
+spl_autoload_register('psr0_autoload', TRUE);
+
+// Suggestion: look at Drupal 8 core/lib/Drupal/Core/Database directory
+$base = $argv[1];
+
+$logger = new Logger(LOG_DEBUG);
+$imager = 'dot';
+$format = 'svg';
+
+$graph = new Graph($base, $logger);
+echo $graph->build($imager, $format);
+

+ 29 - 0
apps/ftp/ftp.php

@@ -0,0 +1,29 @@
+<?php
+error_reporting(-1);
+
+require 'misc.php';
+
+spl_autoload_register('psr0_autoload', TRUE);
+
+use \OSInet\Finite_State_Machine\FtpClient;
+use \OSInet\Finite_State_Machine\Grapher;
+
+$ftp = new FtpClient(array(
+  'source' => 'ftp.xml',
+)) or die('Could not load the FtpClient class.');
+// echo "FSM loaded, ". count($ftp->fTransitions) ." transitions. State: {$ftp->getState()}\n";
+
+FALSE && $ftp->setParameters(array(
+  'RemoteHost' => 'ftp.example.com',
+  'RemoteUser' => 'anonymous',
+  'RemotePass' => 'anonymous',
+  'RemoteWd' => '/dev',
+  'RemoteFile' => 'null',
+  'LocalWd' => '/dev',
+  'LocalFile' => 'null',
+  'FtpCallback' => '',
+));
+
+// print_r($ftp->fTransitions);
+$grapher = new Grapher($ftp, array('debug' => 1));
+echo $grapher->render();

+ 0 - 0
ftp.xml → apps/ftp/ftp.xml


+ 0 - 0
u_ftp.php → apps/ftp/u_ftp.php


+ 0 - 0
boxed_scalars.php → misc/boxed_scalars.php


+ 0 - 24
misc.php → misc/misc.php

@@ -45,30 +45,6 @@ function __autoload($name)
   }
 */
 
-/**
- * Sample PSR-0 autoloader.
- *
- * Do not use as such: it is only placed here to show use of the FSM classes in
- * a PSR-0 application.
- *
- * Straight from the PSR-0 standard.
- *
- * @param string $className
- */
-function psr0_autoload($className) {
-  $className = ltrim($className, '\\');
-  $fileName  = '';
-  $namespace = '';
-  if ($lastNsPos = strripos($className, '\\')) {
-    $namespace = substr($className, 0, $lastNsPos);
-    $className = substr($className, $lastNsPos + 1);
-    $fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
-  }
-  $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
-  //print_r(debug_backtrace());
-  $sts = require $fileName;
-}
-
 function get_temp_dir()
   {
   return 'e:/src/OsinetOffice/tmp';

+ 31 - 0
misc/psr0.php

@@ -0,0 +1,31 @@
+<?php
+/**
+ * Sample PSR-0 autoloader.
+ *
+ * Do not use as such: it is only present to demo classes in a PSR-0 context.
+ *
+ * Straight from the PSR-0 standard.
+ *
+ * @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
+ *
+ * @param string $className
+ */
+function psr0_autoload($className) {
+  $className = ltrim($className, '\\');
+  $fileName  = '';
+  $namespace = '';
+  if ($lastNsPos = strripos($className, '\\')) {
+    $namespace = substr($className, 0, $lastNsPos);
+    $className = substr($className, $lastNsPos + 1);
+    $fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
+  }
+  $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
+  /*
+   $stack = array_slice(debug_backtrace(), 2, 1);
+  $stack = reset($stack);
+  unset($stack['args']);
+  print_r($stack);
+  */
+  $sts = @include $fileName;
+  return $sts;
+}

+ 0 - 0
u_date_code.php → misc/u_date_code.php