2021-04-08 08:08:59 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* 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 Composer\Autoload\ClassLoader as ComposerClassLoader ;
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader ;
use Symfony\Component\Debug\DebugClassLoader ;
use Symfony\Component\Debug\Exception\ClassNotFoundException ;
use Symfony\Component\Debug\Exception\FatalErrorException ;
2022-12-14 15:55:13 +00:00
@ trigger_error ( sprintf ( 'The "%s" class is deprecated since Symfony 4.4, use "%s" instead.' , ClassNotFoundFatalErrorHandler :: class , \Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler :: class ), \E_USER_DEPRECATED );
2021-04-08 08:08:59 +00:00
/**
* ErrorHandler for classes that do not exist .
*
* @ author Fabien Potencier < fabien @ symfony . com >
2022-12-14 15:55:13 +00:00
*
* @ deprecated since Symfony 4.4 , use Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler instead .
2021-04-08 08:08:59 +00:00
*/
class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
{
/**
* { @ inheritdoc }
*/
public function handleError ( array $error , FatalErrorException $exception )
{
if ( ! preg_match ( '/^(Class|Interface|Trait) [\'"]([^\'"]+)[\'"] not found$/' , $error [ 'message' ], $matches )) {
return null ;
}
$typeName = strtolower ( $matches [ 1 ]);
$fullyQualifiedClassName = $matches [ 2 ];
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".' , $typeName , $className , $namespacePrefix );
$tail = ' for another namespace?' ;
} else {
$className = $fullyQualifiedClassName ;
$message = sprintf ( 'Attempted to load %s "%s" from the global namespace.' , $typeName , $className );
$tail = '?' ;
}
if ( $candidates = $this -> getClassCandidates ( $className )) {
$tail = array_pop ( $candidates ) . '"?' ;
if ( $candidates ) {
$tail = ' for e.g. "' . implode ( '", "' , $candidates ) . '" or "' . $tail ;
} else {
$tail = ' for "' . $tail ;
}
}
$message .= " \n Did you forget a \" use \" statement " . $tail ;
return new ClassNotFoundException ( $message , $exception );
}
/**
* Tries to guess the full namespace for a given class name .
*
* By default , it looks for PSR - 0 and PSR - 4 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
*/
2022-12-14 15:55:13 +00:00
private function getClassCandidates ( string $class ) : array
2021-04-08 08:08:59 +00:00
{
if ( ! \is_array ( $functions = spl_autoload_functions ())) {
return [];
}
// find Symfony and Composer autoloaders
$classes = [];
foreach ( $functions as $function ) {
if ( ! \is_array ( $function )) {
continue ;
}
// get class loaders wrapped by DebugClassLoader
if ( $function [ 0 ] instanceof DebugClassLoader ) {
$function = $function [ 0 ] -> getClassLoader ();
if ( ! \is_array ( $function )) {
continue ;
}
}
if ( $function [ 0 ] instanceof ComposerClassLoader || $function [ 0 ] instanceof SymfonyClassLoader ) {
foreach ( $function [ 0 ] -> getPrefixes () as $prefix => $paths ) {
foreach ( $paths as $path ) {
$classes = array_merge ( $classes , $this -> findClassInPath ( $path , $class , $prefix ));
}
}
}
if ( $function [ 0 ] instanceof ComposerClassLoader ) {
foreach ( $function [ 0 ] -> getPrefixesPsr4 () as $prefix => $paths ) {
foreach ( $paths as $path ) {
$classes = array_merge ( $classes , $this -> findClassInPath ( $path , $class , $prefix ));
}
}
}
}
return array_unique ( $classes );
}
2022-12-14 15:55:13 +00:00
private function findClassInPath ( string $path , string $class , string $prefix ) : array
2021-04-08 08:08:59 +00:00
{
if ( ! $path = realpath ( $path . '/' . strtr ( $prefix , '\\_' , '//' )) ? : realpath ( $path . '/' . \dirname ( strtr ( $prefix , '\\_' , '//' ))) ? : realpath ( $path )) {
return [];
}
$classes = [];
$filename = $class . '.php' ;
foreach ( new \RecursiveIteratorIterator ( new \RecursiveDirectoryIterator ( $path , \RecursiveDirectoryIterator :: SKIP_DOTS ), \RecursiveIteratorIterator :: LEAVES_ONLY ) as $file ) {
if ( $filename == $file -> getFileName () && $class = $this -> convertFileToClass ( $path , $file -> getPathName (), $prefix )) {
$classes [] = $class ;
}
}
return $classes ;
}
2022-12-14 15:55:13 +00:00
private function convertFileToClass ( string $path , string $file , string $prefix ) : ? string
2021-04-08 08:08:59 +00:00
{
$candidates = [
// namespaced class
$namespacedClass = str_replace ([ $path . \DIRECTORY_SEPARATOR , '.php' , '/' ], [ '' , '' , '\\' ], $file ),
// namespaced class (with target dir)
$prefix . $namespacedClass ,
// namespaced class (with target dir and separator)
$prefix . '\\' . $namespacedClass ,
// PEAR class
str_replace ( '\\' , '_' , $namespacedClass ),
// PEAR class (with target dir)
str_replace ( '\\' , '_' , $prefix . $namespacedClass ),
// PEAR class (with target dir and separator)
str_replace ( '\\' , '_' , $prefix . '\\' . $namespacedClass ),
];
if ( $prefix ) {
$candidates = array_filter ( $candidates , function ( $candidate ) use ( $prefix ) { return 0 === strpos ( $candidate , $prefix ); });
}
// 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.
foreach ( $candidates as $candidate ) {
if ( $this -> classExists ( $candidate )) {
return $candidate ;
}
}
try {
require_once $file ;
} catch ( \Throwable $e ) {
return null ;
}
foreach ( $candidates as $candidate ) {
if ( $this -> classExists ( $candidate )) {
return $candidate ;
}
}
return null ;
}
2022-12-14 15:55:13 +00:00
private function classExists ( string $class ) : bool
2021-04-08 08:08:59 +00:00
{
return class_exists ( $class , false ) || interface_exists ( $class , false ) || trait_exists ( $class , false );
}
}