diff --git a/src/Symfony/Component/ClassLoader/DebugClassLoader.php b/src/Symfony/Component/ClassLoader/DebugClassLoader.php index 842f4744c0f66..9a6069fe68bc7 100644 --- a/src/Symfony/Component/ClassLoader/DebugClassLoader.php +++ b/src/Symfony/Component/ClassLoader/DebugClassLoader.php @@ -39,6 +39,16 @@ public function __construct($classFinder) $this->classFinder = $classFinder; } + /** + * Gets the wrapped class loader. + * + * @return object a class loader instance + */ + public function getClassLoader() + { + return $this->classFinder; + } + /** * Replaces all autoloaders implementing a findFile method by a DebugClassLoader wrapper. */ diff --git a/src/Symfony/Component/Debug/CHANGELOG.md b/src/Symfony/Component/Debug/CHANGELOG.md index 2ad5ce695c365..a8321da551b26 100644 --- a/src/Symfony/Component/Debug/CHANGELOG.md +++ b/src/Symfony/Component/Debug/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +2.4.0 +----- + + * improved error messages for not found classes and functions + 2.3.0 ----- diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index dfb7dad003f4f..8afa49ee64eef 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -11,9 +11,12 @@ namespace Symfony\Component\Debug; -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\Exception\ContextErrorException; use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\Exception\ContextErrorException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; /** * ErrorHandler. @@ -55,7 +58,7 @@ class ErrorHandler /** * Registers the error handler. * - * @param integer $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable) + * @param integer $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable) * @param Boolean $displayErrors Display errors (for dev environment) or just log they (production usage) * * @return The registered error handler @@ -74,16 +77,32 @@ public static function register($level = null, $displayErrors = true) return $handler; } + /** + * Sets the level at which the conversion to Exception is done. + * + * @param integer|null $level The level (null to use the error_reporting() value and 0 to disable) + */ public function setLevel($level) { $this->level = null === $level ? error_reporting() : $level; } + /** + * Sets the display_errors flag value. + * + * @param integer $displayErrors The display_errors flag value + */ public function setDisplayErrors($displayErrors) { $this->displayErrors = $displayErrors; } + /** + * Sets a logger for the given channel. + * + * @param LoggerInterface $logger A logger interface + * @param string $channel The channel associated with the logger (deprecation or emergency) + */ public static function setLogger(LoggerInterface $logger, $channel = 'deprecation') { self::$loggers[$channel] = $logger; @@ -157,10 +176,37 @@ public function handleFatal() restore_exception_handler(); if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) { - $level = isset($this->levels[$type]) ? $this->levels[$type] : $type; - $message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']); - $exception = new FatalErrorException($message, 0, $type, $error['file'], $error['line']); - $exceptionHandler[0]->handle($exception); + $this->handleFatalError($exceptionHandler[0], $error); } } + + /** + * Gets the fatal error handlers. + * + * Override this method if you want to define more fatal error handlers. + * + * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface + */ + protected function getFatalErrorHandlers() + { + return array( + new UndefinedFunctionFatalErrorHandler(), + new ClassNotFoundFatalErrorHandler(), + ); + } + + private function handleFatalError(ExceptionHandler $exceptionHandler, array $error) + { + $level = isset($this->levels[$error['type']]) ? $this->levels[$error['type']] : $error['type']; + $message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']); + $exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line']); + + foreach ($this->getFatalErrorHandlers() as $handler) { + if ($ex = $handler->handleError($error, $exception)) { + return $exceptionHandler->handle($ex); + } + } + + $exceptionHandler->handle($exception); + } } diff --git a/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php b/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php new file mode 100644 index 0000000000000..94169b45a2075 --- /dev/null +++ b/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Class (or Trait or Interface) Not Found Exception. + * + * @author Konstanton Myakshin + */ +class ClassNotFoundException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + } +} diff --git a/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php b/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php new file mode 100644 index 0000000000000..572c8b30a1a7b --- /dev/null +++ b/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Undefined Function Exception. + * + * @author Konstanton Myakshin + */ +class UndefinedFunctionException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + } +} diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php new file mode 100644 index 0000000000000..0bf60d894a260 --- /dev/null +++ b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\ClassNotFoundException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Composer\Autoload\ClassLoader as ComposerClassLoader; +use Symfony\Component\ClassLoader as SymfonyClassLoader; +use Symfony\Component\ClassLoader\DebugClassLoader; + +/** + * ErrorHandler for classes that do not exist. + * + * @author Fabien Potencier + */ +class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '" not found'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + foreach (array('class', 'interface', 'trait') as $typeName) { + $prefix = ucfirst($typeName).' "'; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + continue; + } + + $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { + $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); + $message = sprintf( + 'Attempted to load %s "%s" from namespace "%s" in %s line %d. Do you need to "use" it from another namespace?', + $typeName, + $className, + $namespacePrefix, + $error['file'], + $error['line'] + ); + } else { + $className = $fullyQualifiedClassName; + $message = sprintf( + 'Attempted to load %s "%s" from the global namespace in %s line %d. Did you forget a use statement for this %s?', + $typeName, + $className, + $error['file'], + $error['line'], + $typeName + ); + } + + if ($classes = $this->getClassCandidates($className)) { + $message .= sprintf(' Perhaps you need to add a use statement for one of the following: %s.', implode(', ', $classes)); + } + + return new ClassNotFoundException($message, $exception); + } + } + + /** + * Tries to guess the full namespace for a given class name. + * + * By default, it looks for PSR-0 classes registered via a Symfony or a Composer + * autoloader (that should cover all common cases). + * + * @param string $class A class name (without its namespace) + * + * @return array An array of possible fully qualified class names + */ + private function getClassCandidates($class) + { + if (!is_array($functions = spl_autoload_functions())) { + return array(); + } + + // find Symfony and Composer autoloaders + $classes = array(); + foreach ($functions as $function) { + if (!is_array($function)) { + continue; + } + + // get class loaders wrapped by DebugClassLoader + if ($function[0] instanceof DebugClassLoader && method_exists($function[0], 'getClassLoader')) { + $function[0] = $function[0]->getClassLoader(); + } + + if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) { + foreach ($function[0]->getPrefixes() as $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($function[0], $path, $class)); + } + } + } + } + + return $classes; + } + + private function findClassInPath($loader, $path, $class) + { + if (!$path = realpath($path)) { + continue; + } + + $classes = array(); + $filename = $class.'.php'; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($filename == $file->getFileName() && $class = $this->convertFileToClass($loader, $path, $file->getPathName())) { + $classes[] = $class; + } + } + + return $classes; + } + + private function convertFileToClass($loader, $path, $file) + { + // We cannot use the autoloader here as most of them use require; but if the class + // is not found, the new autoloader call will require the file again leading to a + // "cannot redeclare class" error. + require_once $file; + + $file = str_replace(array($path.'/', '.php'), array('', ''), $file); + + // is it a namespaced class? + $class = str_replace('/', '\\', $file); + if (class_exists($class, false) || interface_exists($class, false) || (function_exists('trait_exists') && trait_exists($class, false))) { + return $class; + } + + // is it a PEAR-like class name instead? + $class = str_replace('/', '_', $file); + if (class_exists($class, false) || interface_exists($class, false) || (function_exists('trait_exists') && trait_exists($class, false))) { + return $class; + } + } +} diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php new file mode 100644 index 0000000000000..6b87eb30a126e --- /dev/null +++ b/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * Attempts to convert fatal errors to exceptions. + * + * @author Fabien Potencier + */ +interface FatalErrorHandlerInterface +{ + /** + * Attempts to convert an error into an exception. + * + * @param array $error An array as returned by error_get_last() + * @param FatalErrorException $exception A FatalErrorException instance + * + * @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise + */ + public function handleError(array $error, FatalErrorException $exception); +} diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php new file mode 100644 index 0000000000000..4b6fc905c9e25 --- /dev/null +++ b/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\UndefinedFunctionException; +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * ErrorHandler for undefined functions. + * + * @author Fabien Potencier + */ +class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '()'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + $prefix = 'Call to undefined function '; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + return; + } + + $fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { + $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); + $message = sprintf( + 'Attempted to call function "%s" from namespace "%s" in %s line %d.', + $functionName, + $namespacePrefix, + $error['file'], + $error['line'] + ); + } else { + $functionName = $fullyQualifiedFunctionName; + $message = sprintf( + 'Attempted to call function "%s" from the global namespace in %s line %d.', + $functionName, + $error['file'], + $error['line'] + ); + } + + $candidates = array(); + foreach (get_defined_functions() as $type => $definedFunctionNames) { + foreach ($definedFunctionNames as $definedFunctionName) { + if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) { + $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1); + } else { + $definedFunctionNameBasename = $definedFunctionName; + } + + if ($definedFunctionNameBasename === $functionName) { + $candidates[] = '\\'.$definedFunctionName; + } + } + } + + if ($candidates) { + $message .= ' Did you mean to call: '.implode(', ', array_map(function ($val) { + return '"'.$val.'"'; + }, $candidates)).'?'; + } + + return new UndefinedFunctionException($message, $exception); + } +} diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index db06f6107e6ec..81eef501c0672 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -20,7 +20,6 @@ */ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase { - public function testConstruct() { $handler = ErrorHandler::register(3); @@ -90,4 +89,51 @@ public function testHandle() restore_error_handler(); } + + /** + * @dataProvider provideFatalErrorHandlersData + */ + public function testFatalErrorHandlers($error, $class, $translatedMessage) + { + $handler = new ErrorHandler(); + $exceptionHandler = new MockExceptionHandler(); + + $m = new \ReflectionMethod($handler, 'handleFatalError'); + $m->setAccessible(true); + $m->invoke($handler, $exceptionHandler, $error); + + $this->assertInstanceof($class, $exceptionHandler->e); + $this->assertSame($translatedMessage, $exceptionHandler->e->getMessage()); + $this->assertSame($error['type'], $exceptionHandler->e->getSeverity()); + $this->assertSame($error['file'], $exceptionHandler->e->getFile()); + $this->assertSame($error['line'], $exceptionHandler->e->getLine()); + } + + public function provideFatalErrorHandlersData() + { + return array( + // undefined function + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function test_namespaced_function()', + ), + 'Symfony\Component\Debug\Exception\UndefinedFunctionException', + 'Attempted to call function "test_namespaced_function" from the global namespace in foo.php line 12. Did you mean to call: "\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function"?', + ), + // class not found + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class "WhizBangFactory" not found', + ), + 'Symfony\Component\Debug\Exception\ClassNotFoundException', + 'Attempted to load class "WhizBangFactory" from the global namespace in foo.php line 12. Did you forget a use statement for this class?', + ), + ); + } } diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php new file mode 100644 index 0000000000000..7112c8c5f3e53 --- /dev/null +++ b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; + +class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideClassNotFoundData + */ + public function testClassNotFound($error, $translatedMessage) + { + $handler = new ClassNotFoundFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceof('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); + $this->assertSame($translatedMessage, $exception->getMessage()); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideClassNotFoundData() + { + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class "WhizBangFactory" not found', + ), + 'Attempted to load class "WhizBangFactory" from the global namespace in foo.php line 12. Did you forget a use statement for this class?', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class "Foo\\Bar\\WhizBangFactory" not found', + ), + 'Attempted to load class "WhizBangFactory" from namespace "Foo\\Bar" in foo.php line 12. Do you need to "use" it from another namespace?', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class "UndefinedFunctionException" not found', + ), + 'Attempted to load class "UndefinedFunctionException" from the global namespace in foo.php line 12. Did you forget a use statement for this class? Perhaps you need to add a use statement for one of the following: Symfony\Component\Debug\Exception\UndefinedFunctionException.', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class "PEARClass" not found', + ), + 'Attempted to load class "PEARClass" from the global namespace in foo.php line 12. Did you forget a use statement for this class? Perhaps you need to add a use statement for one of the following: Symfony_Component_Debug_Tests_Fixtures_PEARClass.', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class "Foo\\Bar\\UndefinedFunctionException" not found', + ), + 'Attempted to load class "UndefinedFunctionException" from namespace "Foo\Bar" in foo.php line 12. Do you need to "use" it from another namespace? Perhaps you need to add a use statement for one of the following: Symfony\Component\Debug\Exception\UndefinedFunctionException.', + ), + ); + } +} diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php new file mode 100644 index 0000000000000..8ba08cb2aa97d --- /dev/null +++ b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; + +class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideUndefinedFunctionData + */ + public function testUndefinedFunction($error, $translatedMessage) + { + $handler = new UndefinedFunctionFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceof('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception); + $this->assertSame($translatedMessage, $exception->getMessage()); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideUndefinedFunctionData() + { + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function test_namespaced_function()', + ), + 'Attempted to call function "test_namespaced_function" from the global namespace in foo.php line 12. Did you mean to call: "\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function"?', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()', + ), + 'Attempted to call function "test_namespaced_function" from namespace "Foo\\Bar\\Baz" in foo.php line 12. Did you mean to call: "\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function"?', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function foo()', + ), + 'Attempted to call function "foo" from the global namespace in foo.php line 12.', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()', + ), + 'Attempted to call function "foo" from namespace "Foo\Bar\Baz" in foo.php line 12.', + ), + ); + } +} + +function test_namespaced_function() +{ +} diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/PEARClass.php b/src/Symfony/Component/Debug/Tests/Fixtures/PEARClass.php new file mode 100644 index 0000000000000..39f228182e0fa --- /dev/null +++ b/src/Symfony/Component/Debug/Tests/Fixtures/PEARClass.php @@ -0,0 +1,5 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Symfony\Component\Debug\ExceptionHandler; + +class MockExceptionHandler extends Exceptionhandler +{ + public $e; + + public function handle(\Exception $e) + { + $this->e = $e; + } +}