<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,6 +1,35 @@
 &lt;?php
-class StaticReflector {
+interface ClassCollator {
+  function collate($first, $second);
+}
+
+class DummyClassCollator implements ClassCollator {
+  function collate($first, $second) {
+    return 'mixed';
+  }
+}
+
+class TraceIncludesLogger {
+  protected $reflector;
+  protected $includes = array();
+  function __construct(StaticReflector $reflector) {
+    $this-&gt;reflector = $reflector;
+  }
+  function log($trace) {
+    $filename = isset($trace['filename']) ? $trace['filename'] : '';
+    if (!isset($this-&gt;includes[$filename]) &amp;&amp; is_file($filename)) {
+      $this-&gt;reflector-&gt;scanFile($filename);
+    }
+    $this-&gt;includes[$filename] = true;
+  }
+  function log_include($trace) {
+    $this-&gt;log($trace);
+  }
+}
+
+class StaticReflector implements ClassCollator {
   protected $scanner;
+  protected $names = array();
   protected $typemap = array();
   protected $collate_cache = array();
   protected $ancestors_cache = array();
@@ -12,6 +41,8 @@ class StaticReflector {
     $inheritance_scanner-&gt;notifyOnImplements(array($this, 'logSupertype'));
   }
   function logSupertype($class, $super) {
+    $this-&gt;names[strtolower($super)] = $super;
+    $this-&gt;names[strtolower($class)] = $class;
     $class = strtolower($class);
     $super = strtolower($super);
     if (!isset($this-&gt;typemap[$class])) {
@@ -34,13 +65,20 @@ class StaticReflector {
   function export() {
     return $this-&gt;typemap;
   }
+  protected function symbolsToNames($symbols = array()) {
+    $names = array();
+    foreach ($symbols as $symbol) {
+      $names[] = $this-&gt;names[$symbol];
+    }
+    return $names;
+  }
   function ancestors($class) {
     $class = strtolower($class);
-    return isset($this-&gt;typemap[$class]) ? $this-&gt;typemap[$class] : array();
+    return $this-&gt;symbolsToNames(isset($this-&gt;typemap[$class]) ? $this-&gt;typemap[$class] : array());
   }
   function ancestorsAndSelf($class) {
     $class = strtolower($class);
-    return isset($this-&gt;typemap[$class]) ? array_merge(array($class), $this-&gt;typemap[$class]) : array($class);
+    return $this-&gt;symbolsToNames(isset($this-&gt;typemap[$class]) ? array_merge(array($class), $this-&gt;typemap[$class]) : array($class));
   }
   function allAncestors($class) {
     $class = strtolower($class);
@@ -55,7 +93,7 @@ class StaticReflector {
     return $result;
   }
   function allAncestorsAndSelf($class) {
-    return array_merge(array(strtolower($class)), $this-&gt;allAncestors($class));
+    return array_merge(array($this-&gt;names[strtolower($class)]), $this-&gt;allAncestors($class));
   }
   /**
    * Finds the first common ancestor, if possible</diff>
      <filename>reflector.inc.php</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,9 @@
 &lt;?php
 class Signatures {
   protected $signatures_array = array();
-  function __construct($signatures_array = array()) {
-    foreach ($signatures_array as $name =&gt; $sig) {
-      $this-&gt;signatures_array[strtolower($name)] = $sig;
-    }
+  protected $collator;
+  function __construct(ClassCollator $collator) {
+    $this-&gt;collator = $collator;
   }
   function has($func, $class = &quot;&quot;) {
     $name = strtolower($class ? ($class . '-&gt;' . $func) : $func);
@@ -16,26 +15,24 @@ class Signatures {
     }
     $name = strtolower($class ? ($class . '-&gt;' . $func) : $func);
     if (!isset($this-&gt;signatures_array[$name])) {
-      $this-&gt;signatures_array[$name] = new FunctionSignature();
+      $this-&gt;signatures_array[$name] = new FunctionSignature($this-&gt;collator);
     }
     return $this-&gt;signatures_array[$name];
   }
 }
 
-/**
- * Signatures are collected at 3 different places (in order of authority):
- *   static analysis (typehints)
- *   runtime analysis (trace)
- *   docblockcomments (optional)
- */
 class FunctionSignature {
   protected $arguments = array();
   protected $return_type;
+  protected $collator;
+  function __construct(ClassCollator $collator) {
+    $this-&gt;collator = $collator;
+  }
   function blend($arguments, $return_type) {
     if ($arguments) {
       foreach ($arguments as $id =&gt; $type) {
         $arg = $this-&gt;getArgumentById($id);
-        $arg-&gt;blendType($type);
+        $arg-&gt;collateWith($type);
       }
     }
     if ($return_type) {
@@ -47,7 +44,7 @@ class FunctionSignature {
   }
   function getArgumentById($id) {
     if (!isset($this-&gt;arguments[$id])) {
-      $this-&gt;arguments[$id] = new FunctionArgument($id);
+      $this-&gt;arguments[$id] = new FunctionArgument($id, null, '???', $this-&gt;collator);
     }
     return $this-&gt;arguments[$id];
   }
@@ -69,10 +66,16 @@ class FunctionArgument {
   protected $id;
   protected $name;
   protected $type;
-  function __construct($id, $name = null, $type = '???') {
+  protected $collator;
+  function __construct($id, $name = null, $type = '???', ClassCollator $collator) {
     $this-&gt;id = $id;
     $this-&gt;name = $name;
-    $this-&gt;type = $type;
+    if ($type === 'null') {
+      $this-&gt;type = '???';
+    } else {
+      $this-&gt;type = $type;
+    }
+    $this-&gt;collator = $collator;
   }
   function getId() {
     return $this-&gt;id;
@@ -95,10 +98,37 @@ class FunctionArgument {
   function setType($type) {
     $this-&gt;type = $type;
   }
-  function blendType($type) {
-    // todo: could probably be more intelligent
-    if ($type != '???') {
+  function collateWith($type) {
+    static $primitive = array('boolean', 'string', 'array', 'integer', 'double', 'mixed');
+    if ($this-&gt;type === $type) {
+      return;
+    }
+    if ($type === 'null') {
+      // todo: set this-&gt;nullable = true
+      return;
+    }
+    if ($this-&gt;type === '???') {
       $this-&gt;type = $type;
+    } elseif ($type != '???') {
+      if (in_array($type, $primitive) || in_array($this-&gt;type, $primitive)) {
+        $tmp = array($this-&gt;type, $type);
+        sort($tmp);
+        switch (implode(&quot;:&quot;, $tmp)) {
+        case 'integer:string':
+        case 'double:string':
+          $this-&gt;type = 'string';
+          break;
+        case 'double:integer':
+          $this-&gt;type = 'double';
+          break;
+        default:
+          $this-&gt;type = 'mixed';
+        }
+      } else {
+        //$this-&gt;type = $type;
+        $collate = $this-&gt;collator-&gt;collate($this-&gt;type, $type);
+        $this-&gt;type = $collate === '*CANT_COLLATE*' ? 'mixed' : $collate;
+      }
     }
   }
 }</diff>
      <filename>signature.inc.php</filename>
    </modified>
    <modified>
      <diff>@@ -3,9 +3,10 @@ error_reporting(E_ALL | E_STRICT);
 
 /**
  * todo:
- *   blendType to use StaticReflector + xtrace_TraceIncludesLogger
  *   xtrace -&gt; resources ..
  *   use static typehints for parameter types
+ *   use docblock comments for parameter types
+ *   merge with existing docblock comments
  */
 
 // You need to have simpletest in your include_path
@@ -108,32 +109,32 @@ class TestOfStaticReflector extends UnitTestCase {
     $reflector = new StaticReflector();
     $reflector-&gt;scanString('&lt;'.'?php class Foo implements Bar, Doink {}');
     $reflector-&gt;scanString('&lt;'.'?php class Zip implements Bar {}');
-    $this-&gt;assertEqual($reflector-&gt;ancestors('Foo'), array('bar', 'doink'));
-    $this-&gt;assertEqual($reflector-&gt;ancestors('zip'), array('bar'));
+    $this-&gt;assertEqual($reflector-&gt;ancestors('Foo'), array('Bar', 'Doink'));
+    $this-&gt;assertEqual($reflector-&gt;ancestors('Zip'), array('Bar'));
   }
   function test_can_collate_same() {
     $reflector = new StaticReflector();
     $reflector-&gt;scanString('&lt;'.'?php class Foo extends Bar {}');
     $reflector-&gt;scanString('&lt;'.'?php class Zip extends Bar {}');
-    $this-&gt;assertEqual($reflector-&gt;collate('foo', 'foo'), 'foo');
+    $this-&gt;assertEqual($reflector-&gt;collate('Foo', 'Foo'), 'Foo');
   }
   function test_can_collate_direct_inheritance() {
     $reflector = new StaticReflector();
     $reflector-&gt;scanString('&lt;'.'?php class Foo extends Bar {}');
     $reflector-&gt;scanString('&lt;'.'?php class Zip extends Bar {}');
-    $this-&gt;assertEqual($reflector-&gt;collate('foo', 'zip'), 'bar');
+    $this-&gt;assertEqual($reflector-&gt;collate('Foo', 'Zip'), 'Bar');
   }
   function test_can_collate_child_to_parent() {
     $reflector = new StaticReflector();
     $reflector-&gt;scanString('&lt;'.'?php class Foo {}');
     $reflector-&gt;scanString('&lt;'.'?php class Bar extends Foo {}');
-    $this-&gt;assertEqual($reflector-&gt;collate('foo', 'bar'), 'foo');
+    $this-&gt;assertEqual($reflector-&gt;collate('Foo', 'Bar'), 'Foo');
   }
   function test_can_collate_parent_to_child() {
     $reflector = new StaticReflector();
     $reflector-&gt;scanString('&lt;'.'?php class Foo {}');
     $reflector-&gt;scanString('&lt;'.'?php class Bar extends Foo {}');
-    $this-&gt;assertEqual($reflector-&gt;collate('bar', 'foo'), 'foo');
+    $this-&gt;assertEqual($reflector-&gt;collate('Bar', 'Foo'), 'Foo');
   }
 }
 
@@ -303,7 +304,6 @@ class TestOfTracer extends UnitTestCase {
   function test_can_execute_sandbox_code() {
     chdir($this-&gt;sandbox());
     $output = shell_exec('php ' . escapeshellarg($this-&gt;sandbox() . '/main.php'));
-    //$this-&gt;dump(&quot;\n----\n&quot; . $output . &quot;\n----\n&quot;);
     $this-&gt;assertEqual(&quot;(completed)\n&quot;, $output);
   }
   function test_can_execute_sandbox_code_with_instrumentation() {
@@ -321,7 +321,7 @@ class TestOfTracer extends UnitTestCase {
   function test_can_parse_tracefile() {
     chdir($this-&gt;sandbox());
     shell_exec(escapeshellcmd($this-&gt;bindir() . '/trace.sh') . &quot; &quot; . escapeshellarg($this-&gt;sandbox() . '/main.php'));
-    $sigs = new Signatures();
+    $sigs = new Signatures(new DummyClassCollator());
     $this-&gt;assertFalse($sigs-&gt;has('callit'));
     $trace = new xtrace_TraceReader(new SplFileObject($this-&gt;sandbox() . '/dumpfile.xt'));
     $collector = new xtrace_TraceSignatureLogger($sigs);
@@ -331,10 +331,55 @@ class TestOfTracer extends UnitTestCase {
   function test_can_parse_class_arg() {
     chdir($this-&gt;sandbox());
     shell_exec(escapeshellcmd($this-&gt;bindir() . '/trace.sh') . &quot; &quot; . escapeshellarg($this-&gt;sandbox() . '/main.php'));
-    $sigs = new Signatures();
+    $sigs = new Signatures(new DummyClassCollator());
     $trace = new xtrace_TraceReader(new SplFileObject($this-&gt;sandbox() . '/dumpfile.xt'));
     $collector = new xtrace_TraceSignatureLogger($sigs);
     $trace-&gt;process(new xtrace_FunctionTracer($collector));
     $this-&gt;assertEqual('Foo', $sigs-&gt;get('callit')-&gt;getArgumentById(0)-&gt;getType());
   }
+}
+
+class TestOfCollation extends UnitTestCase {
+  function bindir() {
+    return dirname(__FILE__);
+  }
+  function sandbox() {
+    return dirname(__FILE__) . '/sandbox';
+  }
+  function setUp() {
+    $this-&gt;curdir = getcwd();
+    $dir_sandbox = $this-&gt;sandbox();
+    mkdir($dir_sandbox);
+    $source_main = '&lt;'.'?php' . &quot;\n&quot; .
+      'class Foo {' . &quot;\n&quot; .
+      '}'. &quot;\n&quot; .
+      'class Bar extends Foo {' . &quot;\n&quot; .
+      '}'. &quot;\n&quot; .
+      'class Cuux extends Foo {' . &quot;\n&quot; .
+      '}'. &quot;\n&quot; .
+      'function do_stuff($x) {}'. &quot;\n&quot; .
+      'do_stuff(new Bar());'. &quot;\n&quot; .
+      'do_stuff(new Cuux());'
+      ;
+    file_put_contents($dir_sandbox . '/main.php', $source_main);
+  }
+  function tearDown() {
+    chdir($this-&gt;curdir);
+    $dir_sandbox = $this-&gt;sandbox();
+    unlink($dir_sandbox . '/main.php');
+    if (is_file($dir_sandbox . '/dumpfile.xt')) {
+      unlink($dir_sandbox . '/dumpfile.xt');
+    }
+    rmdir($dir_sandbox);
+  }
+  function test_can_collate_classes() {
+    chdir($this-&gt;sandbox());
+    shell_exec(escapeshellcmd($this-&gt;bindir() . '/trace.sh') . &quot; &quot; . escapeshellarg($this-&gt;sandbox() . '/main.php'));
+    $reflector = new StaticReflector();
+    $sigs = new Signatures($reflector);
+    $trace = new xtrace_TraceReader(new SplFileObject($this-&gt;sandbox() . '/dumpfile.xt'));
+    $collector = new xtrace_TraceSignatureLogger($sigs, $reflector);
+    $trace-&gt;process(new xtrace_FunctionTracer($collector));
+    $this-&gt;assertEqual('Foo', $sigs-&gt;get('do_stuff')-&gt;getArgumentById(0)-&gt;getType());
+  }
 }
\ No newline at end of file</diff>
      <filename>test.php</filename>
    </modified>
    <modified>
      <diff>@@ -16,10 +16,11 @@ if (realpath($_SERVER['SCRIPT_FILENAME']) === __FILE__) {
   }
 
   // read trace
-  $db = new Signatures();
+  $reflector = new StaticReflector();
+  $sigs = new Signatures();
   $trace = new xtrace_TraceReader(new SplFileObject($trace_filename));
-  $collector = new xtrace_TraceSignatureLogger($db);
-  $trace-&gt;process(new xtrace_FunctionTracer($collector));
+  $collector = new xtrace_TraceSignatureLogger($sigs, $reflector);
+  $trace-&gt;process(new xtrace_FunctionTracer($collector, $reflector));
 
   // transform file
   $scanner = new ScannerMultiplexer();
@@ -27,7 +28,7 @@ if (realpath($_SERVER['SCRIPT_FILENAME']) === __FILE__) {
   $function_body_scanner = $scanner-&gt;appendScanner(new FunctionBodyScanner());
   $modifiers_scanner = $scanner-&gt;appendScanner(new ModifiersScanner());
   $class_scanner = $scanner-&gt;appendScanner(new ClassScanner());
-  $editor = new TracerDocBlockEditor($db, $class_scanner, $function_body_scanner);
+  $editor = new TracerDocBlockEditor($sigs, $class_scanner, $function_body_scanner);
   $transformer = $scanner-&gt;appendScanner(new DocCommentEditorTransformer($function_body_scanner, $modifiers_scanner, $parameters_scanner, $editor));
   $tokenizer = new TokenStreamParser();
   $token_stream = $tokenizer-&gt;scan(file_get_contents($file_to_weave));</diff>
      <filename>weave.php</filename>
    </modified>
    <modified>
      <diff>@@ -36,13 +36,11 @@ class xtrace_TraceReader {
 class xtrace_FunctionTracer {
   protected $handler;
   protected $stack = array();
-  protected $include_functions;
   protected $internal_functions;
   function __construct($handler) {
     $this-&gt;handler = $handler;
     $defined_functions = get_defined_functions();
-    $this-&gt;include_functions = array('include', 'include_once', 'require', 'require_once');
-    $this-&gt;internal_functions = array_merge($defined_functions['internal'], $this-&gt;include_functions);
+    $this-&gt;internal_functions = array_merge($defined_functions['internal'], array('include', 'include_once', 'require', 'require_once'));
   }
   function trace_start($time) {}
   function trace_end($time) {}
@@ -66,43 +64,30 @@ class xtrace_FunctionTracer {
     if (!in_array($fun_call['function'], $this-&gt;internal_functions)) {
       $this-&gt;handler-&gt;log($fun_call);
     }
-    if (in_array($fun_call['function'], $this-&gt;include_functions)) {
-      $this-&gt;handler-&gt;log_include($fun_call);
-    }
-  }
-}
-
-class xtrace_TraceIncludesLogger {
-  protected $includes = array();
-  function log($trace) {
-    $this-&gt;includes[$trace['filename']] = true;
-  }
-  function log_include($trace) {
-    $this-&gt;log($trace);
-  }
-  function getIncludes() {
-    $result = array();
-    foreach (array_keys($this-&gt;includes) as $filename) {
-      if (is_file($filename)) {
-        $result[] = $filename;
-      }
-    }
-    return $result;
   }
 }
 
 class xtrace_TraceSignatureLogger {
   protected $signatures;
-  function __construct(Signatures $signatures) {
+  protected $reflector;
+  protected $includes = array();
+  function __construct(Signatures $signatures, StaticReflector $reflector = null) {
     $this-&gt;signatures = $signatures;
+    $this-&gt;reflector = $reflector;
   }
   function log($trace) {
+    if ($this-&gt;reflector) {
+      $filename = isset($trace['filename']) ? $trace['filename'] : '';
+      if (!isset($this-&gt;includes[$filename]) &amp;&amp; is_file($filename)) {
+        $this-&gt;reflector-&gt;scanFile($filename);
+      }
+      $this-&gt;includes[$filename] = true;
+    }
     $sig = $this-&gt;signatures-&gt;get($trace['function']);
     $sig-&gt;blend(
       $this-&gt;parseArguments($trace['arguments']),
       $this-&gt;parseReturnType($trace['return_value']));
   }
-  function log_include($trace) { /* void */ }
   function parseArguments($as_string) {
     // todo: resources ..
     $types = array();</diff>
      <filename>xtrace.inc.php</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>2c1014c96df02719d7bad34cf8b0b25dfde41444</id>
    </parent>
  </parents>
  <author>
    <name>Troels Knak-Nielsen</name>
    <email>tkn@tkn-desktop-ubuntu.(none)</email>
  </author>
  <url>http://github.com/troelskn/php-tracer-weaver/commit/66d6702215fc9fd03195c2b23dd8fd786cfa0206</url>
  <id>66d6702215fc9fd03195c2b23dd8fd786cfa0206</id>
  <committed-date>2008-12-06T16:08:38-08:00</committed-date>
  <authored-date>2008-12-06T16:08:38-08:00</authored-date>
  <message>Now uses static type info to collate class types</message>
  <tree>12513e14850799570435cd7de2117993bf3c1cfb</tree>
  <committer>
    <name>Troels Knak-Nielsen</name>
    <email>tkn@tkn-desktop-ubuntu.(none)</email>
  </committer>
</commit>
