Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
17.65% covered (danger)
17.65%
3 / 17
CRAP
52.73% covered (warning)
52.73%
251 / 476
DebugClassLoader
0.00% covered (danger)
0.00%
0 / 1
17.65% covered (danger)
17.65%
3 / 17
10727.24
52.73% covered (warning)
52.73%
251 / 476
 __construct
0.00% covered (danger)
0.00%
0 / 1
11.82
73.68% covered (warning)
73.68%
14 / 19
 getClassLoader
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 enable
0.00% covered (danger)
0.00%
0 / 1
6.03
90.91% covered (success)
90.91%
10 / 11
 disable
0.00% covered (danger)
0.00%
0 / 1
42
0.00% covered (danger)
0.00%
0 / 9
 checkClasses
0.00% covered (danger)
0.00%
0 / 1
182
0.00% covered (danger)
0.00%
0 / 21
 findFile
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 1
 loadClass
0.00% covered (danger)
0.00%
0 / 1
8.15
86.67% covered (warning)
86.67%
13 / 15
 checkClass
0.00% covered (danger)
0.00%
0 / 1
23.99
76.00% covered (warning)
76.00%
19 / 25
 checkAnnotations
0.00% covered (danger)
0.00%
0 / 1
167.34
87.34% covered (warning)
87.34%
138 / 158
 checkCase
0.00% covered (danger)
0.00%
0 / 1
72
0.00% covered (danger)
0.00%
0 / 19
 darwinRealpath
0.00% covered (danger)
0.00%
0 / 1
210
0.00% covered (danger)
0.00%
0 / 38
 getOwnInterfaces
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
8 / 8
 setReturnType
0.00% covered (danger)
0.00%
0 / 1
31.52
88.10% covered (warning)
88.10%
37 / 42
 normalizeType
100.00% covered (success)
100.00%
1 / 1
8
100.00% covered (success)
100.00%
11 / 11
 patchMethod
0.00% covered (danger)
0.00%
0 / 1
756
0.00% covered (danger)
0.00%
0 / 58
 getUseStatements
0.00% covered (danger)
0.00%
0 / 1
90
0.00% covered (danger)
0.00%
0 / 22
 fixReturnStatements
0.00% covered (danger)
0.00%
0 / 1
182
0.00% covered (danger)
0.00%
0 / 18
<?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\ErrorHandler;
use Doctrine\Common\Persistence\Proxy as LegacyProxy;
use Doctrine\Persistence\Proxy;
use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation;
use PHPUnit\Framework\MockObject\MockObject;
use Prophecy\Prophecy\ProphecySubjectInterface;
use ProxyManager\Proxy\ProxyInterface;
/**
 * Autoloader checking if the class is really defined in the file found.
 *
 * The ClassLoader will wrap all registered autoloaders
 * and will throw an exception if a file is found but does
 * not declare the class.
 *
 * It can also patch classes to turn docblocks into actual return types.
 * This behavior is controlled by the SYMFONY_PATCH_TYPE_DECLARATIONS env var,
 * which is a url-encoded array with the follow parameters:
 *  - "force": any value enables deprecation notices - can be any of:
 *      - "docblock" to patch only docblock annotations
 *      - "object" to turn union types to the "object" type when possible (not recommended)
 *      - "1" to add all possible return types including magic methods
 *      - "0" to add possible return types excluding magic methods
 *  - "php": the target version of PHP - e.g. "7.1" doesn't generate "object" types
 *  - "deprecations": "1" to trigger a deprecation notice when a child class misses a
 *                    return type while the parent declares an "@return" annotation
 *
 * Note that patching doesn't care about any coding style so you'd better to run
 * php-cs-fixer after, with rules "phpdoc_trim_consecutive_blank_line_separation"
 * and "no_superfluous_phpdoc_tags" enabled typically.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Christophe Coevoet <stof@notk.org>
 * @author Nicolas Grekas <p@tchwork.com>
 * @author Guilhem Niot <guilhem.niot@gmail.com>
 */
