| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 | <?phpnamespace 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;  }}
 |