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 ;
2018-08-31 15:35:24 +01:00
use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation ;
2013-08-28 11:04:02 +01:00
/**
* 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 >
2018-08-31 15:35:24 +01:00
* @ author Guilhem Niot < guilhem . niot @ gmail . com >
2013-08-28 11:04:02 +01:00
*/
class DebugClassLoader
{
2014-02-04 10:33:08 +00:00
private $classLoader ;
private $isFinder ;
2019-01-16 21:53:45 +00:00
private $loaded = [];
2014-05-06 14:29:22 +01:00
private static $caseCheck ;
2019-01-16 21:53:45 +00:00
private static $checkedClasses = [];
private static $final = [];
private static $finalMethods = [];
private static $deprecated = [];
private static $internal = [];
private static $internalMethods = [];
private static $annotatedParameters = [];
private static $darwinCache = [ '/' => [ '/' , []]];
private static $method = [];
2013-08-28 11:04:02 +01:00
2015-04-02 08:55:37 +01:00
public function __construct ( callable $classLoader )
2013-08-28 11:04:02 +01:00
{
2015-04-02 08:55:37 +01:00
$this -> classLoader = $classLoader ;
2018-07-26 09:45:46 +01:00
$this -> isFinder = \is_array ( $classLoader ) && method_exists ( $classLoader [ 0 ], 'findFile' );
2014-05-06 14:29:22 +01:00
if ( ! isset ( self :: $caseCheck )) {
2018-07-26 12:13:39 +01:00
$file = file_exists ( __FILE__ ) ? __FILE__ : rtrim ( realpath ( '.' ), \DIRECTORY_SEPARATOR );
$i = strrpos ( $file , \DIRECTORY_SEPARATOR );
2016-03-29 18:31:32 +01:00
$dir = substr ( $file , 0 , 1 + $i );
$file = substr ( $file , 1 + $i );
$test = strtoupper ( $file ) === $file ? strtolower ( $file ) : strtoupper ( $file );
$test = realpath ( $dir . $test );
if ( false === $test || false === $i ) {
2016-03-12 15:40:45 +00:00
// filesystem is case sensitive
self :: $caseCheck = 0 ;
2018-07-05 12:24:53 +01:00
} elseif ( substr ( $test , - \strlen ( $file )) === $file ) {
2016-03-29 18:31:32 +01:00
// filesystem is case insensitive and realpath() normalizes the case of characters
2016-03-12 15:40:45 +00:00
self :: $caseCheck = 1 ;
2016-03-29 18:31:32 +01:00
} elseif ( false !== stripos ( PHP_OS , 'darwin' )) {
// on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
2016-03-12 15:40:45 +00:00
self :: $caseCheck = 2 ;
2016-03-29 18:31:32 +01:00
} else {
// filesystem case checks failed, fallback to disabling them
self :: $caseCheck = 0 ;
2016-03-12 15:40:45 +00:00
}
2014-05-06 14:29:22 +01:00
}
2013-08-28 11:04:02 +01:00
}
/**
* Gets the wrapped class loader .
*
2015-04-02 08:55:37 +01:00
* @ return callable The wrapped class loader
2013-08-28 11:04:02 +01:00
*/
public function getClassLoader ()
{
2015-04-02 08:55:37 +01:00
return $this -> classLoader ;
2013-08-28 11:04:02 +01:00
}
/**
2015-04-26 09:04:53 +01: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
2018-07-05 12:24:53 +01:00
if ( ! \is_array ( $functions = spl_autoload_functions ())) {
2013-08-28 11:04:02 +01:00
return ;
}
foreach ( $functions as $function ) {
spl_autoload_unregister ( $function );
}
foreach ( $functions as $function ) {
2018-07-05 12:24:53 +01:00
if ( ! \is_array ( $function ) || ! $function [ 0 ] instanceof self ) {
2019-01-16 21:53:45 +00:00
$function = [ new static ( $function ), 'loadClass' ];
2013-08-28 11:04:02 +01:00
}
spl_autoload_register ( $function );
}
}
/**
* Disables the wrapping .
*/
public static function disable ()
{
2018-07-05 12:24:53 +01:00
if ( ! \is_array ( $functions = spl_autoload_functions ())) {
2013-08-28 11:04:02 +01:00
return ;
}
foreach ( $functions as $function ) {
spl_autoload_unregister ( $function );
}
foreach ( $functions as $function ) {
2018-07-05 12:24:53 +01:00
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 );
}
}
2019-01-14 08:43:48 +00:00
/**
* @ return string | null
*/
public function findFile ( $class )
2019-01-10 08:28:12 +00:00
{
return $this -> isFinder ? $this -> classLoader [ 0 ] -> findFile ( $class ) ? : null : null ;
}
2013-08-28 11:04:02 +01:00
/**
* Loads the given class or interface .
*
* @ param string $class The name of the class
*
* @ throws \RuntimeException
*/
public function loadClass ( $class )
{
2017-05-21 16:02:52 +01:00
$e = error_reporting ( error_reporting () | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR );
2014-02-04 10:33:08 +00:00
try {
2017-08-25 18:21:35 +01:00
if ( $this -> isFinder && ! isset ( $this -> loaded [ $class ])) {
$this -> loaded [ $class ] = true ;
2018-11-27 10:58:55 +00:00
if ( ! $file = $this -> classLoader [ 0 ] -> findFile ( $class ) ? : false ) {
// no-op
} elseif ( \function_exists ( 'opcache_is_script_cached' ) && @ opcache_is_script_cached ( $file )) {
2017-08-25 18:21:35 +01:00
require $file ;
2017-12-20 19:11:52 +00:00
2018-11-27 10:58:55 +00:00
return ;
} else {
require $file ;
2014-02-04 10:33:08 +00:00
}
} else {
2018-11-25 01:27:51 +00:00
( $this -> classLoader )( $class );
2014-02-04 10:33:08 +00:00
$file = false ;
}
2015-09-28 01:26:11 +01:00
} finally {
2017-05-21 16:02:52 +01:00
error_reporting ( $e );
2014-02-04 10:33:08 +00:00
}
2017-12-20 19:11:52 +00:00
$this -> checkClass ( $class , $file );
}
2014-02-04 10:33:08 +00:00
2017-12-20 19:11:52 +00:00
private function checkClass ( $class , $file = null )
{
2019-06-13 11:57:15 +01:00
$exists = null === $file || class_exists ( $class , false ) || interface_exists ( $class , false ) || trait_exists ( $class , false );
2017-12-20 19:11:52 +00:00
if ( null !== $file && $class && '\\' === $class [ 0 ]) {
2014-05-06 14:29:22 +01:00
$class = substr ( $class , 1 );
}
2014-02-04 10:33:08 +00:00
if ( $exists ) {
2017-12-20 19:11:52 +00:00
if ( isset ( self :: $checkedClasses [ $class ])) {
return ;
}
self :: $checkedClasses [ $class ] = true ;
2014-05-06 14:29:22 +01:00
$refl = new \ReflectionClass ( $class );
2017-12-20 19:11:52 +00:00
if ( null === $file && $refl -> isInternal ()) {
return ;
}
2014-05-06 14:29:22 +01:00
$name = $refl -> getName ();
2014-02-04 10:33:08 +00:00
2019-06-13 11:57:15 +01:00
if ( $name !== $class && 0 === strcasecmp ( $name , $class )) {
2017-04-17 14:29:15 +01:00
throw new \RuntimeException ( sprintf ( 'Case mismatch between loaded and declared class names: "%s" vs "%s".' , $class , $name ));
2014-02-04 10:33:08 +00:00
}
2014-12-29 17:11:47 +00:00
2018-09-22 11:34:13 +01:00
$deprecations = $this -> checkAnnotations ( $refl , $name );
foreach ( $deprecations as $message ) {
@ trigger_error ( $message , E_USER_DEPRECATED );
2017-08-06 14:38:20 +01:00
}
2018-09-22 11:34:13 +01:00
}
2014-12-29 17:11:47 +00:00
2018-09-22 11:34:13 +01:00
if ( ! $file ) {
return ;
}
2017-08-07 13:16:05 +01:00
2018-09-22 11:34:13 +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 ));
2017-08-07 13:16:05 +01:00
}
2018-09-22 11:34:13 +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 ));
}
2017-08-07 13:16:05 +01:00
2018-09-22 11:34:13 +01:00
if ( self :: $caseCheck && $message = $this -> checkCase ( $refl , $file , $class )) {
throw new \RuntimeException ( sprintf ( 'Case mismatch between class and real file names: "%s" vs "%s" in "%s".' , $message [ 0 ], $message [ 1 ], $message [ 2 ]));
}
}
2017-12-20 19:11:52 +00:00
2018-09-22 11:34:13 +01:00
public function checkAnnotations ( \ReflectionClass $refl , $class )
{
2019-01-16 21:53:45 +00:00
$deprecations = [];
2018-09-22 11:34:13 +01:00
// Don't trigger deprecations for classes in the same vendor
2019-06-13 11:57:15 +01:00
if ( 2 > $len = 1 + ( strpos ( $class , '\\' ) ? : strpos ( $class , '_' ))) {
2018-09-22 11:34:13 +01:00
$len = 0 ;
$ns = '' ;
} else {
2019-06-13 11:57:15 +01:00
$ns = str_replace ( '_' , '\\' , substr ( $class , 0 , $len ));
2018-09-22 11:34:13 +01:00
}
2017-12-20 19:11:52 +00:00
2018-09-22 11:34:13 +01:00
// Detect annotations on the class
if ( false !== $doc = $refl -> getDocComment ()) {
2019-01-16 21:53:45 +00:00
foreach ([ 'final' , 'deprecated' , 'internal' ] as $annotation ) {
2019-06-13 11:57:15 +01:00
if ( false !== strpos ( $doc , $annotation ) && preg_match ( '#\n\s+\* @' . $annotation . '(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s' , $doc , $notice )) {
2019-01-17 17:25:46 +00:00
self :: ${$annotation} [ $class ] = isset ( $notice [ 1 ]) ? preg_replace ( '#\.?\r?\n( \*)? *(?= |\r?\n|$)#' , '' , $notice [ 1 ]) : '' ;
2017-08-07 13:16:05 +01:00
}
}
2018-10-17 10:28:44 +01:00
2019-06-13 12:03:18 +01:00
if ( $refl -> isInterface () && false !== strpos ( $doc , 'method' ) && preg_match_all ( '#\n \* @method\s+(static\s+)?+(?:[\w\|&\[\]\\\]+\s+)?(\w+(?:\s*\([^\)]*\))?)+(.+?([[:punct:]]\s*)?)?(?=\r?\n \*(?: @|/$|\r?\n))#' , $doc , $notice , PREG_SET_ORDER )) {
2018-10-17 10:28:44 +01:00
foreach ( $notice as $method ) {
$static = '' !== $method [ 1 ];
$name = $method [ 2 ];
$description = $method [ 3 ] ? ? null ;
if ( false === strpos ( $name , '(' )) {
$name .= '()' ;
}
if ( null !== $description ) {
$description = trim ( $description );
if ( ! isset ( $method [ 4 ])) {
$description .= '.' ;
}
}
2019-01-16 21:53:45 +00:00
self :: $method [ $class ][] = [ $class , $name , $static , $description ];
2018-10-17 10:28:44 +01:00
}
}
2018-09-22 11:34:13 +01:00
}
2017-08-07 13:16:05 +01:00
2019-06-13 11:57:15 +01:00
$parent = get_parent_class ( $class );
2018-09-22 11:34:13 +01:00
$parentAndOwnInterfaces = $this -> getOwnInterfaces ( $class , $parent );
if ( $parent ) {
$parentAndOwnInterfaces [ $parent ] = $parent ;
2017-08-07 13:16:05 +01:00
2018-09-22 11:34:13 +01:00
if ( ! isset ( self :: $checkedClasses [ $parent ])) {
$this -> checkClass ( $parent );
2017-08-06 14:38:20 +01:00
}
2018-09-22 11:34:13 +01:00
if ( isset ( self :: $final [ $parent ])) {
$deprecations [] = sprintf ( 'The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".' , $parent , self :: $final [ $parent ], $class );
2017-08-07 13:16:05 +01:00
}
2018-09-22 11:34:13 +01:00
}
2014-12-29 17:11:47 +00:00
2018-09-22 11:34:13 +01:00
// Detect if the parent is annotated
2019-06-13 11:57:15 +01:00
foreach ( $parentAndOwnInterfaces + class_uses ( $class , false ) as $use ) {
2018-09-22 11:34:13 +01:00
if ( ! isset ( self :: $checkedClasses [ $use ])) {
$this -> checkClass ( $use );
}
2019-06-13 11:57:15 +01:00
if ( isset ( self :: $deprecated [ $use ]) && strncmp ( $ns , str_replace ( '_' , '\\' , $use ), $len ) && ! isset ( self :: $deprecated [ $class ])) {
2018-09-22 11:34:13 +01:00
$type = class_exists ( $class , false ) ? 'class' : ( interface_exists ( $class , false ) ? 'interface' : 'trait' );
$verb = class_exists ( $use , false ) || interface_exists ( $class , false ) ? 'extends' : ( interface_exists ( $use , false ) ? 'implements' : 'uses' );
2016-01-09 16:31:38 +00:00
2018-09-22 11:34:13 +01:00
$deprecations [] = sprintf ( 'The "%s" %s %s "%s" that is deprecated%s.' , $class , $type , $verb , $use , self :: $deprecated [ $use ]);
}
2019-06-13 11:57:15 +01:00
if ( isset ( self :: $internal [ $use ]) && strncmp ( $ns , str_replace ( '_' , '\\' , $use ), $len )) {
2018-09-22 11:34:13 +01:00
$deprecations [] = sprintf ( 'The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".' , $use , class_exists ( $use , false ) ? 'class' : ( interface_exists ( $use , false ) ? 'interface' : 'trait' ), self :: $internal [ $use ], $class );
}
2018-10-17 10:28:44 +01:00
if ( isset ( self :: $method [ $use ])) {
if ( $refl -> isAbstract ()) {
if ( isset ( self :: $method [ $class ])) {
self :: $method [ $class ] = array_merge ( self :: $method [ $class ], self :: $method [ $use ]);
} else {
self :: $method [ $class ] = self :: $method [ $use ];
}
} elseif ( ! $refl -> isInterface ()) {
$hasCall = $refl -> hasMethod ( '__call' );
$hasStaticCall = $refl -> hasMethod ( '__callStatic' );
foreach ( self :: $method [ $use ] as $method ) {
list ( $interface , $name , $static , $description ) = $method ;
if ( $static ? $hasStaticCall : $hasCall ) {
continue ;
}
$realName = substr ( $name , 0 , strpos ( $name , '(' ));
if ( ! $refl -> hasMethod ( $realName ) || ! ( $methodRefl = $refl -> getMethod ( $realName )) -> isPublic () || ( $static && ! $methodRefl -> isStatic ()) || ( ! $static && $methodRefl -> isStatic ())) {
$deprecations [] = sprintf ( 'Class "%s" should implement method "%s::%s"%s' , $class , ( $static ? 'static ' : '' ) . $interface , $name , null == $description ? '.' : ': ' . $description );
}
}
}
}
2018-09-22 11:34:13 +01:00
}
2017-08-07 13:16:05 +01:00
2019-06-13 11:57:15 +01:00
if ( trait_exists ( $class )) {
2018-09-22 11:34:13 +01:00
return $deprecations ;
}
2016-01-09 16:31:38 +00:00
2018-09-22 21:55:36 +01:00
// Inherit @final, @internal and @param annotations for methods
2019-01-16 21:53:45 +00:00
self :: $finalMethods [ $class ] = [];
self :: $internalMethods [ $class ] = [];
self :: $annotatedParameters [ $class ] = [];
2018-09-22 11:34:13 +01:00
foreach ( $parentAndOwnInterfaces as $use ) {
2019-01-16 21:53:45 +00:00
foreach ([ 'finalMethods' , 'internalMethods' , 'annotatedParameters' ] as $property ) {
2018-09-22 11:34:13 +01:00
if ( isset ( self :: ${$property} [ $use ])) {
self :: ${$property} [ $class ] = self :: ${$property} [ $class ] ? self :: ${$property} [ $use ] + self :: ${$property} [ $class ] : self :: ${$property} [ $use ];
2017-08-06 14:38:20 +01:00
}
2018-09-22 11:34:13 +01:00
}
}
2016-01-09 16:31:38 +00:00
2018-09-22 11:34:13 +01:00
foreach ( $refl -> getMethods ( \ReflectionMethod :: IS_PUBLIC | \ReflectionMethod :: IS_PROTECTED ) as $method ) {
if ( $method -> class !== $class ) {
continue ;
}
2018-08-31 15:35:24 +01:00
2018-09-22 11:34:13 +01:00
if ( $parent && isset ( self :: $finalMethods [ $parent ][ $method -> name ])) {
list ( $declaringClass , $message ) = self :: $finalMethods [ $parent ][ $method -> name ];
$deprecations [] = sprintf ( 'The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".' , $declaringClass , $method -> name , $message , $class );
}
2018-08-31 15:35:24 +01:00
2018-09-22 11:34:13 +01:00
if ( isset ( self :: $internalMethods [ $class ][ $method -> name ])) {
list ( $declaringClass , $message ) = self :: $internalMethods [ $class ][ $method -> name ];
2019-06-13 11:57:15 +01:00
if ( strncmp ( $ns , $declaringClass , $len )) {
2018-09-22 11:34:13 +01:00
$deprecations [] = sprintf ( 'The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".' , $declaringClass , $method -> name , $message , $class );
2018-08-31 15:35:24 +01:00
}
2014-12-29 17:11:47 +00:00
}
2018-08-31 15:35:24 +01:00
2018-09-22 21:55:36 +01:00
// To read method annotations
$doc = $method -> getDocComment ();
2018-09-09 10:07:24 +01:00
2018-09-22 21:55:36 +01:00
if ( isset ( self :: $annotatedParameters [ $class ][ $method -> name ])) {
2019-01-16 21:53:45 +00:00
$definedParameters = [];
2018-09-22 21:55:36 +01:00
foreach ( $method -> getParameters () as $parameter ) {
$definedParameters [ $parameter -> name ] = true ;
}
2018-08-31 15:35:24 +01:00
2018-09-22 21:55:36 +01:00
foreach ( self :: $annotatedParameters [ $class ][ $method -> name ] as $parameterName => $deprecation ) {
2019-01-25 19:28:19 +00:00
if ( ! isset ( $definedParameters [ $parameterName ]) && ! ( $doc && preg_match ( " / \\ n \\ s+ \\ * @param +((?(?!callable * \ ().*?|callable * \ (.* \ ).*?))(?<= ) \\ \$ { $parameterName } \\ b/ " , $doc ))) {
2018-09-22 21:55:36 +01:00
$deprecations [] = sprintf ( $deprecation , $class );
2018-08-31 15:35:24 +01:00
}
}
2018-09-22 21:55:36 +01:00
}
2018-08-31 15:35:24 +01:00
2018-09-22 21:55:36 +01:00
if ( ! $doc ) {
2018-09-22 11:34:13 +01:00
continue ;
2017-08-07 13:16:05 +01:00
}
2018-09-22 21:55:36 +01:00
$finalOrInternal = false ;
2019-01-16 21:53:45 +00:00
foreach ([ 'final' , 'internal' ] as $annotation ) {
2019-06-13 11:57:15 +01:00
if ( false !== strpos ( $doc , $annotation ) && preg_match ( '#\n\s+\* @' . $annotation . '(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s' , $doc , $notice )) {
2019-01-17 17:25:46 +00:00
$message = isset ( $notice [ 1 ]) ? preg_replace ( '#\.?\r?\n( \*)? *(?= |\r?\n|$)#' , '' , $notice [ 1 ]) : '' ;
2019-01-16 21:53:45 +00:00
self :: $ { $annotation . 'Methods' }[ $class ][ $method -> name ] = [ $class , $message ];
2018-09-22 21:55:36 +01:00
$finalOrInternal = true ;
2018-08-31 15:35:24 +01:00
}
2014-12-29 17:11:47 +00:00
}
2018-09-22 21:55:36 +01:00
2019-06-13 11:57:15 +01:00
if ( $finalOrInternal || $method -> isConstructor () || false === strpos ( $doc , '@param' ) || StatelessInvocation :: class === $class ) {
2018-09-22 21:55:36 +01:00
continue ;
}
2019-01-25 19:28:19 +00:00
if ( ! preg_match_all ( '#\n\s+\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\$([a-zA-Z0-9_\x7f-\xff]++)#' , $doc , $matches , PREG_SET_ORDER )) {
2018-09-22 21:55:36 +01:00
continue ;
}
if ( ! isset ( self :: $annotatedParameters [ $class ][ $method -> name ])) {
2019-01-16 21:53:45 +00:00
$definedParameters = [];
2018-09-22 21:55:36 +01:00
foreach ( $method -> getParameters () as $parameter ) {
$definedParameters [ $parameter -> name ] = true ;
2018-08-31 15:35:24 +01:00
}
2018-09-22 21:55:36 +01:00
}
foreach ( $matches as list (, $parameterType , $parameterName )) {
if ( ! isset ( $definedParameters [ $parameterName ])) {
$parameterType = trim ( $parameterType );
self :: $annotatedParameters [ $class ][ $method -> name ][ $parameterName ] = sprintf ( 'The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its parent class "%s", not defining it is deprecated.' , $method -> name , $parameterType ? $parameterType . ' ' : '' , $parameterName , $method -> class );
2014-12-29 17:11:47 +00:00
}
}
2014-02-04 10:33:08 +00:00
}
2018-09-22 11:34:13 +01:00
return $deprecations ;
}
2014-02-04 10:33:08 +00:00
2018-09-22 11:34:13 +01:00
public function checkCase ( \ReflectionClass $refl , $file , $class )
{
$real = explode ( '\\' , $class . strrchr ( $file , '.' ));
$tail = explode ( \DIRECTORY_SEPARATOR , str_replace ( '/' , \DIRECTORY_SEPARATOR , $file ));
2015-09-14 08:37:59 +01:00
2018-09-22 11:34:13 +01:00
$i = \count ( $tail ) - 1 ;
$j = \count ( $real ) - 1 ;
2015-09-14 08:37:59 +01:00
2018-09-22 11:34:13 +01:00
while ( isset ( $tail [ $i ], $real [ $j ]) && $tail [ $i ] === $real [ $j ]) {
-- $i ;
-- $j ;
2014-02-04 10:33:08 +00:00
}
2018-09-22 11:34:13 +01:00
array_splice ( $tail , 0 , $i + 1 );
2014-02-04 10:33:08 +00:00
2018-09-22 11:34:13 +01:00
if ( ! $tail ) {
return ;
}
2015-09-14 08:37:59 +01:00
2018-09-22 11:34:13 +01:00
$tail = \DIRECTORY_SEPARATOR . implode ( \DIRECTORY_SEPARATOR , $tail );
$tailLen = \strlen ( $tail );
$real = $refl -> getFileName ();
2015-09-14 08:37:59 +01:00
2018-09-22 11:34:13 +01:00
if ( 2 === self :: $caseCheck ) {
$real = $this -> darwinRealpath ( $real );
}
2015-09-14 08:37:59 +01:00
2018-09-22 11:34:13 +01:00
if ( 0 === substr_compare ( $real , $tail , - $tailLen , $tailLen , true )
&& 0 !== substr_compare ( $real , $tail , - $tailLen , $tailLen , false )
) {
2019-01-16 21:53:45 +00:00
return [ substr ( $tail , - $tailLen + 1 ), substr ( $real , - $tailLen + 1 ), substr ( $real , 0 , - $tailLen + 1 )];
2018-09-22 11:34:13 +01:00
}
}
2015-08-03 20:57:20 +01:00
2018-09-22 11:34:13 +01:00
/**
* `realpath` on MacOSX doesn ' t normalize the case of characters .
*/
private function darwinRealpath ( $real )
{
$i = 1 + strrpos ( $real , '/' );
$file = substr ( $real , $i );
$real = substr ( $real , 0 , $i );
if ( isset ( self :: $darwinCache [ $real ])) {
$kDir = $real ;
} else {
$kDir = strtolower ( $real );
2015-08-03 20:57:20 +01:00
2018-09-22 11:34:13 +01:00
if ( isset ( self :: $darwinCache [ $kDir ])) {
$real = self :: $darwinCache [ $kDir ][ 0 ];
} else {
$dir = getcwd ();
chdir ( $real );
$real = getcwd () . '/' ;
chdir ( $dir );
$dir = $real ;
$k = $kDir ;
$i = \strlen ( $dir ) - 1 ;
while ( ! isset ( self :: $darwinCache [ $k ])) {
2019-01-16 21:53:45 +00:00
self :: $darwinCache [ $k ] = [ $dir , []];
2018-09-22 11:34:13 +01:00
self :: $darwinCache [ $dir ] = & self :: $darwinCache [ $k ];
while ( '/' !== $dir [ -- $i ]) {
}
$k = substr ( $k , 0 , ++ $i );
$dir = substr ( $dir , 0 , $i -- );
2014-04-24 13:55:37 +01:00
}
2018-09-22 11:34:13 +01:00
}
}
2014-02-21 16:50:25 +00:00
2018-09-22 11:34:13 +01:00
$dirFiles = self :: $darwinCache [ $kDir ][ 1 ];
2014-02-21 16:50:25 +00:00
2019-02-23 23:39:04 +00:00
if ( ! isset ( $dirFiles [ $file ]) && ') : eval()\'d code' === substr ( $file , - 17 )) {
// Get the file name from "file_name.php(123) : eval()'d code"
$file = substr ( $file , 0 , strrpos ( $file , '(' , - 17 ));
}
2018-09-22 11:34:13 +01:00
if ( isset ( $dirFiles [ $file ])) {
return $real .= $dirFiles [ $file ];
}
$kFile = strtolower ( $file );
if ( ! isset ( $dirFiles [ $kFile ])) {
foreach ( scandir ( $real , 2 ) as $f ) {
if ( '.' !== $f [ 0 ]) {
$dirFiles [ $f ] = $f ;
if ( $f === $file ) {
$kFile = $k = $file ;
} elseif ( $f !== $k = strtolower ( $f )) {
$dirFiles [ $k ] = $f ;
}
2014-02-04 10:33:08 +00:00
}
2014-04-24 13:55:37 +01:00
}
2018-09-22 11:34:13 +01:00
self :: $darwinCache [ $kDir ][ 1 ] = $dirFiles ;
2013-08-28 11:04:02 +01:00
}
2018-09-22 11:34:13 +01:00
return $real .= $dirFiles [ $kFile ];
2013-08-28 11:04:02 +01:00
}
2017-08-07 13:16:05 +01:00
/**
* `class_implements` includes interfaces from the parents so we have to manually exclude them .
*
* @ param string $class
* @ param string | false $parent
*
* @ return string []
*/
private function getOwnInterfaces ( $class , $parent )
{
$ownInterfaces = class_implements ( $class , false );
if ( $parent ) {
foreach ( class_implements ( $parent , false ) as $interface ) {
unset ( $ownInterfaces [ $interface ]);
}
}
foreach ( $ownInterfaces as $interface ) {
foreach ( class_implements ( $interface ) as $interface ) {
unset ( $ownInterfaces [ $interface ]);
}
}
return $ownInterfaces ;
}
2013-08-28 11:04:02 +01:00
}