2013-08-28 11:04:02 +01: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 ;
/**
* Autoloader checking if the class is really defined in the file found .
*
2014-02-04 10:33:08 +00:00
* The ClassLoader will wrap all registered autoloaders
* and will throw an exception if a file is found but does
2013-08-28 11:04:02 +01:00
* not declare the class .
*
* @ author Fabien Potencier < fabien @ symfony . com >
* @ author Christophe Coevoet < stof @ notk . org >
2014-02-04 10:33:08 +00:00
* @ author Nicolas Grekas < p @ tchwork . com >
2013-08-28 11:04:02 +01:00
*
* @ api
*/
class DebugClassLoader
{
2014-02-04 10:33:08 +00:00
private $classLoader ;
private $isFinder ;
private $wasFinder ;
2014-05-06 14:29:22 +01:00
private static $caseCheck ;
2013-08-28 11:04:02 +01:00
/**
* Constructor .
*
2014-02-04 10:33:08 +00:00
* @ param callable | object $classLoader
2013-08-28 11:04:02 +01:00
*
* @ api
2014-02-04 10:33:08 +00:00
* @ deprecated since 2.5 , passing an object is deprecated and support for it will be removed in 3.0
2013-08-28 11:04:02 +01:00
*/
2014-02-04 10:33:08 +00:00
public function __construct ( $classLoader )
2013-08-28 11:04:02 +01:00
{
2014-02-04 10:33:08 +00:00
$this -> wasFinder = is_object ( $classLoader ) && method_exists ( $classLoader , 'findFile' );
if ( $this -> wasFinder ) {
$this -> classLoader = array ( $classLoader , 'loadClass' );
$this -> isFinder = true ;
} else {
$this -> classLoader = $classLoader ;
$this -> isFinder = is_array ( $classLoader ) && method_exists ( $classLoader [ 0 ], 'findFile' );
}
2014-05-06 14:29:22 +01:00
if ( ! isset ( self :: $caseCheck )) {
self :: $caseCheck = false !== stripos ( PHP_OS , 'win' ) ? ( false !== stripos ( PHP_OS , 'darwin' ) ? 2 : 1 ) : 0 ;
}
2013-08-28 11:04:02 +01:00
}
/**
* Gets the wrapped class loader .
*
2014-02-04 10:33:08 +00:00
* @ return callable | object a class loader
*
* @ deprecated since 2.5 , returning an object is deprecated and support for it will be removed in 3.0
2013-08-28 11:04:02 +01:00
*/
public function getClassLoader ()
{
2014-02-04 10:33:08 +00:00
if ( $this -> wasFinder ) {
return $this -> classLoader [ 0 ];
} else {
return $this -> classLoader ;
}
2013-08-28 11:04:02 +01:00
}
/**
2014-02-04 10:33:08 +00:00
* Wraps all autoloaders
2013-08-28 11:04:02 +01:00
*/
public static function enable ()
{
2014-02-04 10:33:08 +00:00
// Ensures we don't hit https://bugs.php.net/42098
2014-05-26 11:29:25 +01:00
class_exists ( 'Symfony\Component\Debug\ErrorHandler' );
2014-07-06 12:06:03 +01:00
class_exists ( 'Psr\Log\LogLevel' );
2014-02-04 10:33:08 +00:00
2013-08-28 11:04:02 +01:00
if ( ! is_array ( $functions = spl_autoload_functions ())) {
return ;
}
foreach ( $functions as $function ) {
spl_autoload_unregister ( $function );
}
foreach ( $functions as $function ) {
2014-02-04 10:33:08 +00:00
if ( ! is_array ( $function ) || ! $function [ 0 ] instanceof self ) {
$function = array ( new static ( $function ), 'loadClass' );
2013-08-28 11:04:02 +01:00
}
spl_autoload_register ( $function );
}
}
/**
* Disables the wrapping .
*/
public static function disable ()
{
if ( ! is_array ( $functions = spl_autoload_functions ())) {
return ;
}
foreach ( $functions as $function ) {
spl_autoload_unregister ( $function );
}
foreach ( $functions as $function ) {
if ( is_array ( $function ) && $function [ 0 ] instanceof self ) {
2014-02-04 10:33:08 +00:00
$function = $function [ 0 ] -> getClassLoader ();
2013-08-28 11:04:02 +01:00
}
spl_autoload_register ( $function );
}
}
/**
* Finds a file by class name
*
* @ param string $class A class name to resolve to file
*
* @ return string | null
2014-02-04 10:33:08 +00:00
*
* @ deprecated Deprecated since 2.5 , to be removed in 3.0 .
2013-08-28 11:04:02 +01:00
*/
public function findFile ( $class )
{
2014-02-04 10:33:08 +00:00
if ( $this -> wasFinder ) {
return $this -> classLoader [ 0 ] -> findFile ( $class );
}
2013-08-28 11:04:02 +01:00
}
/**
* Loads the given class or interface .
*
* @ param string $class The name of the class
*
2014-12-04 20:26:11 +00:00
* @ return bool | null True , if loaded
2013-08-28 11:04:02 +01:00
*
* @ throws \RuntimeException
*/
public function loadClass ( $class )
{
2014-02-04 10:33:08 +00:00
ErrorHandler :: stackErrors ();
try {
if ( $this -> isFinder ) {
if ( $file = $this -> classLoader [ 0 ] -> findFile ( $class )) {
require $file ;
}
} else {
call_user_func ( $this -> classLoader , $class );
$file = false ;
}
} catch ( \Exception $e ) {
ErrorHandler :: unstackErrors ();
throw $e ;
}
ErrorHandler :: unstackErrors ();
$exists = class_exists ( $class , false ) || interface_exists ( $class , false ) || ( function_exists ( 'trait_exists' ) && trait_exists ( $class , false ));
2014-05-06 14:29:22 +01:00
if ( '\\' === $class [ 0 ]) {
$class = substr ( $class , 1 );
}
2014-02-04 10:33:08 +00:00
if ( $exists ) {
2014-05-06 14:29:22 +01:00
$refl = new \ReflectionClass ( $class );
$name = $refl -> getName ();
2014-02-04 10:33:08 +00:00
2014-06-02 07:59:12 +01:00
if ( $name !== $class && 0 === strcasecmp ( $name , $class )) {
2014-02-04 10:33:08 +00:00
throw new \RuntimeException ( sprintf ( 'Case mismatch between loaded and declared class names: %s vs %s' , $class , $name ));
}
}
if ( $file ) {
2014-05-06 14:29:22 +01:00
if ( ! $exists ) {
if ( false !== strpos ( $class , '/' )) {
throw new \RuntimeException ( sprintf ( 'Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".' , $class ));
}
2014-02-04 10:33:08 +00:00
2014-05-06 14:29:22 +01:00
throw new \RuntimeException ( sprintf ( 'The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.' , $class , $file ));
}
if ( self :: $caseCheck && preg_match ( '#([/\\\\][a-zA-Z_\x7F-\xFF][a-zA-Z0-9_\x7F-\xFF]*)+\.(php|hh)$#D' , $file , $tail )) {
2014-04-24 13:55:37 +01:00
$tail = $tail [ 0 ];
2014-05-06 14:29:22 +01:00
$real = $refl -> getFilename ();
2014-04-24 13:55:37 +01:00
2014-05-06 14:29:22 +01:00
if ( 2 === self :: $caseCheck ) {
// realpath() on MacOSX doesn't normalize the case of characters
2014-04-24 13:55:37 +01:00
$cwd = getcwd ();
$basename = strrpos ( $real , '/' );
chdir ( substr ( $real , 0 , $basename ));
$basename = substr ( $real , $basename + 1 );
2014-05-06 14:29:22 +01:00
// glob() patterns are case-sensitive even if the underlying fs is not
if ( ! in_array ( $basename , glob ( $basename . '*' , GLOB_NOSORT ), true )) {
$real = getcwd () . '/' ;
$h = opendir ( '.' );
while ( false !== $f = readdir ( $h )) {
if ( 0 === strcasecmp ( $f , $basename )) {
$real .= $f ;
break ;
}
2014-02-21 16:50:25 +00:00
}
2014-05-06 14:29:22 +01:00
closedir ( $h );
2014-02-21 16:50:25 +00:00
}
2014-04-24 13:55:37 +01:00
chdir ( $cwd );
}
2014-02-21 16:50:25 +00:00
2014-12-04 20:26:11 +00:00
if ( 0 === substr_compare ( $real , $tail , - strlen ( $tail ), strlen ( $tail ), true )
2014-04-24 13:55:37 +01:00
&& 0 !== substr_compare ( $real , $tail , - strlen ( $tail ), strlen ( $tail ), false )
) {
throw new \RuntimeException ( sprintf ( 'Case mismatch between class and source file names: %s vs %s' , $class , $real ));
2014-02-04 10:33:08 +00:00
}
2014-04-24 13:55:37 +01:00
}
2013-08-28 11:04:02 +01:00
return true ;
}
}
}