class DebugClassLoader
{
    private const SPECIAL_RETURN_TYPES = [
        'mixed' => 'mixed',
        'void' => 'void',
        'null' => 'null',
        'resource' => 'resource',
        'static' => 'object',
        '$this' => 'object',
        'boolean' => 'bool',
        'true' => 'bool',
        'false' => 'bool',
        'integer' => 'int',
        'array' => 'array',
        'bool' => 'bool',
        'callable' => 'callable',
        'float' => 'float',
        'int' => 'int',
        'iterable' => 'iterable',
        'object' => 'object',
        'string' => 'string',
        'self' => 'self',
        'parent' => 'parent',
    ];
    private const BUILTIN_RETURN_TYPES = [
        'void' => true,
        'array' => true,
        'bool' => true,
        'callable' => true,
        'float' => true,
        'int' => true,
        'iterable' => true,
        'object' => true,
        'string' => true,
        'self' => true,
        'parent' => true,
    ];
    private const MAGIC_METHODS = [
        '__set' => 'void',
        '__isset' => 'bool',
        '__unset' => 'void',
        '__sleep' => 'array',
        '__wakeup' => 'void',
        '__toString' => 'string',
        '__clone' => 'void',
        '__debugInfo' => 'array',
        '__serialize' => 'array',
        '__unserialize' => 'void',
    ];
    private const INTERNAL_TYPES = [
        'ArrayAccess' => [
            'offsetExists' => 'bool',
            'offsetSet' => 'void',
            'offsetUnset' => 'void',
        ],
        'Countable' => [
            'count' => 'int',
        ],
        'Iterator' => [
            'next' => 'void',
            'valid' => 'bool',
            'rewind' => 'void',
        ],
        'IteratorAggregate' => [
            'getIterator' => '\Traversable',
        ],
        'OuterIterator' => [
            'getInnerIterator' => '\Iterator',
        ],
        'RecursiveIterator' => [
            'hasChildren' => 'bool',
        ],
        'SeekableIterator' => [
            'seek' => 'void',
        ],
        'Serializable' => [
            'serialize' => 'string',
            'unserialize' => 'void',
        ],
        'SessionHandlerInterface' => [
            'open' => 'bool',
            'close' => 'bool',
            'read' => 'string',
            'write' => 'bool',
            'destroy' => 'bool',
            'gc' => 'bool',
        ],
        'SessionIdInterface' => [
            'create_sid' => 'string',
        ],
        'SessionUpdateTimestampHandlerInterface' => [
            'validateId' => 'bool',
            'updateTimestamp' => 'bool',
        ],
        'Throwable' => [
            'getMessage' => 'string',
            'getCode' => 'int',
            'getFile' => 'string',
            'getLine' => 'int',
            'getTrace' => 'array',
            'getPrevious' => '?\Throwable',
            'getTraceAsString' => 'string',
        ],
    ];
    private $classLoader;
    private $isFinder;
    private $loaded = [];
    private $patchTypes;
    private static $caseCheck;
    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 = [];
    private static $returnTypes = [];
    private static $methodTraits = [];
    private static $fileOffsets = [];
    public function __construct(callable $classLoader)
    {
        $this->classLoader = $classLoader;
        $this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile');
        parse_str(getenv('SYMFONY_PATCH_TYPE_DECLARATIONS') ?: '', $this->patchTypes);
        $this->patchTypes += [
            'force' => null,
            'php' => null,
            'deprecations' => false,
        ];
        if (!isset(self::$caseCheck)) {
            $file = is_file(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR);
            $i = strrpos($file, \DIRECTORY_SEPARATOR);
            $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) {
                // filesystem is case sensitive
                self::$caseCheck = 0;
            } elseif (substr($test, -\strlen($file)) === $file) {
                // filesystem is case insensitive and realpath() normalizes the case of characters
                self::$caseCheck = 1;
            } elseif (false !== stripos(PHP_OS, 'darwin')) {
                // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
                self::$caseCheck = 2;
            } else {
                // filesystem case checks failed, fallback to disabling them
                self::$caseCheck = 0;
            }
        }
    }
    /**
     * Gets the wrapped class loader.
     *
     * @return callable The wrapped class loader
     */
    public function getClassLoader(): callable
    {
        return $this->classLoader;
    }
    /**
     * Wraps all autoloaders.
     */
    public static function enable(): void
    {
        // Ensures we don't hit https://bugs.php.net/42098
        class_exists('Symfony\Component\ErrorHandler\ErrorHandler');
        class_exists('Psr\Log\LogLevel');
        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) {
                $function = [new static($function), 'loadClass'];
            }
            spl_autoload_register($function);
        }
    }
    /**
     * Disables the wrapping.
     */
    public static function disable(): void
    {
        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) {
                $function = $function[0]->getClassLoader();
            }
            spl_autoload_register($function);
        }
    }
    public static function checkClasses(): bool
    {
        if (!\is_array($functions = spl_autoload_functions())) {
            return false;
        }
        $loader = null;
        foreach ($functions as $function) {
            if (\is_array($function) && $function[0] instanceof self) {
                $loader = $function[0];
                break;
            }
        }
        if (null === $loader) {
            return false;
        }
        static $offsets = [
            'get_declared_interfaces' => 0,
            'get_declared_traits' => 0,
            'get_declared_classes' => 0,
        ];
        foreach ($offsets as $getSymbols => $i) {
            $symbols = $getSymbols();
            for (; $i < \count($symbols); ++$i) {
                if (!is_subclass_of($symbols[$i], MockObject::class)
                    && !is_subclass_of($symbols[$i], ProphecySubjectInterface::class)
                    && !is_subclass_of($symbols[$i], Proxy::class)
                    && !is_subclass_of($symbols[$i], ProxyInterface::class)
                    && !is_subclass_of($symbols[$i], LegacyProxy::class)
                ) {
                    $loader->checkClass($symbols[$i]);
                }
            }
            $offsets[$getSymbols] = $i;
        }
        return true;
    }
    public function findFile(string $class): ?string
    {
        return $this->isFinder ? ($this->classLoader[0]->findFile($class) ?: null) : null;
    }
    /**
     * Loads the given class or interface.
     *
     * @throws \RuntimeException
     */
    public function loadClass(string $class): void
    {
        $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
        try {
            if ($this->isFinder && !isset($this->loaded[$class])) {
                $this->loaded[$class] = true;