Index: PHPUnit/Framework/TestResult.php
===================================================================
--- PHPUnit/Framework/TestResult.php	(revision 5279)
+++ PHPUnit/Framework/TestResult.php	(working copy)
@@ -125,6 +125,20 @@
     protected $topTestSuite = NULL;
 
     /**
+     * Check parameter types on function/method calls using function traces from Xdebug
+     * 
+     * @var boolean
+     */
+    protected $checkParamTypes = FALSE;
+
+    /**
+     * Parameter type verification depth
+     *
+     * @var    int
+     */
+    protected $checkParamTypeDepth = 2;
+
+    /**
      * Code Coverage information provided by Xdebug.
      *
      * @var    array
@@ -466,6 +480,204 @@
         return $this->topTestSuite;
     }
 
+     /**
+     * Enables or disables parameter type checking and sets depth
+     *
+     * @param  boolean $flag
+     * @param  int $depth
+     * @throws InvalidArgumentException
+     * @since  Method not released yet
+     */
+    public function checkParamTypes($flag, $depth)
+    {
+        if (is_bool($flag)) {
+            $this->checkParamTypes = $flag;
+        } else {
+            throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+        }
+        if (!is_null($depth)) {
+            if (is_numeric($depth)) {
+                $this->checkParamTypeDepth = $depth;
+            } else {
+                throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'integer');
+            }
+        }
+    }
+
+    
+     /**
+     * Compares 2 types, including classes
+     *
+     * @param string $paramType Can be 'class ClassName' or just the type itself
+     * @param string $docblockType The type to compare to
+     * @return boolean True if matched
+     */
+    private function compareTypes($callType, $docblockType)
+    {
+        if (trim($callType) == '???') {
+            return true;
+        }
+
+        $docblockTypes = explode("|", $docblockType);
+        foreach ($docblockTypes as $docblockType) {
+            if ($docblockType == 'mixed') {
+                return true;
+            }
+
+            preg_match_all('/\w+/', $callType, $callTypes); // Split up by words to get class names (if any)
+
+            if ($callTypes[0][0] == 'class') {
+                $className = $callTypes[0][1];
+                $implements = class_implements($className);
+                $parents = class_parents($className);
+                $callTypes = array_merge(array($className), $implements, $parents);
+
+            } else {
+                $callTypes = array($callTypes[0][0]);
+            }
+
+            $foundMatch = false;
+            foreach ($callTypes as $callType) {
+                switch ($callType) {
+                    case $docblockType:
+                        return true;
+                        break;
+                    case 'float':
+                        if ($docblockType == 'double' || $docblockType == 'number') {
+                            return true;
+                        }
+                        break;
+                    case 'int':
+                    case 'long':
+                        if ($docblockType == 'integer' || $docblockType == 'long' || $docblockType == 'number') {
+                            return true;
+                        }
+                        break;
+                    case 'bool':
+                        if ($docblockType == 'boolean') {
+                            return true;
+                        }
+                        break;
+                }
+            }
+        }
+        return $foundMatch;
+    }
+
+
+    /**
+     * Append parameter type check to test
+     *
+     * @param PHPUnit_Framework_Test $test
+     * @param string                 $traceFile
+     * @since Unreleased at this point
+     */
+    public function appendFunctionCalls(PHPUnit_Framework_Test $test, $traceFile)
+    {
+        if (($handle = fopen($traceFile, 'r')) === false) {
+            return false;
+        }
+        while ($dataLine = fgetcsv($handle, null, "\t")) {
+            if (isset($dataLine[5]) && is_numeric($dataLine[0]) && $dataLine[5] == get_class($test) . '->' . $test->getName()) {
+                $minimumLevel = $dataLine[0];
+                break;
+            }
+        }
+
+        while ($dataLine = fgetcsv($handle, null, "\t")) {
+            if ($dataLine[0] <= $minimumLevel) {
+                break;
+            }
+
+            if ($dataLine[0] < $minimumLevel + 1 + $this->checkParamTypeDepth) {
+                if ($dataLine[2] == 0) { // It's a function/method call
+                    preg_match(
+                        '/(?P<classOrFunction>\w+){0,1}(?:\:\:|->){0,1}(?P<method>\w+){0,1}/',
+                        $dataLine[5],
+                        $functionCall
+                    );
+
+                    unset($docBlock);
+
+                    if (!isset($functionCall['method']) && function_exists($functionCall['classOrFunction'])) {
+                        $calledName = $functionCall['classOrFunction'];
+                        $func = new ReflectionFunction($functionCall['classOrFunction']);
+                        $docBlock = $func->getDocComment();
+
+                    } elseif (method_exists($functionCall['classOrFunction'], $functionCall['method'])) {
+                        $calledName = $functionCall['classOrFunction'] . '->' . $functionCall['method'];
+                        $method = new ReflectionMethod($functionCall['classOrFunction'], $functionCall['method']);
+                        $docBlock = $method->getDocComment();
+                    }
+
+                    if (isset($docBlock)) {
+                        $foundReturn = false;
+
+                        preg_match_all('/\s*\*\s*@(?P<tag>phpunit-no-type-check)/', $docBlock, $noTypeCheck, PREG_SET_ORDER);
+                        if (count($noTypeCheck) == 0) {
+
+                            preg_match_all('/\s*\*\s*@(?P<tag>param|return)\s+(?P<type>\S+)\s+(?P<paramName>\$?\w+)?/', $docBlock, $docBlockVars, PREG_SET_ORDER);
+
+                            for ($cntDocBlockTag = 0; $cntDocBlockTag < count($docBlockVars); $cntDocBlockTag++) {
+                                if ($docBlockVars[$cntDocBlockTag]['tag'] == "param") {
+                                    $foundMatch = $this->compareTypes($dataLine[11 + $cntDocBlockTag], $docBlockVars[$cntDocBlockTag]['type']);
+
+                                    if ($foundMatch === false) {
+                                        $this->addFailure($test, new PHPUnit_Framework_AssertionFailedError('Invalid type calling ' . $calledName . ' : parameter ' . ($cntDocBlockTag + 1)  . ' (' . $docBlockVars[$cntDocBlockTag]['paramName'] . ') should be of type ' . $docBlockVars[$cntDocBlockTag][2] . ' but got ' . $dataLine[11 + $cntDocBlockTag] . ' instead in ' . $dataLine[8]), 1);
+                                    }
+
+                                } else {
+                                    $returnStack[] = array(
+                                        'calledName'    => $calledName,
+                                        'type'          => $docBlockVars[$cntDocBlockTag]['type'],
+                                        'noTypeCheck'   => 0
+                                    );
+                                    $foundReturn = true;
+                                }
+                            }
+                        }
+
+                        if ($foundReturn === false) {
+                            $returnStack[] = array(
+                                'calledName'    => $calledName,
+                                'type'          => '',
+                                'noTypeCheck'   => count($noTypeCheck)
+                            );
+                        }
+                    }
+
+                } else { // It's a return
+                    if (isset($dataLine[5])) {
+                        $returnPop = array_pop($returnStack);
+
+                        if ($returnPop['noTypeCheck'] == 0 && $returnPop['type'] != '') {
+                            preg_match('/(?P<type>\w+)\s?(?P<class>\w+)?/', $dataLine[5], $returnTypes);
+
+                            if (substr($returnTypes['type'], 0, 1) == "'") {
+                                $returnType = 'string';
+                            } elseif (is_numeric($returnTypes['type'])) {
+                                $returnType = 'int';
+                            } elseif ($returnTypes['type'] == 'class') {
+                                $returnType = 'class ' . $returnTypes['class'];
+                            } elseif ($returnTypes['type'] == 'TRUE' || $returnTypes['type'] == 'FALSE') {
+                                $returnType = 'bool';
+                            } elseif ($returnTypes['type'] == 'array') {
+                                $returnType = 'array';
+                            } else {
+                                $returnType = 'unknown';
+                            }
+                            
+                            if (!$this->compareTypes($returnType, $returnPop['type'])) {
+                                $this->addFailure($test, new PHPUnit_Framework_AssertionFailedError('Invalid type returned from ' . $returnPop['calledName'] . ' : return should be of type ' . $returnPop['type'] . ' but got ' . $returnType . ' instead'), 1);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        fclose($handle);
+    }
+    
     /**
      * Enables or disables the collection of Code Coverage information.
      *
@@ -674,7 +886,10 @@
         }
 
         $useXdebug = self::$useXdebug &&
-                     $this->collectCodeCoverageInformation &&
+                     (
+                        $this->collectCodeCoverageInformation ||
+                        $this->checkParamTypes
+                     ) &&
                      !$test instanceof PHPUnit_Extensions_SeleniumTestCase;
 
         if ($useXdebug) {
@@ -679,6 +894,20 @@
 
         if ($useXdebug) {
             xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
+            if ($this->checkParamTypes) {
+                ini_set('xdebug.auto_trace', 1);
+                ini_set('xdebug.trace_format', 1);
+                ini_set('xdebug.collect_return', 1);
+                ini_set('xdebug.collect_params', 1);
+                ini_set('xdebug.trace_options', 0);
+                if (defined('PHPUNIT_TMPDIR')) {
+                    $tmpDir = PHPUNIT_TMPDIR;
+                } else {
+                    $tmpDir = sys_get_temp_dir();
+                }
+                $traceFile = tempnam($tmpDir, 'PHPUnit_VarTypeCheck_');
+                xdebug_start_trace($traceFile);
+            }
         }
 
         PHPUnit_Util_Timer::start();
@@ -698,13 +927,24 @@
         $time = PHPUnit_Util_Timer::stop();
 
         if ($useXdebug) {
-            $codeCoverage = xdebug_get_code_coverage();
-            xdebug_stop_code_coverage();
+            if ($this->checkParamTypes) {
+                $traceFile = xdebug_get_tracefile_name();
+                $functionData = file_get_contents($traceFile);
+                xdebug_stop_trace();
+                $this->appendFunctionCalls(
+                        $test, $traceFile
+                    );
+            }
+
+            if ($this->codeCoverageInformation) {
+	            $codeCoverage = xdebug_get_code_coverage();
+	            xdebug_stop_code_coverage();
 
-            if (!$test instanceof PHPUnit_Framework_Warning) {
-                $this->appendCodeCoverageInformation(
-                  $test, $codeCoverage
-                );
+	            if (!$test instanceof PHPUnit_Framework_Warning) {
+	                $this->appendCodeCoverageInformation(
+	                  $test, $codeCoverage
+	                );
+	            }
             }
         }
 
Index: PHPUnit/TextUI/Command.php
===================================================================
--- PHPUnit/TextUI/Command.php	(revision 5279)
+++ PHPUnit/TextUI/Command.php	(working copy)
@@ -87,6 +87,8 @@
      */
     protected $longOptions = array(
       'bootstrap=' => NULL,
+      'check-param-types' => NULL,
+      'check-param-type-depth=' => NULL,
       'colors' => NULL,
       'configuration=' => NULL,
       'coverage-clover=' => NULL,
@@ -266,6 +268,16 @@
                 }
                 break;
 
+                case '--check-param-types': {
+                    $this->arguments['checkParamTypes'] = TRUE;
+                }
+                break;
+
+                case '--check-param-type-depth': {
+                    $this->arguments['checkParamTypeDepth'] = $option[1];
+                }
+                break;
+
                 case '--colors': {
                     $this->arguments['colors'] = TRUE;
                 }
@@ -764,6 +776,9 @@
   --testdox-html <file>    Write agile documentation in HTML format to file.
   --testdox-text <file>    Write agile documentation in Text format to file.
 
+  --check-param-types      Check parameter types on functions called
+  --check-param-type-depth Sets depth of parameter type checks (default = 2)
+
   --filter <pattern>       Filter which tests to run.
   --group ...              Only runs tests from the specified group(s).
   --exclude-group ...      Exclude tests from the specified group(s).
Index: PHPUnit/TextUI/TestRunner.php
===================================================================
--- PHPUnit/TextUI/TestRunner.php	(revision 5279)
+++ PHPUnit/TextUI/TestRunner.php	(working copy)
@@ -267,6 +267,14 @@
             $result->collectCodeCoverageInformation(TRUE);
         }
 
+        if ($arguments['checkParamTypes'] &&
+            extension_loaded('xdebug')) {
+            $result->checkParamTypes(
+                TRUE,
+                isset($arguments['checkParamTypeDepth']) ? $arguments['checkParamTypeDepth'] : null
+            );
+        }
+
         if (isset($arguments['jsonLogfile'])) {
             require_once 'PHPUnit/Util/Log/JSON.php';
 
@@ -707,6 +715,7 @@
         $arguments['reportLowUpperBound']         = isset($arguments['reportLowUpperBound'])         ? $arguments['reportLowUpperBound']         : 35;
         $arguments['reportYUI']                   = isset($arguments['reportYUI'])                   ? $arguments['reportYUI']                   : TRUE;
         $arguments['stopOnFailure']               = isset($arguments['stopOnFailure'])               ? $arguments['stopOnFailure']               : FALSE;
+        $arguments['checkParamTypeDepth']         = isset($arguments['checkParamTypeDepth'])         ? $arguments['checkParamTypeDepth']         : 2;
 
         if ($arguments['filter'] !== FALSE &&
             preg_match('/^[a-zA-Z0-9_]/', $arguments['filter'])) {
