Вход Регистрация
Файл: vendor/filp/whoops/src/Whoops/Run.php
Строк: 607
<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops;

use 
InvalidArgumentException;
use 
Throwable;
use 
WhoopsExceptionErrorException;
use 
WhoopsExceptionInspector;
use 
WhoopsHandlerCallbackHandler;
use 
WhoopsHandlerHandler;
use 
WhoopsHandlerHandlerInterface;
use 
WhoopsUtilMisc;
use 
WhoopsUtilSystemFacade;

final class 
Run implements RunInterface
{
    
/**
     * @var bool
     */
    
private $isRegistered;

    
/**
     * @var bool
     */
    
private $allowQuit       true;

    
/**
     * @var bool
     */
    
private $sendOutput      true;

    
/**
     * @var integer|false
     */
    
private $sendHttpCode    500;

    
/**
     * @var integer|false
     */
    
private $sendExitCode    1;

    
/**
     * @var HandlerInterface[]
     */
    
private $handlerStack = [];

    
/**
     * @var array
     * @psalm-var list<array{patterns: string, levels: int}>
     */
    
private $silencedPatterns = [];

    
/**
     * @var SystemFacade
     */
    
private $system;

    
/**
     * In certain scenarios, like in shutdown handler, we can not throw exceptions.
     *
     * @var bool
     */
    
private $canThrowExceptions true;

    public function 
__construct(SystemFacade $system null)
    {
        
$this->system $system ?: new SystemFacade;
    }

    
/**
     * Explicitly request your handler runs as the last of all currently registered handlers.
     *
     * @param callable|HandlerInterface $handler
     *
     * @return Run
     */
    
public function appendHandler($handler)
    {
        
array_unshift($this->handlerStack$this->resolveHandler($handler));
        return 
$this;
    }

    
/**
     * Explicitly request your handler runs as the first of all currently registered handlers.
     *
     * @param callable|HandlerInterface $handler
     *
     * @return Run
     */
    
public function prependHandler($handler)
    {
        return 
$this->pushHandler($handler);
    }

    
/**
     * Register your handler as the last of all currently registered handlers (to be executed first).
     * Prefer using appendHandler and prependHandler for clarity.
     *
     * @param callable|HandlerInterface $handler
     *
     * @return Run
     *
     * @throws InvalidArgumentException If argument is not callable or instance of HandlerInterface.
     */
    
public function pushHandler($handler)
    {
        
$this->handlerStack[] = $this->resolveHandler($handler);
        return 
$this;
    }

    
/**
     * Removes and returns the last handler pushed to the handler stack.
     *
     * @see Run::removeFirstHandler(), Run::removeLastHandler()
     *
     * @return HandlerInterface|null
     */
    
public function popHandler()
    {
        return 
array_pop($this->handlerStack);
    }

    
/**
     * Removes the first handler.
     *
     * @return void
     */
    
public function removeFirstHandler()
    {
        
array_pop($this->handlerStack);
    }

    
/**
     * Removes the last handler.
     *
     * @return void
     */
    
public function removeLastHandler()
    {
        
array_shift($this->handlerStack);
    }

    
/**
     * Returns an array with all handlers, in the order they were added to the stack.
     *
     * @return array
     */
    
public function getHandlers()
    {
        return 
$this->handlerStack;
    }

    
/**
     * Clears all handlers in the handlerStack, including the default PrettyPage handler.
     *
     * @return Run
     */
    
public function clearHandlers()
    {
        
$this->handlerStack = [];
        return 
$this;
    }

    
/**
     * Registers this instance as an error handler.
     *
     * @return Run
     */
    
public function register()
    {
        if (!
$this->isRegistered) {
            
// Workaround PHP bug 42098
            // https://bugs.php.net/bug.php?id=42098
            
class_exists("\Whoops\Exception\ErrorException");
            
class_exists("\Whoops\Exception\FrameCollection");
            
class_exists("\Whoops\Exception\Frame");
            
class_exists("\Whoops\Exception\Inspector");

            
$this->system->setErrorHandler([$thisself::ERROR_HANDLER]);
            
$this->system->setExceptionHandler([$thisself::EXCEPTION_HANDLER]);
            
$this->system->registerShutdownFunction([$thisself::SHUTDOWN_HANDLER]);

            
$this->isRegistered true;
        }

        return 
$this;
    }

    
/**
     * Unregisters all handlers registered by this WhoopsRun instance.
     *
     * @return Run
     */
    
public function unregister()
    {
        if (
$this->isRegistered) {
            
$this->system->restoreExceptionHandler();
            
$this->system->restoreErrorHandler();

            
$this->isRegistered false;
        }

        return 
$this;
    }

    
/**
     * Should Whoops allow Handlers to force the script to quit?
     *
     * @param bool|int $exit
     *
     * @return bool
     */
    
public function allowQuit($exit null)
    {
        if (
func_num_args() == 0) {
            return 
$this->allowQuit;
        }

        return 
$this->allowQuit = (bool) $exit;
    }

    
/**
     * Silence particular errors in particular files.
     *
     * @param array|string $patterns List or a single regex pattern to match.
     * @param int          $levels   Defaults to E_STRICT | E_DEPRECATED.
     *
     * @return Run
     */
    
public function silenceErrorsInPaths($patterns$levels 10240)
    {
        
$this->silencedPatterns array_merge(
            
$this->silencedPatterns,
            
array_map(
                function (
$pattern) use ($levels) {
                    return [
                        
"pattern" => $pattern,
                        
"levels" => $levels,
                    ];
                },
                (array) 
$patterns
            
)
        );

        return 
$this;
    }

    
/**
     * Returns an array with silent errors in path configuration.
     *
     * @return array
     */
    
public function getSilenceErrorsInPaths()
    {
        return 
$this->silencedPatterns;
    }

    
/**
     * Should Whoops send HTTP error code to the browser if possible?
     * Whoops will by default send HTTP code 500, but you may wish to
     * use 502, 503, or another 5xx family code.
     *
     * @param bool|int $code
     *
     * @return int|false
     *
     * @throws InvalidArgumentException
     */
    
public function sendHttpCode($code null)
    {
        if (
func_num_args() == 0) {
            return 
$this->sendHttpCode;
        }

        if (!
$code) {
            return 
$this->sendHttpCode false;
        }

        if (
$code === true) {
            
$code 500;
        }

        if (
$code 400 || 600 <= $code) {
            throw new 
InvalidArgumentException(
                
"Invalid status code '$code', must be 4xx or 5xx"
            
);
        }

        return 
$this->sendHttpCode $code;
    }

    
/**
     * Should Whoops exit with a specific code on the CLI if possible?
     * Whoops will exit with 1 by default, but you can specify something else.
     *
     * @param int $code
     *
     * @return int
     *
     * @throws InvalidArgumentException
     */
    
public function sendExitCode($code null)
    {
        if (
func_num_args() == 0) {
            return 
$this->sendExitCode;
        }

        if (
$code || 255 <= $code) {
            throw new 
InvalidArgumentException(
                
"Invalid status code '$code', must be between 0 and 254"
            
);
        }

        return 
$this->sendExitCode = (int) $code;
    }

    
/**
     * Should Whoops push output directly to the client?
     * If this is false, output will be returned by handleException.
     *
     * @param bool|int $send
     *
     * @return bool
     */
    
public function writeToOutput($send null)
    {
        if (
func_num_args() == 0) {
            return 
$this->sendOutput;
        }

        return 
$this->sendOutput = (bool) $send;
    }

    
/**
     * Handles an exception, ultimately generating a Whoops error page.
     *
     * @param Throwable $exception
     *
     * @return string Output generated by handlers.
     */
    
public function handleException($exception)
    {
        
// Walk the registered handlers in the reverse order
        // they were registered, and pass off the exception
        
$inspector $this->getInspector($exception);

        
// Capture output produced while handling the exception,
        // we might want to send it straight away to the client,
        // or return it silently.
        
$this->system->startOutputBuffering();

        
// Just in case there are no handlers:
        
$handlerResponse null;
        
$handlerContentType null;

        try {
            foreach (
array_reverse($this->handlerStack) as $handler) {
                
$handler->setRun($this);
                
$handler->setInspector($inspector);
                
$handler->setException($exception);

                
// The HandlerInterface does not require an Exception passed to handle()
                // and neither of our bundled handlers use it.
                // However, 3rd party handlers may have already relied on this parameter,
                // and removing it would be possibly breaking for users.
                
$handlerResponse $handler->handle($exception);

                
// Collect the content type for possible sending in the headers.
                
$handlerContentType method_exists($handler'contentType') ? $handler->contentType() : null;

                if (
in_array($handlerResponse, [Handler::LAST_HANDLERHandler::QUIT])) {
                    
// The Handler has handled the exception in some way, and
                    // wishes to quit execution (Handler::QUIT), or skip any
                    // other handlers (Handler::LAST_HANDLER). If $this->allowQuit
                    // is false, Handler::QUIT behaves like Handler::LAST_HANDLER
                    
break;
                }
            }

            
$willQuit $handlerResponse == Handler::QUIT && $this->allowQuit();
        } finally {
            
$output $this->system->cleanOutputBuffer();
        }

        
// If we're allowed to, send output generated by handlers directly
        // to the output, otherwise, and if the script doesn't quit, return
        // it so that it may be used by the caller
        
if ($this->writeToOutput()) {
            
// @todo Might be able to clean this up a bit better
            
if ($willQuit) {
                
// Cleanup all other output buffers before sending our output:
                
while ($this->system->getOutputBufferLevel() > 0) {
                    
$this->system->endOutputBuffering();
                }

                
// Send any headers if needed:
                
if (Misc::canSendHeaders() && $handlerContentType) {
                    
header("Content-Type: {$handlerContentType}");
                }
            }

            
$this->writeToOutputNow($output);
        }

        if (
$willQuit) {
            
// HHVM fix for https://github.com/facebook/hhvm/issues/4055
            
$this->system->flushOutputBuffer();

            
$this->system->stopExecution(
                
$this->sendExitCode()
            );
        }

        return 
$output;
    }

    
/**
     * Converts generic PHP errors to ErrorException instances, before passing them off to be handled.
     *
     * This method MUST be compatible with set_error_handler.
     *
     * @param int         $level
     * @param string      $message
     * @param string|null $file
     * @param int|null    $line
     *
     * @return bool
     *
     * @throws ErrorException
     */
    
public function handleError($level$message$file null$line null)
    {
        if (
$level $this->system->getErrorReportingLevel()) {
            foreach (
$this->silencedPatterns as $entry) {
                
$pathMatches = (bool) preg_match($entry["pattern"], $file);
                
$levelMatches $level $entry["levels"];
                if (
$pathMatches && $levelMatches) {
                    
// Ignore the error, abort handling
                    // See https://github.com/filp/whoops/issues/418
                    
return true;
                }
            }

            
// XXX we pass $level for the "code" param only for BC reasons.
            // see https://github.com/filp/whoops/issues/267
            
$exception = new ErrorException($message/*code*/ $level/*severity*/ $level$file$line);
            if (
$this->canThrowExceptions) {
                throw 
$exception;
            } else {
                
$this->handleException($exception);
            }
            
// Do not propagate errors which were already handled by Whoops.
            
return true;
        }

        
// Propagate error to the next handler, allows error_get_last() to
        // work on silenced errors.
        
return false;
    }

    
/**
     * Special case to deal with Fatal errors and the like.
     *
     * @return void
     */
    
public function handleShutdown()
    {
        
// If we reached this step, we are in shutdown handler.
        // An exception thrown in a shutdown handler will not be propagated
        // to the exception handler. Pass that information along.
        
$this->canThrowExceptions false;

        
$error $this->system->getLastError();
        if (
$error && Misc::isLevelFatal($error['type'])) {
            
// If there was a fatal error,
            // it was not handled in handleError yet.
            
$this->allowQuit false;
            
$this->handleError(
                
$error['type'],
                
$error['message'],
                
$error['file'],
                
$error['line']
            );
        }
    }

    
/**
     * @param Throwable $exception
     *
     * @return Inspector
     */
    
private function getInspector($exception)
    {
        return new 
Inspector($exception);
    }

    
/**
     * Resolves the giving handler.
     *
     * @param callable|HandlerInterface $handler
     *
     * @return HandlerInterface
     *
     * @throws InvalidArgumentException
     */
    
private function resolveHandler($handler)
    {
        if (
is_callable($handler)) {
            
$handler = new CallbackHandler($handler);
        }

        if (!
$handler instanceof HandlerInterface) {
            throw new 
InvalidArgumentException(
                
"Handler must be a callable, or instance of "
                
"Whoops\Handler\HandlerInterface"
            
);
        }

        return 
$handler;
    }

    
/**
     * Echo something to the browser.
     *
     * @param string $output
     *
     * @return Run
     */
    
private function writeToOutputNow($output)
    {
        if (
$this->sendHttpCode() && Misc::canSendHeaders()) {
            
$this->system->setHttpResponseCode(
                
$this->sendHttpCode()
            );
        }

        echo 
$output;

        return 
$this;
    }
}
Онлайн: 0
Реклама