Showing preview only (304K chars total). Download the full file or copy to clipboard to get everything.
Repository: filp/whoops
Branch: master
Commit: 67342bc80785
Files: 76
Total size: 283.3 KB
Directory structure:
gitextract_qw9s0v9n/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── tests.yml
├── .gitignore
├── .mailmap
├── .scrutinizer.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── SECURITY.md
├── composer.json
├── docs/
│ ├── API Documentation.md
│ ├── Framework Integration.md
│ ├── Open Files In An Editor.md
│ └── Replay Errors.md
├── examples/
│ ├── example-ajax-only.php
│ ├── example.php
│ └── lib.php
├── phpunit.xml.dist
├── src/
│ └── Whoops/
│ ├── Exception/
│ │ ├── ErrorException.php
│ │ ├── Formatter.php
│ │ ├── Frame.php
│ │ ├── FrameCollection.php
│ │ └── Inspector.php
│ ├── Handler/
│ │ ├── CallbackHandler.php
│ │ ├── Handler.php
│ │ ├── HandlerInterface.php
│ │ ├── JsonResponseHandler.php
│ │ ├── PlainTextHandler.php
│ │ ├── PrettyPageHandler.php
│ │ └── XmlResponseHandler.php
│ ├── Inspector/
│ │ ├── InspectorFactory.php
│ │ ├── InspectorFactoryInterface.php
│ │ └── InspectorInterface.php
│ ├── Resources/
│ │ ├── css/
│ │ │ ├── prism.css
│ │ │ └── whoops.base.css
│ │ ├── js/
│ │ │ ├── prism.js
│ │ │ └── whoops.base.js
│ │ └── views/
│ │ ├── env_details.html.php
│ │ ├── frame_code.html.php
│ │ ├── frame_list.html.php
│ │ ├── frames_container.html.php
│ │ ├── frames_description.html.php
│ │ ├── header.html.php
│ │ ├── header_outer.html.php
│ │ ├── layout.html.php
│ │ ├── panel_details.html.php
│ │ ├── panel_details_outer.html.php
│ │ ├── panel_left.html.php
│ │ └── panel_left_outer.html.php
│ ├── Run.php
│ ├── RunInterface.php
│ └── Util/
│ ├── HtmlDumperOutput.php
│ ├── Misc.php
│ ├── SystemFacade.php
│ └── TemplateHelper.php
└── tests/
├── Whoops/
│ ├── Exception/
│ │ ├── FormatterTest.php
│ │ ├── FrameCollectionTest.php
│ │ ├── FrameTest.php
│ │ └── InspectorTest.php
│ ├── Handler/
│ │ ├── CallbackHandlerTest.php
│ │ ├── JsonResponseHandlerTest.php
│ │ ├── PlainTextHandlerTest.php
│ │ ├── PrettyPageHandlerTest.php
│ │ └── XmlResponseHandlerTest.php
│ ├── RunTest.php
│ ├── TestCase.php
│ └── Util/
│ ├── HtmlDumperOutputTest.php
│ ├── MiscTest.php
│ ├── SystemFacadeTest.php
│ └── TemplateHelperTest.php
├── bootstrap.php
└── fixtures/
├── frame.lines-test.php
└── template.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{json,php}]
indent_size = 4
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitattributes
================================================
docs/ export-ignore
examples/ export-ignore
tests/ export-ignore
.github/ export-ignore
.editorconfig export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.scrutinizer.yml export-ignore
phpunit.xml.dist export-ignore
CONTRIBUTING.md export-ignore
README.md export-ignore
================================================
FILE: .github/FUNDING.yml
================================================
github: denis-sokolov
================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests
on:
push:
pull_request:
jobs:
tests:
name: PHP ${{ matrix.php }}
runs-on: ubuntu-22.04
strategy:
matrix:
php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2
coverage: none
env:
update: true
- name: Setup Problem Matchers
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Fix PHPUnit Version PHP < 7.4
uses: nick-invision/retry@v3
with:
timeout_minutes: 5
max_attempts: 5
command: composer require "phpunit/phpunit:^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8" --dev --no-update --no-interaction
if: "matrix.php < 7.4"
- name: Fix PHPUnit Version PHP >= 7.4
uses: nick-invision/retry@v3
with:
timeout_minutes: 5
max_attempts: 5
command: composer require "phpunit/phpunit:^9.3.3" --dev --no-update --no-interaction
if: "matrix.php >= 7.4"
- name: Install PHP Dependencies
uses: nick-invision/retry@v3
with:
timeout_minutes: 5
max_attempts: 5
command: composer update --no-interaction --no-progress
- name: Execute PHPUnit
run: vendor/bin/phpunit
================================================
FILE: .gitignore
================================================
composer.lock
phpunit.xml
report
vendor
================================================
FILE: .mailmap
================================================
Denis Sokolov <denis@sokolov.cc>
Filipe Dobreira <dobreira@gmail.com>
================================================
FILE: .scrutinizer.yml
================================================
imports:
- php
filter:
paths: [src/*]
tools:
php_cs_fixer:
config: { level: psr1 }
php_hhvm: true
php_mess_detector: true
sensiolabs_security_checker: true
external_code_coverage:
timeout: '3600'
================================================
FILE: CHANGELOG.md
================================================
# CHANGELOG
## v2.18.0
* Line numbers are now clickable.
## v2.17.0
* Support cursor IDE.
## v2.16.0
* Support PHP `8.4`.
* Drop support for PHP older than `7.1`.
## v2.15.4
* Improve link color in comments.
## v2.15.3
* Improve performance of the syntax highlighting (#758).
## v2.15.2
* Fixed missing code highlight, which additionally led to issue with switching tabs, between application and all frames ([#747](https://github.com/filp/whoops/issues/747)).
## v2.15.1
* Fixed bug with PrettyPageHandler "*Calling `getFrameFilters` method on null*" ([#751](https://github.com/filp/whoops/pull/751)).
## v2.15.0
* Add addFrameFilter ([#749](https://github.com/filp/whoops/pull/749))
## v2.14.6
* Upgraded prismJS to version `1.29.0` due to security issue ([#741][i741]).
[i741]: https://github.com/filp/whoops/pull/741
## v2.14.5
* Allow `ArrayAccess` on super globals.
## v2.14.4
* Fix PHP `5.5` support.
* Allow to use psr/log `2` or `3`.
## v2.14.3
* Support PHP `8.1`.
## v2.14.1
* Fix syntax highlighting scrolling too far.
* Improve the way we detect xdebug linkformat.
## v2.14.0
* Switched syntax highlighting to Prism.js.
Avoids licensing issues with prettify, and uses a maintained, modern project.
## v2.13.0
* Add Netbeans editor.
## v2.12.1
* Avoid redirecting away from an error.
## v2.12.0
* Hide non-string values in super globals when requested.
## v2.11.0
* Customize exit code.
## v2.10.0
* Better chaining on handler classes.
## v2.9.2
* Fix copy button styles.
## v2.9.1
* Fix xdebug function crash on PHP `8`.
## v2.9.0
* `JsonResponseHandler` includes the exception code.
## v2.8.0
* Support PHP 8.
## v2.7.3
* `PrettyPageHandler` functionality to hide superglobal keys has a clearer name
(`hideSuperglobalKey`).
## v2.7.2
* `PrettyPageHandler` now accepts custom js files.
* `PrettyPageHandler` and `templateHelper` is now accessible through inheritance.
## v2.7.1
* Fix a PHP warning in some cases with anonymous classes.
## v2.7.0
* Added `removeFirstHandler` and `removeLastHandler`.
## v2.6.0
* Fix 2.4.0 `pushHandler` changing the order of handlers.
## v2.5.1
* Fix error messaging in a rare case.
## v2.5.0
* Automatically configure xdebug if available.
## v2.4.1
* Try harder to close all output buffers.
## v2.4.0
* Allow to prepend and append handlers.
## v2.3.2
* Various fixes from the community.
## v2.3.1
* Prevent exception in Whoops when caught exception frame is not related to real file.
## v2.3.0
* Show previous exception messages.
## v2.2.0
* Support PHP `7.2`.
## v2.1.0
* Add a `SystemFacade` to allow clients to override Whoops behavior.
* Show frame arguments in `PrettyPageHandler`.
* Highlight the line with the error.
* Add icons to search on Google and Stack Overflow.
## v2.0.0
Backwards compatibility breaking changes:
* `Run` class is now `final`. If you inherited from `Run`, please now instead use a custom `SystemFacade` injected into the `Run` constructor, or contribute your changes to our core.
* PHP < 5.5 support dropped.
================================================
FILE: CONTRIBUTING.md
================================================
If you want to give me some feedback or make a suggestion, create an [issue on GitHub](https://github.com/filp/whoops/issues/new).
If you want to get your hands dirty, great! Here's a couple of steps/guidelines:
- See [a list of possible features to add](https://github.com/filp/whoops/wiki/Possible-features-to-add) for ideas on what can be improved.
- Add tests for your changes (in `tests/`).
- Remember to stick to the existing code style as best as possible. When in doubt, follow `PSR-2`.
- Before investing a lot of time coding, create an issue to get our opinion on your big changes.
- Update the documentation, if applicable.
- If you want to add an integration to a web framework, please [review our guidelines for that](docs/Framework%20Integration.md#contributing-an-integration-with-a-framework).
In `PrettyPageHandler` we are using a Zepto library, but if you are only familiar with jQuery, note that it is pretty much identical.
================================================
FILE: LICENSE.md
================================================
# The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# whoops
PHP errors for cool kids
[](https://packagist.org/packages/filp/whoops)
[](https://packagist.org/packages/filp/whoops)
[](https://github.com/filp/whoops/actions?query=workflow%3ATests)
[](https://scrutinizer-ci.com/g/filp/whoops)
[](https://scrutinizer-ci.com/g/filp/whoops)
-----

**whoops** is an error handler framework for PHP. Out-of-the-box, it provides a pretty
error interface that helps you debug your web projects, but at heart it's a simple yet
powerful stacked error handling system.
## Features
- Flexible, stack-based error handling
- Stand-alone library with (currently) no required dependencies
- Simple API for dealing with exceptions, trace frames & their data
- Includes a pretty rad error page for your webapp projects
- Includes the ability to [open referenced files directly in your editor and IDE](docs/Open%20Files%20In%20An%20Editor.md)
- Includes handlers for different response formats (JSON, XML, SOAP)
- Easy to extend and integrate with existing libraries
- Clean, well-structured & tested code-base
## Sponsors
<a href="https://blackfire.io/docs/introduction?utm_source=whoops&utm_medium=github_readme&utm_campaign=logo"><img src="https://i.imgur.com/zR8rsqk.png" alt="Blackfire.io" width="254" height="64"></a>
## Installing
If you use Laravel 4, Laravel 5.5+ or [Mezzio](https://docs.mezzio.dev/mezzio/), you already have Whoops. There are also community-provided instructions on how to integrate Whoops into
[Silex 1](https://github.com/whoops-php/silex-1),
[Silex 2](https://github.com/texthtml/whoops-silex),
[Phalcon](https://github.com/whoops-php/phalcon),
[Laravel 3](https://gist.github.com/hugomrdias/5169713#file-start-php),
[Laravel 5](https://github.com/GrahamCampbell/Laravel-Exceptions),
[CakePHP 3](https://github.com/dereuromark/cakephp-whoops/tree/cake3),
[CakePHP 4](https://github.com/dereuromark/cakephp-whoops),
[Zend 2](https://github.com/ghislainf/zf2-whoops),
[Zend 3](https://github.com/Ppito/zf3-whoops),
[Yii 1](https://github.com/igorsantos07/yii-whoops),
[FuelPHP](https://github.com/indigophp/fuel-whoops),
[Slim](https://github.com/zeuxisoo/php-slim-whoops/),
[Pimple](https://github.com/texthtml/whoops-pimple),
[Laminas](https://github.com/Ppito/laminas-whoops),
or any framework consuming [StackPHP middlewares](https://github.com/thecodingmachine/whoops-stackphp)
or [PSR-7 middlewares](https://github.com/franzliedke/whoops-middleware).
If you are not using any of these frameworks, here's a very simple way to install:
1. Use [Composer](http://getcomposer.org) to install Whoops into your project:
```bash
composer require filp/whoops
```
1. Register the pretty handler in your code:
```php
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
```
For more options, have a look at the **example files** in [`examples/`](./examples) to get a feel for how things work. Also take a look at the [API Documentation](docs/API%20Documentation.md) and the list of available handlers below.
You may also want to override some system calls Whoops does. To do that, extend `Whoops\Util\SystemFacade`, override functions that you want and pass it as the argument to the `Run` constructor.
You may also collect the HTML generated to process it yourself:
```php
$whoops = new \Whoops\Run;
$whoops->allowQuit(false);
$whoops->writeToOutput(false);
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$html = $whoops->handleException($e);
```
### Available Handlers
**whoops** currently ships with the following built-in handlers, available in the `Whoops\Handler` namespace:
- [`PrettyPageHandler`](https://github.com/filp/whoops/blob/master/src/Whoops/Handler/PrettyPageHandler.php) - Shows a pretty error page when something goes pants-up
- [`PlainTextHandler`](https://github.com/filp/whoops/blob/master/src/Whoops/Handler/PlainTextHandler.php) - Outputs plain text message for use in CLI applications
- [`CallbackHandler`](https://github.com/filp/whoops/blob/master/src/Whoops/Handler/CallbackHandler.php) - Wraps a closure or other callable as a handler. You do not need to use this handler explicitly, **whoops** will automatically wrap any closure or callable you pass to `Whoops\Run::pushHandler`
- [`JsonResponseHandler`](https://github.com/filp/whoops/blob/master/src/Whoops/Handler/JsonResponseHandler.php) - Captures exceptions and returns information on them as a JSON string. Can be used to, for example, play nice with AJAX requests.
- [`XmlResponseHandler`](https://github.com/filp/whoops/blob/master/src/Whoops/Handler/XmlResponseHandler.php) - Captures exceptions and returns information on them as a XML string. Can be used to, for example, play nice with AJAX requests.
You can also use pluggable handlers, such as [SOAP handler](https://github.com/whoops-php/soap).
## Authors
This library was primarily developed by [Filipe Dobreira](https://github.com/filp), and is currently maintained by [Denis Sokolov](https://github.com/denis-sokolov). A lot of awesome fixes and enhancements were also sent in by [various contributors](https://github.com/filp/whoops/contributors). Special thanks to [Graham Campbell](https://github.com/GrahamCampbell) and [Markus Staab](https://github.com/staabm) for continuous participation.
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
Only the latest released version of Whoops is supported.
To facilitate upgrades we almost never make backwards-incompatible changes.
## Reporting a Vulnerability
Please report vulnerabilities over email, by sending an email to `denis` at `sokolov` dot `cc`.
================================================
FILE: composer.json
================================================
{
"name": "filp/whoops",
"license": "MIT",
"description": "php error handling for cool kids",
"keywords": ["library", "error", "handling", "exception", "whoops", "throwable"],
"homepage": "https://filp.github.io/whoops/",
"authors": [
{
"name": "Filipe Dobreira",
"homepage": "https://github.com/filp",
"role": "Developer"
}
],
"scripts": {
"demo": "php -S localhost:8000 ./examples/example.php",
"test": "phpunit --testdox tests"
},
"require": {
"php": "^7.1 || ^8.0",
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3 || ^10.5.58",
"mockery/mockery": "^1.0",
"symfony/var-dumper": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0"
},
"suggest": {
"symfony/var-dumper": "Pretty print complex values better with var-dumper available",
"whoops/soap": "Formats errors as SOAP responses"
},
"autoload": {
"psr-4": {
"Whoops\\": "src/Whoops/"
}
},
"autoload-dev": {
"psr-4": {
"Whoops\\": "tests/Whoops/"
}
},
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
}
}
================================================
FILE: docs/API Documentation.md
================================================
# API Documentation
### Core Classes:
- [`Whoops\Run`](#whoops-run) - The main `Whoops` class - represents the stack and current execution
- [`Whoops\Handler\Handler` and `Whoops\Handler\HandlerInterface`](#handler-abstract) - Abstract representation of a Handler, and utility methods
- [`Whoops\Exception\Inspector`](#inspector) - Exposes methods to inspect an exception
- [`Whoops\Exception\FrameCollection`](#frame-collection) - Exposes methods to work with a list of frames
- [`Whoops\Exception\Frame`](#frame) - Exposes methods to inspect a single stack trace frame from an exception
### Core Handlers:
- [`Whoops\Handler\CallbackHandler`](#handler-callback) - Wraps regular closures as handlers
- [`Whoops\Handler\JsonResponseHandler`](#handler-json) - Formats errors and exceptions as a JSON payload
- [`Whoops\Handler\PrettyPageHandler`](#handler-pretty) - Outputs a detailed, fancy error page
### Core Functions:
- [`Whoops\Util\Misc::isAjaxRequest()`](#fn-ajax) - Determines whether the current request was triggered by XMLHttpRequest
- [`Whoops\Util\Misc::isCommandLine()`](#fn-cli) - Determines whether the current request was triggered via php commandline interface (CLI)
# Core Classes:
## <a name="whoops-run"></a> `Whoops\Run`
The `Run` class models an instance of an execution, and integrates the methods to control whoops' execution in that context, and control the handlers stack.
### Constants
```php
string Run::EXCEPTION_HANDLER // (name for exception handler method)
string Run::ERROR_HANDLER // (name for error handler method)
string Run::SHUTDOWN_HANDLER // (name for shutdown handler method)
```
### Methods
```php
Run::prependHandler(Whoops\HandlerInterface $handler)
#=> Whoops\Run
Run::appendHandler(Whoops\HandlerInterface $handler)
#=> Whoops\Run
Run::removeFirstHandler()
#=> null
Run::removeLastHandler()
#=> null
// Returns all handlers in the stack
Run::getHandlers()
#=> Whoops\HandlerInterface[]
// Returns a Whoops\Inspector instance for a given Exception
Run::getInspector(Exception $exception)
#=> Whoops\Exception\Inspector
// Registers this Whoops\Run instance as an error/exception/shutdown
// handler with PHP
Run::register()
#=> Whoops\Run
// I'll let you guess this one
Run::unregister()
#=> Whoops\Run
// Send a custom exit code in CLI context (default: 1)
Run::sendExitCode($code = null)
#=> int
// If true, allows Whoops to terminate script execution (default: true)
Run::allowQuit($allowQuit = null)
#=> bool
// Silence errors for paths matching regular expressions and PHP error constants.
// Can be called multiple times.
Run::silenceErrorsInPaths($patterns, $levels = E_STRICT | E_DEPRECATED)
#=> Whoops\Run
// If true, allows Whoops to send output produced by handlers directly
// to the client. You'll want to set this to false if you want to
// package the handlers' response into your HTTP response abstraction
// or something (default: true)
Run::writeToOutput($send = null)
#=> bool
// ** HANDLERS **
// These are semi-internal methods that receive input from
// PHP directly. If you know what you're doing, you can
// also call them directly
// Handles an exception with the current stack. Returns the
// output produced by handlers.
Run::handleException(Exception $exception)
#=> string
// Handles an error with the current stack. Errors are
// converted into SPL ErrorException instances
Run::handleError(int $level, string $message, string $file = null, int $line = null)
#=> null
// Hooked as a shutdown handler, captures fatal errors and handles them
// through the current stack:
Run::handleShutdown()
#=> null
// adds a new frame filter callback to the frame filters stack
Run::addFrameFilter()
#=> Whoops\Run
```
## <a name="handler-abstract"></a> `Whoops\Handler\Handler` & `Whoops\Handler\HandlerInterface`
This abstract class contains the base methods for concrete handler implementations. Custom handlers can extend it, or implement the `Whoops\Handler\HandlerInterface` interface.
### Constants
```php
int Handler::DONE // If returned from HandlerInterface::handle, does absolutely nothing.
int Handler::LAST_HANDLER // ...tells whoops to not execute any more handlers after this one.
int Handler::QUIT // ...tells whoops to quit script execution immediately.
```
### Methods
```php
// Custom handlers should expose this method, which will be called once an
// exception needs to be handled. The Handler::* constants can be used to
// signal the underlying logic as to what to do next.
HandlerInterface::handle()
#=> null | int
// Sets the Run instance for this handler
HandlerInterface::setRun(Whoops\Run $run)
#=> null
// Sets the Inspector instance for this handler
HandlerInterface::setInspector(Whoops\Exception\Inspector $inspector)
#=> null
// Sets the Exception for this handler to handle
HandlerInterface::setException(Exception $exception)
#=> null
```
## <a name="inspector"></a> `Whoops\Exception\Inspector`
The `Inspector` class provides methods to inspect an exception instance, with particular focus on its frames/stack-trace.
### Methods
```php
Inspector::__construct(Exception $exception)
#=> null
// Returns the Exception instance being inspected
Inspector::getException()
#=> Exception
// Returns the string name of the Exception being inspected
// A faster way of doing get_class($inspector->getException())
Inspector::getExceptionName()
#=> string
// Returns the string message for the Exception being inspected
// A faster way of doing $inspector->getException()->getMessage()
Inspector::getExceptionMessage()
#=> string
// Returns an iterator instance for all the frames in the stack
// trace for the Exception being inspected.
Inspector::getFrames()
#=> Whoops\Exception\FrameIterator
```
## <a name="frame-collection"></a> `Whoops\Exception\FrameCollection`
The `FrameCollection` class exposes a fluent interface to manipulate and examine a
collection of `Frame` instances.
`FrameCollection` objects are **serializable**.
### Methods
```php
// Returns the number of frames in the collection
// May also be called as count($frameCollection)
FrameCollection::count()
#=> int
// Filter the Frames in the collection with a callable.
// The callable must accept a Frame object, and return
// true to keep it in the collection, or false not to.
FrameCollection::filter(callable $callable)
#=> FrameCollection
// See: array_map
// The callable must accept a Frame object, and return
// a Frame object, doesn't matter if it's the same or not
// - will throw an UnexpectedValueException if something
// else is returned.
FrameCollection::map(callable $callable)
#=> FrameCollection
```
## <a name="frame"></a> `Whoops\Exception\Frame`
The `Frame` class models a single frame in an exception's stack trace. You can use it to retrieve info about things such as frame context, file, line number. Additionally, you have available functionality to add comments to a frame, which is made available to other handlers.
`Frame` objects are **serializable**.
### Methods
```php
// Returns the file path for the file where this frame occurred.
// The optional $shortened argument allows you to retrieve a
// shorter, human-readable file path for display.
Frame::getFile(bool $shortened = false)
#=> string | null (Some frames do not have a file path)
// Returns the line number for this frame
Frame::getLine()
#=> int | null
// Returns the class name for this frame, if it occurred
// within a class/instance.
Frame::getClass()
#=> string | null
// Returns the function name for this frame, if it occurred
// within a function/method
Frame::getFunction()
#=> string | null
// Returns an array of arguments for this frame. Empty if no
// arguments were provided.
Frame::getArgs()
#=> array
// Returns the full file contents for the file where this frame
// occurred.
Frame::getFileContents()
#=> string | null
// Returns an array of lines for a file, optionally scoped to a
// given range of line numbers. i.e: Frame::getFileLines(0, 3)
// returns the first 3 lines after line 0 (1)
Frame::getFileLines(int $start = 0, int $length = null)
#=> array | null
// Adds a comment to this Frame instance. Comments are shared
// with everything that can access the frame instance, obviously,
// so they can be used for a variety of inter-operability purposes.
// The context option can be used to improve comment filtering.
// Additionally, if frames contain URIs, the PrettyPageHandler
// will automagically convert them to clickable anchor elements.
Frame::addComment(string $comment, string $context = 'global')
#=> null
// Returns all comments for this instance optionally filtered by
// a string context identifier.
Frame::getComments(string $filter = null)
#=> array
```
# Core Handlers
## <a name="handler-callback"></a> `Whoops\Handler\CallbackHandler`
The `CallbackHandler` handler wraps regular PHP closures as valid handlers. Useful for quick prototypes or simple handlers. When you pass a closure to `Run::pushHandler`, it's automatically converted to a `CallbackHandler` instance.
```php
<?php
use Whoops\Handler\Handler;
$run->pushHandler(function($exception, $inspector, $run) {
var_dump($exception->getMessage());
return Handler::DONE;
});
$run->popHandler() // #=> Whoops\Handler\CallbackHandler
```
### Methods
```php
// Accepts any valid callable
// For example, a closure, a string function name, an array
// in the format array($class, $method)
CallbackHandler::__construct($callable)
#=> null
CallbackHandler::handle()
#=> int | null
```
## <a name="handler-json"></a> `Whoops\Handler\JsonResponseHandler`
The `JsonResponseHandler`, upon receiving an exception to handle, simply constructs a `JSON` payload, and outputs it. Methods are available to control the detail of the output, and if it should only execute for AJAX requests - paired with another handler under it, such as the `PrettyPageHandler`, it allows you to have meaningful output for both regular and AJAX requests. Neat!
The `JSON` body has the following format:
```json
{
"error": {
"type": "RuntimeException",
"message": "Something broke!",
"file": "/var/project/foo/bar.php",
"line": 22,
# if JsonResponseHandler::addTraceToOutput(true):
"trace": [
{ "file": "/var/project/foo/index.php",
"line": 157,
"function": "handleStuffs",
"class": "MyApplication\DoerOfThings",
"args": [ true, 10, "yay method arguments" ] },
# ... more frames here ...
]
}
}
```
### Methods
```php
// Should detailed stack trace output also be added to the
// JSON payload body?
JsonResponseHandler::addTraceToOutput(bool $yes = null)
#=> bool
JsonResponseHandler::handle()
#=> int | null
```
## <a name="handler-pretty"></a> `Whoops\Handler\PrettyPageHandler`
The `PrettyPageHandler` generates a fancy, detailed error page which includes code views for all frames in the stack trace, environment details, etc. Super neat. It produces a bundled response string that does not require any further HTTP requests, so it's fit to work on pretty much any environment and framework that speaks back to a browser, without you having to explicitly hook it up to your framework/project's routing mechanisms.
### Methods
```php
// Adds a key=>value table of arbitrary data, labeled by $label, to
// the output. Useful where you want to display contextual data along
// with the error, about your application or project.
PrettyPageHandler::addDataTable(string $label, array $data)
#=> null
// Similar to PrettyPageHandler::addDataTable, but accepts a callable
// that will be called only when rendering an exception. This allows
// you to gather additional data that may not be available very early
// in the process.
PrettyPageHandler::addDataTableCallback(string $label, callable $callback)
#=> null
// Returns all data tables registered with this handler. Optionally
// accepts a string label, and will only return the data under that
// label.
PrettyPageHandler::getDataTables(string $label = null)
#=> array | array[]
// Sets the title for the error page
PrettyPageHandler::setPageTitle(string $title)
#=> null
// Returns the title for the error page
PrettyPageHandler::getPageTitle()
#=> string
// Returns a list of string paths where resources
// used by this handler are searched for - the template and CSS
// files.
PrettyPageHandler::getResourcesPaths()
#=> array
// Adds a string path to the location of resources for the
// handler. Useful if you want to roll your own template
// file (pretty-template.php and pretty-page.css) while
// still using the logic this handler provides
PrettyPageHandler::addResourcePath(string $resourcesPath)
#=> null
// Sets an editor to use to open referenced files, either by
// a string identifier, or as an arbitrary callable that returns
// a string or an array that can be used as an href attribute.
// Available built-in editors can be found here: https://github.com/filp/whoops/blob/master/docs/Open Files In An Editor.md
PrettyPageHandler::setEditor(string $editor)
PrettyPageHandler::setEditor(function ($file, $line) { return string })
// Additionally you may want that the link acts as an ajax request (e.g. Intellij platform)
PrettyPageHandler::setEditor(function ($file, $line) {
return array(
'url' => "http://localhost:63342/api/file/?file=$file&line=$line",
'ajax' => true
);
}
)
#=> null
// Similar to PrettyPageHandler::setEditor, but allows you
// to name your custom editor, thus sharing it with the
// rest of the application. Useful if, for example, you integrate
// Whoops into your framework or library, and want to share
// support for extra editors with the end-user.
//
// $resolver may be a callable, like with ::setEditor, or a string
// with placeholders %file and %line.
// For example:
// $handler->addEditor('whatevs', 'whatevs://open?file=file://%file&line=%line')
PrettyPageHandler::addEditor(string $editor, $resolver)
#=> null
PrettyPageHandler::handle()
#=> int | null
```
# Core Functions:
## <a name="fn-ajax"></a> `Whoops\Util\Misc::isAjaxRequest()`
#=> boolean
```php
// Use a certain handler only in AJAX triggered requests
if (Whoops\Util\Misc::isAjaxRequest()){
$run->addHandler($myHandler);
}
```
## <a name="fn-cli"></a> `Whoops\Util\Misc::isCommandLine()`
#=> boolean
```php
// Use a certain handler only in php cli
if (Whoops\Util\Misc::isCommandLine()){
$run->addHandler($myHandler);
}
```
```php
/*
Output the error message only if using command line.
else, output to logger if available.
Allow to safely add this handler to web pages.
*/
$plainTextHandler = new PlainTextHandler();
if (!Whoops\isCommandLine()){
$plainTextHandler->loggerOnly(true);
}
$run->addHandler($myHandler);
```
================================================
FILE: docs/Framework Integration.md
================================================
# Contributing an integration with a framework
Lately we're preferring to keep integration libraries out of the Whoops core.
If possible, consider managing an official Whoops-SomeFramework integration.
The procedure is not hard at all.
1. Keep your integration classes and instructions in a repository of your own;
2. Create a `composer.json` file in your repository with contents similar to the following:
```
{
"name": "username/whoops-someframework",
"description": "Integrates the Whoops library into SomeFramework",
"require": {
"filp/whoops": "1.*"
}
}
```
3. [Register it with Packagist](https://packagist.org/packages/submit).
Once that is done, please create an issue and we will add a link to it in our README.
SomeFramework users then would write this in their `composer.json`:
```
"require": {
"username/whoops-someframework": "*"
}
```
This would also install Whoops and you'd be able to release updates to your package as quickly as you wish them to.
================================================
FILE: docs/Open Files In An Editor.md
================================================
# Open Files In An Editor
When using the pretty error page feature, whoops comes with the ability to
open referenced files directly in your IDE or editor.
This feature only works in case your php-source files are locally accessible to the machine on which the editor is installed.
```php
<?php
use Whoops\Handler\PrettyPageHandler;
$handler = new PrettyPageHandler;
$handler->setEditor('sublime');
```
The following editors are currently supported by default.
- `emacs` - Emacs
- `idea` - IDEA
- `macvim` - MacVim
- `phpstorm` - PhpStorm (on Linux you might need to manually install a [handler](https://github.com/sanduhrs/phpstorm-url-handler))
- `sublime` - Sublime Text 2 and possibly 3 (on OS X you might need [a special handler](https://github.com/inopinatus/sublime_url))
- `textmate` - Textmate
- `xdebug` - xdebug (uses [xdebug.file_link_format](http://xdebug.org/docs/all_settings#file_link_format))
- `vscode` - VSCode (ref [Opening VS Code with URLs](https://code.visualstudio.com/docs/editor/command-line#_opening-vs-code-with-urls))
- `atom` - Atom (ref [Add core URI handlers](https://github.com/atom/atom/pull/15935))
- `espresso` - Espresso
- `netbeans` - Netbeans (ref [xdebug.file_link_format](http://xdebug.org/docs/all_settings#file_link_format))
Adding your own editor is simple:
```php
$handler->setEditor(function($file, $line) {
return "whatever://open?file=$file&line=$line";
});
```
You can add [IntelliJ Platform](https://github.com/pinepain/PhpStormOpener#phpstormopener) support like this:
```php
$handler->setEditor(
function ($file, $line) {
// if your development server is not local it's good to map remote files to local
$translations = array('^' . __DIR__ => '~/Development/PhpStormOpener'); // change to your path
foreach ($translations as $from => $to) {
$file = preg_replace('#' . $from . '#', $to, $file, 1);
}
// IntelliJ platform requires that you send an Ajax request, else the browser will quit the page
return array(
'url' => "http://localhost:63342/api/file/?file=$file&line=$line",
'ajax' => true
);
}
);
```
================================================
FILE: docs/Replay Errors.md
================================================
# Replay Errors
You can replay Errors that happened in production if you serialize them and store them to later replay in a development Whoops:
```php
$serialized_exception = serialize(new Exception('Something has happened!'));
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
$whoops->handleException(unserialize($serialized_exception));
```
https://github.com/filp/whoops/issues/623
================================================
FILE: examples/example-ajax-only.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*
* Run this example file with the PHP 5.4 web server with:
*
* $ cd project_dir
* $ php -S localhost:8080
*
* and access localhost:8080/examples/example-ajax-only.php through your browser
*
* Or just run it through apache/nginx/what-have-yous as usual.
*/
namespace Whoops\Example;
use RuntimeException;
use Whoops\Handler\JsonResponseHandler;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Run;
require __DIR__ . '/../vendor/autoload.php';
$run = new Run();
// We want the error page to be shown by default, if this is a
// regular request, so that's the first thing to go into the stack:
$run->pushHandler(new PrettyPageHandler());
// Now, we want a second handler that will run before the error page,
// and immediately return an error message in JSON format, if something
// goes awry.
if (\Whoops\Util\Misc::isAjaxRequest()) {
$jsonHandler = new JsonResponseHandler();
// You can also tell JsonResponseHandler to give you a full stack trace:
// $jsonHandler->addTraceToOutput(true);
// You can also return a result compliant to the json:api spec
// re: http://jsonapi.org/examples/#error-objects
// tl;dr: error[] becomes errors[[]]
$jsonHandler->setJsonApi(true);
// And push it into the stack:
$run->pushHandler($jsonHandler);
}
// That's it! Register Whoops and throw a dummy exception:
$run->register();
throw new RuntimeException("Oh fudge napkins!");
================================================
FILE: examples/example.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*
* Run this example file with the PHP 5.4 web server with:
*
* $ cd project_dir
* $ php -S localhost:8080
*
* and access localhost:8080/examples/example.php through your browser
*
* Or just run it through apache/nginx/what-have-yous as usual.
*/
namespace Whoops\Example;
use Exception as BaseException;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Run;
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/lib.php';
class Exception extends BaseException
{
}
$run = new Run();
$handler = new PrettyPageHandler();
// Add a custom table to the layout:
$handler->addDataTable('Ice-cream I like', [
'Chocolate' => 'yes',
'Coffee & chocolate' => 'a lot',
'Strawberry & chocolate' => 'it\'s alright',
'Vanilla' => 'ew',
]);
$handler->setApplicationPaths([__FILE__]);
$handler->addDataTableCallback('Details', function(\Whoops\Exception\Inspector $inspector) {
$data = array();
$exception = $inspector->getException();
if ($exception instanceof SomeSpecificException) {
$data['Important exception data'] = $exception->getSomeSpecificData();
}
$data['Exception class'] = get_class($exception);
$data['Exception code'] = $exception->getCode();
return $data;
});
$run->pushHandler($handler);
// Example: tag all frames inside a function with their function name
$run->pushHandler(function ($exception, $inspector, $run) {
$inspector->getFrames()->map(function ($frame) {
if ($function = $frame->getFunction()) {
$frame->addComment("This frame is within function '$function'", 'cpt-obvious');
}
return $frame;
});
});
$run->register();
function fooBar()
{
throw new Exception("Something broke!");
}
function bar()
{
whoops_add_stack_frame(function(){
fooBar();
});
}
bar();
================================================
FILE: examples/lib.php
================================================
<?php
function whoops_add_stack_frame($callback){
$callback();
}
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="true" beStrictAboutTestsThatDoNotTestAnything="false" bootstrap="tests/bootstrap.php" cacheResult="false" colors="true">
<testsuites>
<testsuite name="Whoops Tests Suite">
<directory>tests/Whoops/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src/Whoops/</directory>
<exclude>
<directory>./docs</directory>
<directory>./examples</directory>
<directory>./tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
================================================
FILE: src/Whoops/Exception/ErrorException.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use ErrorException as BaseErrorException;
/**
* Wraps ErrorException; mostly used for typing (at least now)
* to easily cleanup the stack trace of redundant info.
*/
class ErrorException extends BaseErrorException
{
}
================================================
FILE: src/Whoops/Exception/Formatter.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Inspector\InspectorInterface;
class Formatter
{
/**
* Returns all basic information about the exception in a simple array
* for further convertion to other languages
* @param InspectorInterface $inspector
* @param bool $shouldAddTrace
* @param array<callable> $frameFilters
* @return array
*/
public static function formatExceptionAsDataArray(InspectorInterface $inspector, $shouldAddTrace, array $frameFilters = [])
{
$exception = $inspector->getException();
$response = [
'type' => get_class($exception),
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
];
if ($shouldAddTrace) {
$frames = $inspector->getFrames($frameFilters);
$frameData = [];
foreach ($frames as $frame) {
/** @var Frame $frame */
$frameData[] = [
'file' => $frame->getFile(),
'line' => $frame->getLine(),
'function' => $frame->getFunction(),
'class' => $frame->getClass(),
'args' => $frame->getArgs(),
];
}
$response['trace'] = $frameData;
}
return $response;
}
public static function formatExceptionPlain(InspectorInterface $inspector)
{
$message = $inspector->getException()->getMessage();
$frames = $inspector->getFrames();
$plain = $inspector->getExceptionName();
$plain .= ' thrown with message "';
$plain .= $message;
$plain .= '"'."\n\n";
$plain .= "Stacktrace:\n";
foreach ($frames as $i => $frame) {
$plain .= "#". (count($frames) - $i - 1). " ";
$plain .= $frame->getClass() ?: '';
$plain .= $frame->getClass() && $frame->getFunction() ? ":" : "";
$plain .= $frame->getFunction() ?: '';
$plain .= ' in ';
$plain .= ($frame->getFile() ?: '<#unknown>');
$plain .= ':';
$plain .= (int) $frame->getLine(). "\n";
}
return $plain;
}
}
================================================
FILE: src/Whoops/Exception/Frame.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use InvalidArgumentException;
use Serializable;
class Frame implements Serializable
{
/**
* @var array
*/
protected $frame;
/**
* @var string
*/
protected $fileContentsCache;
/**
* @var array[]
*/
protected $comments = [];
/**
* @var bool
*/
protected $application;
public function __construct(array $frame)
{
$this->frame = $frame;
}
/**
* @param bool $shortened
* @return string|null
*/
public function getFile($shortened = false)
{
if (empty($this->frame['file'])) {
return null;
}
$file = $this->frame['file'];
// Check if this frame occurred within an eval().
// @todo: This can be made more reliable by checking if we've entered
// eval() in a previous trace, but will need some more work on the upper
// trace collector(s).
if (preg_match('/^(.*)\((\d+)\) : (?:eval\(\)\'d|assert) code$/', $file, $matches)) {
$file = $this->frame['file'] = $matches[1];
$this->frame['line'] = (int) $matches[2];
}
if ($shortened && is_string($file)) {
// Replace the part of the path that all frames have in common, and add 'soft hyphens' for smoother line-breaks.
$dirname = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
if ($dirname !== '/') {
$file = str_replace($dirname, "…", $file);
}
$file = str_replace("/", "/­", $file);
}
return $file;
}
/**
* @return int|null
*/
public function getLine()
{
return isset($this->frame['line']) ? $this->frame['line'] : null;
}
/**
* @return string|null
*/
public function getClass()
{
return isset($this->frame['class']) ? $this->frame['class'] : null;
}
/**
* @return string|null
*/
public function getFunction()
{
return isset($this->frame['function']) ? $this->frame['function'] : null;
}
/**
* @return array
*/
public function getArgs()
{
return isset($this->frame['args']) ? (array) $this->frame['args'] : [];
}
/**
* Returns the full contents of the file for this frame,
* if it's known.
* @return string|null
*/
public function getFileContents()
{
if ($this->fileContentsCache === null && $filePath = $this->getFile()) {
// Leave the stage early when 'Unknown' or '[internal]' is passed
// this would otherwise raise an exception when
// open_basedir is enabled.
if ($filePath === "Unknown" || $filePath === '[internal]') {
return null;
}
try {
$this->fileContentsCache = file_get_contents($filePath);
} catch (ErrorException $exception) {
// Internal file paths of PHP extensions cannot be opened
}
}
return $this->fileContentsCache;
}
/**
* Adds a comment to this frame, that can be received and
* used by other handlers. For example, the PrettyPage handler
* can attach these comments under the code for each frame.
*
* An interesting use for this would be, for example, code analysis
* & annotations.
*
* @param string $comment
* @param string $context Optional string identifying the origin of the comment
*/
public function addComment($comment, $context = 'global')
{
$this->comments[] = [
'comment' => $comment,
'context' => $context,
];
}
/**
* Returns all comments for this frame. Optionally allows
* a filter to only retrieve comments from a specific
* context.
*
* @param string $filter
* @return array[]
*/
public function getComments($filter = null)
{
$comments = $this->comments;
if ($filter !== null) {
$comments = array_filter($comments, function ($c) use ($filter) {
return $c['context'] == $filter;
});
}
return $comments;
}
/**
* Returns the array containing the raw frame data from which
* this Frame object was built
*
* @return array
*/
public function getRawFrame()
{
return $this->frame;
}
/**
* Returns the contents of the file for this frame as an
* array of lines, and optionally as a clamped range of lines.
*
* NOTE: lines are 0-indexed
*
* @example
* Get all lines for this file
* $frame->getFileLines(); // => array( 0 => '<?php', 1 => '...', ...)
* @example
* Get one line for this file, starting at line 10 (zero-indexed, remember!)
* $frame->getFileLines(9, 1); // array( 9 => '...' )
*
* @throws InvalidArgumentException if $length is less than or equal to 0
* @param int $start
* @param int $length
* @return string[]|null
*/
public function getFileLines($start = 0, $length = null)
{
if (null !== ($contents = $this->getFileContents())) {
$lines = explode("\n", $contents);
// Get a subset of lines from $start to $end
if ($length !== null) {
$start = (int) $start;
$length = (int) $length;
if ($start < 0) {
$start = 0;
}
if ($length <= 0) {
throw new InvalidArgumentException(
"\$length($length) cannot be lower or equal to 0"
);
}
$lines = array_slice($lines, $start, $length, true);
}
return $lines;
}
}
/**
* Implements the Serializable interface, with special
* steps to also save the existing comments.
*
* @see Serializable::serialize
* @return string
*/
public function serialize()
{
$frame = $this->frame;
if (!empty($this->comments)) {
$frame['_comments'] = $this->comments;
}
return serialize($frame);
}
public function __serialize()
{
$frame = $this->frame;
if (!empty($this->comments)) {
$frame['_comments'] = $this->comments;
}
return $frame;
}
/**
* Unserializes the frame data, while also preserving
* any existing comment data.
*
* @see Serializable::unserialize
* @param string $serializedFrame
*/
public function unserialize($serializedFrame)
{
$frame = unserialize($serializedFrame);
if (!empty($frame['_comments'])) {
$this->comments = $frame['_comments'];
unset($frame['_comments']);
}
$this->frame = $frame;
}
public function __unserialize($frame)
{
if (!empty($frame['_comments'])) {
$this->comments = $frame['_comments'];
unset($frame['_comments']);
}
$this->frame = $frame;
}
/**
* Compares Frame against one another
* @param Frame $frame
* @return bool
*/
public function equals(Frame $frame)
{
if (!$this->getFile() || $this->getFile() === 'Unknown' || !$this->getLine()) {
return false;
}
return $frame->getFile() === $this->getFile() && $frame->getLine() === $this->getLine();
}
/**
* Returns whether this frame belongs to the application or not.
*
* @return boolean
*/
public function isApplication()
{
return $this->application;
}
/**
* Mark as an frame belonging to the application.
*
* @param boolean $application
*/
public function setApplication($application)
{
$this->application = $application;
}
}
================================================
FILE: src/Whoops/Exception/FrameCollection.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use ReturnTypeWillChange;
use Serializable;
use UnexpectedValueException;
/**
* Exposes a fluent interface for dealing with an ordered list
* of stack-trace frames.
*/
class FrameCollection implements ArrayAccess, IteratorAggregate, Serializable, Countable
{
/**
* @var array[]
*/
private $frames;
public function __construct(array $frames)
{
$this->frames = array_map(function ($frame) {
return new Frame($frame);
}, $frames);
}
/**
* Filters frames using a callable, returns the same FrameCollection
*
* @param callable $callable
* @return FrameCollection
*/
public function filter($callable)
{
$this->frames = array_values(array_filter($this->frames, $callable));
return $this;
}
/**
* Map the collection of frames
*
* @param callable $callable
* @return FrameCollection
*/
public function map($callable)
{
// Contain the map within a higher-order callable
// that enforces type-correctness for the $callable
$this->frames = array_map(function ($frame) use ($callable) {
$frame = call_user_func($callable, $frame);
if (!$frame instanceof Frame) {
throw new UnexpectedValueException(
"Callable to " . __CLASS__ . "::map must return a Frame object"
);
}
return $frame;
}, $this->frames);
return $this;
}
/**
* Returns an array with all frames, does not affect
* the internal array.
*
* @todo If this gets any more complex than this,
* have getIterator use this method.
* @see FrameCollection::getIterator
* @return array
*/
public function getArray()
{
return $this->frames;
}
/**
* @see IteratorAggregate::getIterator
* @return ArrayIterator
*/
#[ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->frames);
}
/**
* @see ArrayAccess::offsetExists
* @param int $offset
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->frames[$offset]);
}
/**
* @see ArrayAccess::offsetGet
* @param int $offset
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->frames[$offset];
}
/**
* @see ArrayAccess::offsetSet
* @param int $offset
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
throw new \Exception(__CLASS__ . ' is read only');
}
/**
* @see ArrayAccess::offsetUnset
* @param int $offset
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
throw new \Exception(__CLASS__ . ' is read only');
}
/**
* @see Countable::count
* @return int
*/
#[ReturnTypeWillChange]
public function count()
{
return count($this->frames);
}
/**
* Count the frames that belongs to the application.
*
* @return int
*/
public function countIsApplication()
{
return count(array_filter($this->frames, function (Frame $f) {
return $f->isApplication();
}));
}
/**
* @see Serializable::serialize
* @return string
*/
#[ReturnTypeWillChange]
public function serialize()
{
return serialize($this->frames);
}
/**
* @see Serializable::unserialize
* @param string $serializedFrames
*/
#[ReturnTypeWillChange]
public function unserialize($serializedFrames)
{
$this->frames = unserialize($serializedFrames);
}
public function __serialize()
{
return $this->frames;
}
public function __unserialize(array $serializedFrames)
{
$this->frames = $serializedFrames;
}
/**
* @param Frame[] $frames Array of Frame instances, usually from $e->getPrevious()
*/
public function prependFrames(array $frames)
{
$this->frames = array_merge($frames, $this->frames);
}
/**
* Gets the innermost part of stack trace that is not the same as that of outer exception
*
* @param FrameCollection $parentFrames Outer exception frames to compare tail against
* @return Frame[]
*/
public function topDiff(FrameCollection $parentFrames)
{
$diff = $this->frames;
$parentFrames = $parentFrames->getArray();
$p = count($parentFrames)-1;
for ($i = count($diff)-1; $i >= 0 && $p >= 0; $i--) {
/** @var Frame $tailFrame */
$tailFrame = $diff[$i];
if ($tailFrame->equals($parentFrames[$p])) {
unset($diff[$i]);
}
$p--;
}
return $diff;
}
}
================================================
FILE: src/Whoops/Exception/Inspector.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Inspector\InspectorFactory;
use Whoops\Inspector\InspectorInterface;
use Whoops\Util\Misc;
class Inspector implements InspectorInterface
{
/**
* @var \Throwable
*/
private $exception;
/**
* @var \Whoops\Exception\FrameCollection
*/
private $frames;
/**
* @var \Whoops\Exception\Inspector
*/
private $previousExceptionInspector;
/**
* @var \Throwable[]
*/
private $previousExceptions;
/**
* @var \Whoops\Inspector\InspectorFactoryInterface|null
*/
protected $inspectorFactory;
/**
* @param \Throwable $exception The exception to inspect
* @param \Whoops\Inspector\InspectorFactoryInterface $factory
*/
public function __construct($exception, $factory = null)
{
$this->exception = $exception;
$this->inspectorFactory = $factory ?: new InspectorFactory();
}
/**
* @return \Throwable
*/
public function getException()
{
return $this->exception;
}
/**
* @return string
*/
public function getExceptionName()
{
return get_class($this->exception);
}
/**
* @return string
*/
public function getExceptionMessage()
{
return $this->extractDocrefUrl($this->exception->getMessage())['message'];
}
/**
* @return string[]
*/
public function getPreviousExceptionMessages()
{
return array_map(function ($prev) {
/** @var \Throwable $prev */
return $this->extractDocrefUrl($prev->getMessage())['message'];
}, $this->getPreviousExceptions());
}
/**
* @return int[]
*/
public function getPreviousExceptionCodes()
{
return array_map(function ($prev) {
/** @var \Throwable $prev */
return $prev->getCode();
}, $this->getPreviousExceptions());
}
/**
* Returns a url to the php-manual related to the underlying error - when available.
*
* @return string|null
*/
public function getExceptionDocrefUrl()
{
return $this->extractDocrefUrl($this->exception->getMessage())['url'];
}
private function extractDocrefUrl($message)
{
$docref = [
'message' => $message,
'url' => null,
];
// php embbeds urls to the manual into the Exception message with the following ini-settings defined
// http://php.net/manual/en/errorfunc.configuration.php#ini.docref-root
if (!ini_get('html_errors') || !ini_get('docref_root')) {
return $docref;
}
$pattern = "/\[<a href='([^']+)'>(?:[^<]+)<\/a>\]/";
if (preg_match($pattern, $message, $matches)) {
// -> strip those automatically generated links from the exception message
$docref['message'] = preg_replace($pattern, '', $message, 1);
$docref['url'] = $matches[1];
}
return $docref;
}
/**
* Does the wrapped Exception has a previous Exception?
* @return bool
*/
public function hasPreviousException()
{
return $this->previousExceptionInspector || $this->exception->getPrevious();
}
/**
* Returns an Inspector for a previous Exception, if any.
* @todo Clean this up a bit, cache stuff a bit better.
* @return Inspector
*/
public function getPreviousExceptionInspector()
{
if ($this->previousExceptionInspector === null) {
$previousException = $this->exception->getPrevious();
if ($previousException) {
$this->previousExceptionInspector = $this->inspectorFactory->create($previousException);
}
}
return $this->previousExceptionInspector;
}
/**
* Returns an array of all previous exceptions for this inspector's exception
* @return \Throwable[]
*/
public function getPreviousExceptions()
{
if ($this->previousExceptions === null) {
$this->previousExceptions = [];
$prev = $this->exception->getPrevious();
while ($prev !== null) {
$this->previousExceptions[] = $prev;
$prev = $prev->getPrevious();
}
}
return $this->previousExceptions;
}
/**
* Returns an iterator for the inspected exception's
* frames.
*
* @param array<callable> $frameFilters
*
* @return \Whoops\Exception\FrameCollection
*/
public function getFrames(array $frameFilters = [])
{
if ($this->frames === null) {
$frames = $this->getTrace($this->exception);
// Fill empty line/file info for call_user_func_array usages (PHP Bug #44428)
foreach ($frames as $k => $frame) {
if (empty($frame['file'])) {
// Default values when file and line are missing
$file = '[internal]';
$line = 0;
$next_frame = !empty($frames[$k + 1]) ? $frames[$k + 1] : [];
if ($this->isValidNextFrame($next_frame)) {
$file = $next_frame['file'];
$line = $next_frame['line'];
}
$frames[$k]['file'] = $file;
$frames[$k]['line'] = $line;
}
}
// Find latest non-error handling frame index ($i) used to remove error handling frames
$i = 0;
foreach ($frames as $k => $frame) {
if ($frame['file'] == $this->exception->getFile() && $frame['line'] == $this->exception->getLine()) {
$i = $k;
}
}
// Remove error handling frames
if ($i > 0) {
array_splice($frames, 0, $i);
}
$firstFrame = $this->getFrameFromException($this->exception);
array_unshift($frames, $firstFrame);
$this->frames = new FrameCollection($frames);
if ($previousInspector = $this->getPreviousExceptionInspector()) {
// Keep outer frame on top of the inner one
$outerFrames = $this->frames;
$newFrames = clone $previousInspector->getFrames();
// I assume it will always be set, but let's be safe
if (isset($newFrames[0])) {
$newFrames[0]->addComment(
$previousInspector->getExceptionMessage(),
'Exception message:'
);
}
$newFrames->prependFrames($outerFrames->topDiff($newFrames));
$this->frames = $newFrames;
}
// Apply frame filters callbacks on the frames stack
if (!empty($frameFilters)) {
foreach ($frameFilters as $filterCallback) {
$this->frames->filter($filterCallback);
}
}
}
return $this->frames;
}
/**
* Gets the backtrace from an exception.
*
* If xdebug is installed
*
* @param \Throwable $e
* @return array
*/
protected function getTrace($e)
{
$traces = $e->getTrace();
// Get trace from xdebug if enabled, failure exceptions only trace to the shutdown handler by default
if (!$e instanceof \ErrorException) {
return $traces;
}
if (!Misc::isLevelFatal($e->getSeverity())) {
return $traces;
}
if (!extension_loaded('xdebug') || !function_exists('xdebug_is_enabled') || !xdebug_is_enabled()) {
return $traces;
}
// Use xdebug to get the full stack trace and remove the shutdown handler stack trace
$stack = array_reverse(xdebug_get_function_stack());
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$traces = array_diff_key($stack, $trace);
return $traces;
}
/**
* Given an exception, generates an array in the format
* generated by Exception::getTrace()
* @param \Throwable $exception
* @return array
*/
protected function getFrameFromException($exception)
{
return [
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'class' => get_class($exception),
'args' => [
$exception->getMessage(),
],
];
}
/**
* Given an error, generates an array in the format
* generated by ErrorException
* @param ErrorException $exception
* @return array
*/
protected function getFrameFromError(ErrorException $exception)
{
return [
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'class' => null,
'args' => [],
];
}
/**
* Determine if the frame can be used to fill in previous frame's missing info
* happens for call_user_func and call_user_func_array usages (PHP Bug #44428)
*
* @return bool
*/
protected function isValidNextFrame(array $frame)
{
if (empty($frame['file'])) {
return false;
}
if (empty($frame['line'])) {
return false;
}
if (empty($frame['function']) || !stristr($frame['function'], 'call_user_func')) {
return false;
}
return true;
}
}
================================================
FILE: src/Whoops/Handler/CallbackHandler.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
/**
* Wrapper for Closures passed as handlers. Can be used
* directly, or will be instantiated automagically by Whoops\Run
* if passed to Run::pushHandler
*/
class CallbackHandler extends Handler
{
/**
* @var callable
*/
protected $callable;
/**
* @throws InvalidArgumentException If argument is not callable
* @param callable $callable
*/
public function __construct($callable)
{
if (!is_callable($callable)) {
throw new InvalidArgumentException(
'Argument to ' . __METHOD__ . ' must be valid callable'
);
}
$this->callable = $callable;
}
/**
* @return int|null
*/
public function handle()
{
$exception = $this->getException();
$inspector = $this->getInspector();
$run = $this->getRun();
$callable = $this->callable;
// invoke the callable directly, to get simpler stacktraces (in comparison to call_user_func).
// this assumes that $callable is a properly typed php-callable, which we check in __construct().
return $callable($exception, $inspector, $run);
}
}
================================================
FILE: src/Whoops/Handler/Handler.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Inspector\InspectorInterface;
use Whoops\RunInterface;
/**
* Abstract implementation of a Handler.
*/
abstract class Handler implements HandlerInterface
{
/*
Return constants that can be returned from Handler::handle
to message the handler walker.
*/
const DONE = 0x10; // returning this is optional, only exists for
// semantic purposes
/**
* The Handler has handled the Throwable in some way, and wishes to skip any other Handler.
* Execution will continue.
*/
const LAST_HANDLER = 0x20;
/**
* The Handler has handled the Throwable in some way, and wishes to quit/stop execution
*/
const QUIT = 0x30;
/**
* @var RunInterface
*/
private $run;
/**
* @var InspectorInterface $inspector
*/
private $inspector;
/**
* @var \Throwable $exception
*/
private $exception;
/**
* @param RunInterface $run
*/
public function setRun(RunInterface $run)
{
$this->run = $run;
}
/**
* @return RunInterface
*/
protected function getRun()
{
return $this->run;
}
/**
* @param InspectorInterface $inspector
*/
public function setInspector(InspectorInterface $inspector)
{
$this->inspector = $inspector;
}
/**
* @return InspectorInterface
*/
protected function getInspector()
{
return $this->inspector;
}
/**
* @param \Throwable $exception
*/
public function setException($exception)
{
$this->exception = $exception;
}
/**
* @return \Throwable
*/
protected function getException()
{
return $this->exception;
}
}
================================================
FILE: src/Whoops/Handler/HandlerInterface.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Inspector\InspectorInterface;
use Whoops\RunInterface;
interface HandlerInterface
{
/**
* @return int|null A handler may return nothing, or a Handler::HANDLE_* constant
*/
public function handle();
/**
* @param RunInterface $run
* @return void
*/
public function setRun(RunInterface $run);
/**
* @param \Throwable $exception
* @return void
*/
public function setException($exception);
/**
* @param InspectorInterface $inspector
* @return void
*/
public function setInspector(InspectorInterface $inspector);
}
================================================
FILE: src/Whoops/Handler/JsonResponseHandler.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Exception\Formatter;
/**
* Catches an exception and converts it to a JSON
* response. Additionally can also return exception
* frames for consumption by an API.
*/
class JsonResponseHandler extends Handler
{
/**
* @var bool
*/
private $returnFrames = false;
/**
* @var bool
*/
private $jsonApi = false;
/**
* Returns errors[[]] instead of error[] to be in compliance with the json:api spec
* @param bool $jsonApi Default is false
* @return static
*/
public function setJsonApi($jsonApi = false)
{
$this->jsonApi = (bool) $jsonApi;
return $this;
}
/**
* @param bool|null $returnFrames
* @return bool|static
*/
public function addTraceToOutput($returnFrames = null)
{
if (func_num_args() == 0) {
return $this->returnFrames;
}
$this->returnFrames = (bool) $returnFrames;
return $this;
}
/**
* @return int
*/
public function handle()
{
if ($this->jsonApi === true) {
$response = [
'errors' => [
Formatter::formatExceptionAsDataArray(
$this->getInspector(),
$this->addTraceToOutput(),
$this->getRun()->getFrameFilters()
),
]
];
} else {
$response = [
'error' => Formatter::formatExceptionAsDataArray(
$this->getInspector(),
$this->addTraceToOutput(),
$this->getRun()->getFrameFilters()
),
];
}
echo json_encode($response, defined('JSON_PARTIAL_OUTPUT_ON_ERROR') ? JSON_PARTIAL_OUTPUT_ON_ERROR : 0);
return Handler::QUIT;
}
/**
* @return string
*/
public function contentType()
{
return 'application/json';
}
}
================================================
FILE: src/Whoops/Handler/PlainTextHandler.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
* Plaintext handler for command line and logs.
* @author Pierre-Yves Landuré <https://howto.biapy.com/>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Whoops\Exception\Frame;
/**
* Handler outputing plaintext error messages. Can be used
* directly, or will be instantiated automagically by Whoops\Run
* if passed to Run::pushHandler
*/
class PlainTextHandler extends Handler
{
const VAR_DUMP_PREFIX = ' | ';
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var callable
*/
protected $dumper;
/**
* @var bool
*/
private $addTraceToOutput = true;
/**
* @var bool|integer
*/
private $addTraceFunctionArgsToOutput = false;
/**
* @var integer
*/
private $traceFunctionArgsOutputLimit = 1024;
/**
* @var bool
*/
private $addPreviousToOutput = true;
/**
* @var bool
*/
private $loggerOnly = false;
/**
* Constructor.
* @throws InvalidArgumentException If argument is not null or a LoggerInterface
* @param \Psr\Log\LoggerInterface|null $logger
*/
public function __construct($logger = null)
{
$this->setLogger($logger);
}
/**
* Set the output logger interface.
* @throws InvalidArgumentException If argument is not null or a LoggerInterface
* @param \Psr\Log\LoggerInterface|null $logger
*/
public function setLogger($logger = null)
{
if (! (is_null($logger)
|| $logger instanceof LoggerInterface)) {
throw new InvalidArgumentException(
'Argument to ' . __METHOD__ .
" must be a valid Logger Interface (aka. Monolog), " .
get_class($logger) . ' given.'
);
}
$this->logger = $logger;
}
/**
* @return \Psr\Log\LoggerInterface|null
*/
public function getLogger()
{
return $this->logger;
}
/**
* Set var dumper callback function.
*
* @param callable $dumper
* @return static
*/
public function setDumper(callable $dumper)
{
$this->dumper = $dumper;
return $this;
}
/**
* Add error trace to output.
* @param bool|null $addTraceToOutput
* @return bool|static
*/
public function addTraceToOutput($addTraceToOutput = null)
{
if (func_num_args() == 0) {
return $this->addTraceToOutput;
}
$this->addTraceToOutput = (bool) $addTraceToOutput;
return $this;
}
/**
* Add previous exceptions to output.
* @param bool|null $addPreviousToOutput
* @return bool|static
*/
public function addPreviousToOutput($addPreviousToOutput = null)
{
if (func_num_args() == 0) {
return $this->addPreviousToOutput;
}
$this->addPreviousToOutput = (bool) $addPreviousToOutput;
return $this;
}
/**
* Add error trace function arguments to output.
* Set to True for all frame args, or integer for the n first frame args.
* @param bool|integer|null $addTraceFunctionArgsToOutput
* @return static|bool|integer
*/
public function addTraceFunctionArgsToOutput($addTraceFunctionArgsToOutput = null)
{
if (func_num_args() == 0) {
return $this->addTraceFunctionArgsToOutput;
}
if (! is_integer($addTraceFunctionArgsToOutput)) {
$this->addTraceFunctionArgsToOutput = (bool) $addTraceFunctionArgsToOutput;
} else {
$this->addTraceFunctionArgsToOutput = $addTraceFunctionArgsToOutput;
}
return $this;
}
/**
* Set the size limit in bytes of frame arguments var_dump output.
* If the limit is reached, the var_dump output is discarded.
* Prevent memory limit errors.
* @param int $traceFunctionArgsOutputLimit
* @return static
*/
public function setTraceFunctionArgsOutputLimit($traceFunctionArgsOutputLimit)
{
$this->traceFunctionArgsOutputLimit = (int) $traceFunctionArgsOutputLimit;
return $this;
}
/**
* Create plain text response and return it as a string
* @return string
*/
public function generateResponse()
{
$exception = $this->getException();
$message = $this->getExceptionOutput($exception);
if ($this->addPreviousToOutput) {
$previous = $exception->getPrevious();
while ($previous) {
$message .= "\n\nCaused by\n" . $this->getExceptionOutput($previous);
$previous = $previous->getPrevious();
}
}
return $message . $this->getTraceOutput() . "\n";
}
/**
* Get the size limit in bytes of frame arguments var_dump output.
* If the limit is reached, the var_dump output is discarded.
* Prevent memory limit errors.
* @return integer
*/
public function getTraceFunctionArgsOutputLimit()
{
return $this->traceFunctionArgsOutputLimit;
}
/**
* Only output to logger.
* @param bool|null $loggerOnly
* @return static|bool
*/
public function loggerOnly($loggerOnly = null)
{
if (func_num_args() == 0) {
return $this->loggerOnly;
}
$this->loggerOnly = (bool) $loggerOnly;
return $this;
}
/**
* Test if handler can output to stdout.
* @return bool
*/
private function canOutput()
{
return !$this->loggerOnly();
}
/**
* Get the frame args var_dump.
* @param \Whoops\Exception\Frame $frame [description]
* @param integer $line [description]
* @return string
*/
private function getFrameArgsOutput(Frame $frame, $line)
{
if ($this->addTraceFunctionArgsToOutput() === false
|| $this->addTraceFunctionArgsToOutput() < $line) {
return '';
}
// Dump the arguments:
ob_start();
$this->dump($frame->getArgs());
if (ob_get_length() > $this->getTraceFunctionArgsOutputLimit()) {
// The argument var_dump is to big.
// Discarded to limit memory usage.
ob_clean();
return sprintf(
"\n%sArguments dump length greater than %d Bytes. Discarded.",
self::VAR_DUMP_PREFIX,
$this->getTraceFunctionArgsOutputLimit()
);
}
return sprintf(
"\n%s",
preg_replace('/^/m', self::VAR_DUMP_PREFIX, ob_get_clean())
);
}
/**
* Dump variable.
*
* @param mixed $var
* @return void
*/
protected function dump($var)
{
if ($this->dumper) {
call_user_func($this->dumper, $var);
} else {
var_dump($var);
}
}
/**
* Get the exception trace as plain text.
* @return string
*/
private function getTraceOutput()
{
if (! $this->addTraceToOutput()) {
return '';
}
$inspector = $this->getInspector();
$frames = $inspector->getFrames($this->getRun()->getFrameFilters());
$response = "\nStack trace:";
$line = 1;
foreach ($frames as $frame) {
/** @var Frame $frame */
$class = $frame->getClass();
$template = "\n%3d. %s->%s() %s:%d%s";
if (! $class) {
// Remove method arrow (->) from output.
$template = "\n%3d. %s%s() %s:%d%s";
}
$response .= sprintf(
$template,
$line,
$class,
$frame->getFunction(),
$frame->getFile(),
$frame->getLine(),
$this->getFrameArgsOutput($frame, $line)
);
$line++;
}
return $response;
}
/**
* Get the exception as plain text.
* @param \Throwable $exception
* @return string
*/
private function getExceptionOutput($exception)
{
return sprintf(
"%s: %s in file %s on line %d",
get_class($exception),
$exception->getMessage(),
$exception->getFile(),
$exception->getLine()
);
}
/**
* @return int
*/
public function handle()
{
$response = $this->generateResponse();
if ($this->getLogger()) {
$this->getLogger()->error($response);
}
if (! $this->canOutput()) {
return Handler::DONE;
}
echo $response;
return Handler::QUIT;
}
/**
* @return string
*/
public function contentType()
{
return 'text/plain';
}
}
================================================
FILE: src/Whoops/Handler/PrettyPageHandler.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use UnexpectedValueException;
use Whoops\Exception\Formatter;
use Whoops\Util\Misc;
use Whoops\Util\TemplateHelper;
class PrettyPageHandler extends Handler
{
const EDITOR_SUBLIME = "sublime";
const EDITOR_TEXTMATE = "textmate";
const EDITOR_EMACS = "emacs";
const EDITOR_MACVIM = "macvim";
const EDITOR_PHPSTORM = "phpstorm";
const EDITOR_IDEA = "idea";
const EDITOR_VSCODE = "vscode";
const EDITOR_ATOM = "atom";
const EDITOR_ESPRESSO = "espresso";
const EDITOR_XDEBUG = "xdebug";
const EDITOR_NETBEANS = "netbeans";
const EDITOR_CURSOR = "cursor";
/**
* Search paths to be scanned for resources.
*
* Stored in the reverse order they're declared.
*
* @var array
*/
private $searchPaths = [];
/**
* Fast lookup cache for known resource locations.
*
* @var array
*/
private $resourceCache = [];
/**
* The name of the custom css file.
*
* @var string|null
*/
private $customCss = null;
/**
* The name of the custom js file.
*
* @var string|null
*/
private $customJs = null;
/**
* @var array[]
*/
private $extraTables = [];
/**
* @var bool
*/
private $handleUnconditionally = false;
/**
* @var string
*/
private $pageTitle = "Whoops! There was an error.";
/**
* @var array[]
*/
private $applicationPaths;
/**
* @var array[]
*/
private $blacklist = [
'_GET' => [],
'_POST' => [],
'_FILES' => [],
'_COOKIE' => [],
'_SESSION' => [],
'_SERVER' => [],
'_ENV' => [],
];
/**
* An identifier for a known IDE/text editor.
*
* Either a string, or a calalble that resolves a string, that can be used
* to open a given file in an editor. If the string contains the special
* substrings %file or %line, they will be replaced with the correct data.
*
* @example
* "txmt://open?url=%file&line=%line"
*
* @var callable|string $editor
*/
protected $editor;
/**
* A list of known editor strings.
*
* @var array
*/
protected $editors = [
"sublime" => "subl://open?url=file://%file&line=%line",
"textmate" => "txmt://open?url=file://%file&line=%line",
"emacs" => "emacs://open?url=file://%file&line=%line",
"macvim" => "mvim://open/?url=file://%file&line=%line",
"phpstorm" => "phpstorm://open?file=%file&line=%line",
"idea" => "idea://open?file=%file&line=%line",
"vscode" => "vscode://file/%file:%line",
"atom" => "atom://core/open/file?filename=%file&line=%line",
"espresso" => "x-espresso://open?filepath=%file&lines=%line",
"netbeans" => "netbeans://open/?f=%file:%line",
"cursor" => "cursor://file/%file:%line",
];
/**
* @var TemplateHelper
*/
protected $templateHelper;
/**
* Constructor.
*
* @return void
*/
public function __construct()
{
if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) {
// Register editor using xdebug's file_link_format option.
$this->editors['xdebug'] = function ($file, $line) {
return str_replace(['%f', '%l'], [$file, $line], ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'));
};
// If xdebug is available, use it as default editor.
$this->setEditor('xdebug');
}
// Add the default, local resource search path:
$this->searchPaths[] = __DIR__ . "/../Resources";
// blacklist php provided auth based values
$this->blacklist('_SERVER', 'PHP_AUTH_PW');
$this->templateHelper = new TemplateHelper();
if (class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
$cloner = new VarCloner();
// Only dump object internals if a custom caster exists for performance reasons
// https://github.com/filp/whoops/pull/404
$cloner->addCasters(['*' => function ($obj, $a, $stub, $isNested, $filter = 0) {
$class = $stub->class;
$classes = [$class => $class] + class_parents($obj) + class_implements($obj);
foreach ($classes as $class) {
if (isset(AbstractCloner::$defaultCasters[$class])) {
return $a;
}
}
// Remove all internals
return [];
}]);
$this->templateHelper->setCloner($cloner);
}
}
/**
* @return int|null
*
* @throws \Exception
*/
public function handle()
{
if (!$this->handleUnconditionally()) {
// Check conditions for outputting HTML:
// @todo: Make this more robust
if (PHP_SAPI === 'cli') {
// Help users who have been relying on an internal test value
// fix their code to the proper method
if (isset($_ENV['whoops-test'])) {
throw new \Exception(
'Use handleUnconditionally instead of whoops-test'
.' environment variable'
);
}
return Handler::DONE;
}
}
$templateFile = $this->getResource("views/layout.html.php");
$cssFile = $this->getResource("css/whoops.base.css");
$zeptoFile = $this->getResource("js/zepto.min.js");
$prismJs = $this->getResource("js/prism.js");
$prismCss = $this->getResource("css/prism.css");
$clipboard = $this->getResource("js/clipboard.min.js");
$jsFile = $this->getResource("js/whoops.base.js");
if ($this->customCss) {
$customCssFile = $this->getResource($this->customCss);
}
if ($this->customJs) {
$customJsFile = $this->getResource($this->customJs);
}
$inspector = $this->getInspector();
$frames = $this->getExceptionFrames();
$code = $this->getExceptionCode();
// List of variables that will be passed to the layout template.
$vars = [
"page_title" => $this->getPageTitle(),
// @todo: Asset compiler
"stylesheet" => file_get_contents($cssFile),
"zepto" => file_get_contents($zeptoFile),
"prismJs" => file_get_contents($prismJs),
"prismCss" => file_get_contents($prismCss),
"clipboard" => file_get_contents($clipboard),
"javascript" => file_get_contents($jsFile),
// Template paths:
"header" => $this->getResource("views/header.html.php"),
"header_outer" => $this->getResource("views/header_outer.html.php"),
"frame_list" => $this->getResource("views/frame_list.html.php"),
"frames_description" => $this->getResource("views/frames_description.html.php"),
"frames_container" => $this->getResource("views/frames_container.html.php"),
"panel_details" => $this->getResource("views/panel_details.html.php"),
"panel_details_outer" => $this->getResource("views/panel_details_outer.html.php"),
"panel_left" => $this->getResource("views/panel_left.html.php"),
"panel_left_outer" => $this->getResource("views/panel_left_outer.html.php"),
"frame_code" => $this->getResource("views/frame_code.html.php"),
"env_details" => $this->getResource("views/env_details.html.php"),
"title" => $this->getPageTitle(),
"name" => explode("\\", $inspector->getExceptionName()),
"message" => $inspector->getExceptionMessage(),
"previousMessages" => $inspector->getPreviousExceptionMessages(),
"docref_url" => $inspector->getExceptionDocrefUrl(),
"code" => $code,
"previousCodes" => $inspector->getPreviousExceptionCodes(),
"plain_exception" => Formatter::formatExceptionPlain($inspector),
"frames" => $frames,
"has_frames" => !!count($frames),
"handler" => $this,
"handlers" => $this->getRun()->getHandlers(),
"active_frames_tab" => count($frames) && $frames->offsetGet(0)->isApplication() ? 'application' : 'all',
"has_frames_tabs" => $this->getApplicationPaths(),
"tables" => [
"GET Data" => $this->masked($_GET, '_GET'),
"POST Data" => $this->masked($_POST, '_POST'),
"Files" => isset($_FILES) ? $this->masked($_FILES, '_FILES') : [],
"Cookies" => $this->masked($_COOKIE, '_COOKIE'),
"Session" => isset($_SESSION) ? $this->masked($_SESSION, '_SESSION') : [],
"Server/Request Data" => $this->masked($_SERVER, '_SERVER'),
"Environment Variables" => $this->masked($_ENV, '_ENV'),
],
];
if (isset($customCssFile)) {
$vars["stylesheet"] .= file_get_contents($customCssFile);
}
if (isset($customJsFile)) {
$vars["javascript"] .= file_get_contents($customJsFile);
}
// Add extra entries list of data tables:
// @todo: Consolidate addDataTable and addDataTableCallback
$extraTables = array_map(function ($table) use ($inspector) {
return $table instanceof \Closure ? $table($inspector) : $table;
}, $this->getDataTables());
$vars["tables"] = array_merge($extraTables, $vars["tables"]);
$plainTextHandler = new PlainTextHandler();
$plainTextHandler->setRun($this->getRun());
$plainTextHandler->setException($this->getException());
$plainTextHandler->setInspector($this->getInspector());
$vars["preface"] = "<!--\n\n\n" . $this->templateHelper->escape($plainTextHandler->generateResponse()) . "\n\n\n\n\n\n\n\n\n\n\n-->";
$this->templateHelper->setVariables($vars);
$this->templateHelper->render($templateFile);
return Handler::QUIT;
}
/**
* Get the stack trace frames of the exception currently being handled.
*
* @return \Whoops\Exception\FrameCollection
*/
protected function getExceptionFrames()
{
$frames = $this->getInspector()->getFrames($this->getRun()->getFrameFilters());
if ($this->getApplicationPaths()) {
foreach ($frames as $frame) {
foreach ($this->getApplicationPaths() as $path) {
if (strpos($frame->getFile(), $path) === 0) {
$frame->setApplication(true);
break;
}
}
}
}
return $frames;
}
/**
* Get the code of the exception currently being handled.
*
* @return string
*/
protected function getExceptionCode()
{
$exception = $this->getException();
$code = $exception->getCode();
if ($exception instanceof \ErrorException) {
// ErrorExceptions wrap the php-error types within the 'severity' property
$code = Misc::translateErrorCode($exception->getSeverity());
}
return (string) $code;
}
/**
* @return string
*/
public function contentType()
{
return 'text/html';
}
/**
* Adds an entry to the list of tables displayed in the template.
*
* The expected data is a simple associative array. Any nested arrays
* will be flattened with `print_r`.
*
* @param string $label
*
* @return static
*/
public function addDataTable($label, array $data)
{
$this->extraTables[$label] = $data;
return $this;
}
/**
* Lazily adds an entry to the list of tables displayed in the table.
*
* The supplied callback argument will be called when the error is
* rendered, it should produce a simple associative array. Any nested
* arrays will be flattened with `print_r`.
*
* @param string $label
* @param callable $callback Callable returning an associative array
*
* @throws InvalidArgumentException If $callback is not callable
*
* @return static
*/
public function addDataTableCallback($label, /* callable */ $callback)
{
if (!is_callable($callback)) {
throw new InvalidArgumentException('Expecting callback argument to be callable');
}
$this->extraTables[$label] = function (?\Whoops\Inspector\InspectorInterface $inspector = null) use ($callback) {
try {
$result = call_user_func($callback, $inspector);
// Only return the result if it can be iterated over by foreach().
return is_array($result) || $result instanceof \Traversable ? $result : [];
} catch (\Exception $e) {
// Don't allow failure to break the rendering of the original exception.
return [];
}
};
return $this;
}
/**
* Returns all the extra data tables registered with this handler.
*
* Optionally accepts a 'label' parameter, to only return the data table
* under that label.
*
* @param string|null $label
*
* @return array[]|callable
*/
public function getDataTables($label = null)
{
if ($label !== null) {
return isset($this->extraTables[$label]) ?
$this->extraTables[$label] : [];
}
return $this->extraTables;
}
/**
* Set whether to handle unconditionally.
*
* Allows to disable all attempts to dynamically decide whether to handle
* or return prematurely. Set this to ensure that the handler will perform,
* no matter what.
*
* @param bool|null $value
*
* @return bool|static
*/
public function handleUnconditionally($value = null)
{
if (func_num_args() == 0) {
return $this->handleUnconditionally;
}
$this->handleUnconditionally = (bool) $value;
return $this;
}
/**
* Adds an editor resolver.
*
* Either a string, or a closure that resolves a string, that can be used
* to open a given file in an editor. If the string contains the special
* substrings %file or %line, they will be replaced with the correct data.
*
* @example
* $run->addEditor('macvim', "mvim://open?url=file://%file&line=%line")
* @example
* $run->addEditor('remove-it', function($file, $line) {
* unlink($file);
* return "http://stackoverflow.com";
* });
*
* @param string $identifier
* @param string|callable $resolver
*
* @return static
*/
public function addEditor($identifier, $resolver)
{
$this->editors[$identifier] = $resolver;
return $this;
}
/**
* Set the editor to use to open referenced files.
*
* Pass either the name of a configured editor, or a closure that directly
* resolves an editor string.
*
* @example
* $run->setEditor(function($file, $line) { return "file:///{$file}"; });
* @example
* $run->setEditor('sublime');
*
* @param string|callable $editor
*
* @throws InvalidArgumentException If invalid argument identifier provided
*
* @return static
*/
public function setEditor($editor)
{
if (!is_callable($editor) && !isset($this->editors[$editor])) {
throw new InvalidArgumentException(
"Unknown editor identifier: $editor. Known editors:" .
implode(",", array_keys($this->editors))
);
}
$this->editor = $editor;
return $this;
}
/**
* Get the editor href for a given file and line, if available.
*
* @param string $filePath
* @param int $line
*
* @throws InvalidArgumentException If editor resolver does not return a string
*
* @return string|bool
*/
public function getEditorHref($filePath, $line)
{
$editor = $this->getEditor($filePath, $line);
if (empty($editor)) {
return false;
}
// Check that the editor is a string, and replace the
// %line and %file placeholders:
if (!isset($editor['url']) || !is_string($editor['url'])) {
throw new UnexpectedValueException(
__METHOD__ . " should always resolve to a string or a valid editor array; got something else instead."
);
}
$editor['url'] = str_replace("%line", rawurlencode($line), $editor['url']);
$editor['url'] = str_replace("%file", rawurlencode($filePath), $editor['url']);
return $editor['url'];
}
/**
* Determine if the editor link should act as an Ajax request.
*
* @param string $filePath
* @param int $line
*
* @throws UnexpectedValueException If editor resolver does not return a boolean
*
* @return bool
*/
public function getEditorAjax($filePath, $line)
{
$editor = $this->getEditor($filePath, $line);
// Check that the ajax is a bool
if (!isset($editor['ajax']) || !is_bool($editor['ajax'])) {
throw new UnexpectedValueException(
__METHOD__ . " should always resolve to a bool; got something else instead."
);
}
return $editor['ajax'];
}
/**
* Determines both the editor and if ajax should be used.
*
* @param string $filePath
* @param int $line
*
* @return array
*/
protected function getEditor($filePath, $line)
{
if (!$this->editor || (!is_string($this->editor) && !is_callable($this->editor))) {
return [];
}
if (is_string($this->editor) && isset($this->editors[$this->editor]) && !is_callable($this->editors[$this->editor])) {
return [
'ajax' => false,
'url' => $this->editors[$this->editor],
];
}
if (is_callable($this->editor) || (isset($this->editors[$this->editor]) && is_callable($this->editors[$this->editor]))) {
if (is_callable($this->editor)) {
$callback = call_user_func($this->editor, $filePath, $line);
} else {
$callback = call_user_func($this->editors[$this->editor], $filePath, $line);
}
if (empty($callback)) {
return [];
}
if (is_string($callback)) {
return [
'ajax' => false,
'url' => $callback,
];
}
return [
'ajax' => isset($callback['ajax']) ? $callback['ajax'] : false,
'url' => isset($callback['url']) ? $callback['url'] : $callback,
];
}
return [];
}
/**
* Set the page title.
*
* @param string $title
*
* @return static
*/
public function setPageTitle($title)
{
$this->pageTitle = (string) $title;
return $this;
}
/**
* Get the page title.
*
* @return string
*/
public function getPageTitle()
{
return $this->pageTitle;
}
/**
* Adds a path to the list of paths to be searched for resources.
*
* @param string $path
*
* @throws InvalidArgumentException If $path is not a valid directory
*
* @return static
*/
public function addResourcePath($path)
{
if (!is_dir($path)) {
throw new InvalidArgumentException(
"'$path' is not a valid directory"
);
}
array_unshift($this->searchPaths, $path);
return $this;
}
/**
* Adds a custom css file to be loaded.
*
* @param string|null $name
*
* @return static
*/
public function addCustomCss($name)
{
$this->customCss = $name;
return $this;
}
/**
* Adds a custom js file to be loaded.
*
* @param string|null $name
*
* @return static
*/
public function addCustomJs($name)
{
$this->customJs = $name;
return $this;
}
/**
* @return array
*/
public function getResourcePaths()
{
return $this->searchPaths;
}
/**
* Finds a resource, by its relative path, in all available search paths.
*
* The search is performed starting at the last search path, and all the
* way back to the first, enabling a cascading-type system of overrides for
* all resources.
*
* @param string $resource
*
* @throws RuntimeException If resource cannot be found in any of the available paths
*
* @return string
*/
protected function getResource($resource)
{
// If the resource was found before, we can speed things up
// by caching its absolute, resolved path:
if (isset($this->resourceCache[$resource])) {
return $this->resourceCache[$resource];
}
// Search through available search paths, until we find the
// resource we're after:
foreach ($this->searchPaths as $path) {
$fullPath = $path . "/$resource";
if (is_file($fullPath)) {
// Cache the result:
$this->resourceCache[$resource] = $fullPath;
return $fullPath;
}
}
// If we got this far, nothing was found.
throw new RuntimeException(
"Could not find resource '$resource' in any resource paths."
. "(searched: " . join(", ", $this->searchPaths). ")"
);
}
/**
* @deprecated
*
* @return string
*/
public function getResourcesPath()
{
$allPaths = $this->getResourcePaths();
// Compat: return only the first path added
return end($allPaths) ?: null;
}
/**
* @deprecated
*
* @param string $resourcesPath
*
* @return static
*/
public function setResourcesPath($resourcesPath)
{
$this->addResourcePath($resourcesPath);
return $this;
}
/**
* Return the application paths.
*
* @return array
*/
public function getApplicationPaths()
{
return $this->applicationPaths;
}
/**
* Set the application paths.
*
* @return void
*/
public function setApplicationPaths(array $applicationPaths)
{
$this->applicationPaths = $applicationPaths;
}
/**
* Set the application root path.
*
* @param string $applicationRootPath
*
* @return void
*/
public function setApplicationRootPath($applicationRootPath)
{
$this->templateHelper->setApplicationRootPath($applicationRootPath);
}
/**
* blacklist a sensitive value within one of the superglobal arrays.
* Alias for the hideSuperglobalKey method.
*
* @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
* @param string $key The key within the superglobal
* @see hideSuperglobalKey
*
* @return static
*/
public function blacklist($superGlobalName, $key)
{
$this->blacklist[$superGlobalName][] = $key;
return $this;
}
/**
* Hide a sensitive value within one of the superglobal arrays.
*
* @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
* @param string $key The key within the superglobal
* @return static
*/
public function hideSuperglobalKey($superGlobalName, $key)
{
return $this->blacklist($superGlobalName, $key);
}
/**
* Checks all values within the given superGlobal array.
*
* Blacklisted values will be replaced by a equal length string containing
* only '*' characters for string values.
* Non-string values will be replaced with a fixed asterisk count.
* We intentionally dont rely on $GLOBALS as it depends on the 'auto_globals_jit' php.ini setting.
*
* @param array|\ArrayAccess $superGlobal One of the superglobal arrays
* @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
*
* @return array $values without sensitive data
*/
private function masked($superGlobal, $superGlobalName)
{
$blacklisted = $this->blacklist[$superGlobalName];
$values = $superGlobal;
foreach ($blacklisted as $key) {
if (isset($superGlobal[$key])) {
$values[$key] = str_repeat('*', is_string($superGlobal[$key]) ? strlen($superGlobal[$key]) : 3);
}
}
return $values;
}
}
================================================
FILE: src/Whoops/Handler/XmlResponseHandler.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use SimpleXMLElement;
use Whoops\Exception\Formatter;
/**
* Catches an exception and converts it to an XML
* response. Additionally can also return exception
* frames for consumption by an API.
*/
class XmlResponseHandler extends Handler
{
/**
* @var bool
*/
private $returnFrames = false;
/**
* @param bool|null $returnFrames
* @return bool|static
*/
public function addTraceToOutput($returnFrames = null)
{
if (func_num_args() == 0) {
return $this->returnFrames;
}
$this->returnFrames = (bool) $returnFrames;
return $this;
}
/**
* @return int
*/
public function handle()
{
$response = [
'error' => Formatter::formatExceptionAsDataArray(
$this->getInspector(),
$this->addTraceToOutput(),
$this->getRun()->getFrameFilters()
),
];
echo self::toXml($response);
return Handler::QUIT;
}
/**
* @return string
*/
public function contentType()
{
return 'application/xml';
}
/**
* @param SimpleXMLElement $node Node to append data to, will be modified in place
* @param array|\Traversable $data
* @return SimpleXMLElement The modified node, for chaining
*/
private static function addDataToNode(\SimpleXMLElement $node, $data)
{
assert(is_array($data) || $data instanceof Traversable);
foreach ($data as $key => $value) {
if (is_numeric($key)) {
// Convert the key to a valid string
$key = "unknownNode_". (string) $key;
}
// Delete any char not allowed in XML element names
$key = preg_replace('/[^a-z0-9\-\_\.\:]/i', '', $key);
if (is_array($value)) {
$child = $node->addChild($key);
self::addDataToNode($child, $value);
} else {
$value = str_replace('&', '&', print_r($value, true));
$node->addChild($key, $value);
}
}
return $node;
}
/**
* The main function for converting to an XML document.
*
* @param array|\Traversable $data
* @return string XML
*/
private static function toXml($data)
{
assert(is_array($data) || $data instanceof Traversable);
$node = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><root />");
return self::addDataToNode($node, $data)->asXML();
}
}
================================================
FILE: src/Whoops/Inspector/InspectorFactory.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Inspector;
use Whoops\Exception\Inspector;
class InspectorFactory implements InspectorFactoryInterface
{
/**
* @param \Throwable $exception
* @return InspectorInterface
*/
public function create($exception)
{
return new Inspector($exception, $this);
}
}
================================================
FILE: src/Whoops/Inspector/InspectorFactoryInterface.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Inspector;
interface InspectorFactoryInterface
{
/**
* @param \Throwable $exception
* @return InspectorInterface
*/
public function create($exception);
}
================================================
FILE: src/Whoops/Inspector/InspectorInterface.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Inspector;
interface InspectorInterface
{
/**
* @return \Throwable
*/
public function getException();
/**
* @return string
*/
public function getExceptionName();
/**
* @return string
*/
public function getExceptionMessage();
/**
* @return string[]
*/
public function getPreviousExceptionMessages();
/**
* @return int[]
*/
public function getPreviousExceptionCodes();
/**
* Returns a url to the php-manual related to the underlying error - when available.
*
* @return string|null
*/
public function getExceptionDocrefUrl();
/**
* Does the wrapped Exception has a previous Exception?
* @return bool
*/
public function hasPreviousException();
/**
* Returns an Inspector for a previous Exception, if any.
* @todo Clean this up a bit, cache stuff a bit better.
* @return InspectorInterface
*/
public function getPreviousExceptionInspector();
/**
* Returns an array of all previous exceptions for this inspector's exception
* @return \Throwable[]
*/
public function getPreviousExceptions();
/**
* Returns an iterator for the inspected exception's
* frames.
*
* @param array<callable> $frameFilters
*
* @return \Whoops\Exception\FrameCollection
*/
public function getFrames(array $frameFilters = []);
}
================================================
FILE: src/Whoops/Resources/css/prism.css
================================================
/* PrismJS 1.30.0
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+markup-templating+php&plugins=line-highlight+line-numbers */
code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}
pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:after,.line-numbers .line-highlight:before{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,.2)}
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
================================================
FILE: src/Whoops/Resources/css/whoops.base.css
================================================
body {
font: 12px "Helvetica Neue", helvetica, arial, sans-serif;
color: #131313;
background: #eeeeee;
padding:0;
margin: 0;
max-height: 100%;
text-rendering: optimizeLegibility;
}
a {
text-decoration: none;
}
.Whoops.container {
position: relative;
z-index: 9999999999;
}
.panel {
overflow-y: scroll;
height: 100%;
position: fixed;
margin: 0;
left: 0;
top: 0;
}
.branding {
position: absolute;
top: 10px;
right: 20px;
color: #777777;
font-size: 10px;
z-index: 100;
}
.branding a {
color: #e95353;
}
header {
color: white;
box-sizing: border-box;
background-color: #2a2a2a;
padding: 35px 40px;
max-height: 180px;
overflow: hidden;
transition: 0.5s;
}
header.header-expand {
max-height: 1000px;
}
.exc-title {
margin: 0;
color: #bebebe;
font-size: 14px;
}
.exc-title-primary, .exc-title-secondary {
color: #e95353;
}
.exc-message {
font-size: 20px;
word-wrap: break-word;
margin: 4px 0 0 0;
color: white;
}
.exc-message span {
display: block;
}
.exc-message-empty-notice {
color: #a29d9d;
font-weight: 300;
}
.prev-exc-title {
margin: 10px 0;
}
.prev-exc-title + ul {
margin: 0;
padding: 0 0 0 20px;
line-height: 12px;
}
.prev-exc-title + ul li {
font: 12px "Helvetica Neue", helvetica, arial, sans-serif;
}
.prev-exc-title + ul li .prev-exc-code {
display: inline-block;
color: #bebebe;
}
.details-container {
left: 30%;
width: 70%;
background: #fafafa;
}
.details {
padding: 5px;
}
.details-heading {
color: #4288CE;
font-weight: 300;
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px solid rgba(0, 0, 0, .1);
}
.details pre.sf-dump {
white-space: pre;
word-wrap: inherit;
}
.details pre.sf-dump,
.details pre.sf-dump .sf-dump-num,
.details pre.sf-dump .sf-dump-const,
.details pre.sf-dump .sf-dump-str,
.details pre.sf-dump .sf-dump-note,
.details pre.sf-dump .sf-dump-ref,
.details pre.sf-dump .sf-dump-public,
.details pre.sf-dump .sf-dump-protected,
.details pre.sf-dump .sf-dump-private,
.details pre.sf-dump .sf-dump-meta,
.details pre.sf-dump .sf-dump-key,
.details pre.sf-dump .sf-dump-index {
color: #463C54;
}
.left-panel {
width: 30%;
background: #ded8d8;
}
.frames-description {
background: rgba(0, 0, 0, .05);
padding: 8px 15px;
color: #a29d9d;
font-size: 11px;
}
.frames-description.frames-description-application {
text-align: center;
font-size: 12px;
}
.frames-container.frames-container-application .frame:not(.frame-application) {
display: none;
}
.frames-tab {
color: #a29d9d;
display: inline-block;
padding: 4px 8px;
margin: 0 2px;
border-radius: 3px;
}
.frames-tab.frames-tab-active {
background-color: #2a2a2a;
color: #bebebe;
}
.frame {
padding: 14px;
cursor: pointer;
transition: all 0.1s ease;
background: #eeeeee;
}
.frame:not(:last-child) {
border-bottom: 1px solid rgba(0, 0, 0, .05);
}
.frame.active {
box-shadow: inset -5px 0 0 0 #4288CE;
color: #4288CE;
}
.frame:not(.active):hover {
background: #BEE9EA;
}
.frame-method-info {
margin-bottom: 10px;
}
.frame-class, .frame-function, .frame-index {
font-size: 14px;
}
.frame-index {
float: left;
}
.frame-method-info {
margin-left: 24px;
}
.frame-index {
font-size: 11px;
color: #a29d9d;
background-color: rgba(0, 0, 0, .05);
height: 18px;
width: 18px;
line-height: 18px;
border-radius: 5px;
padding: 0 1px 0 1px;
text-align: center;
display: inline-block;
}
.frame-application .frame-index {
background-color: #2a2a2a;
color: #bebebe;
}
.frame-file {
font-family: "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace;
color: #a29d9d;
}
.frame-file .editor-link {
color: #a29d9d;
}
.frame-line {
font-weight: bold;
}
.frame-code {
padding: 5px;
background: #303030;
display: none;
}
.frame-code.active {
display: block;
}
.frame-code .frame-file {
color: #a29d9d;
padding: 12px 6px;
border-bottom: none;
}
.code-block {
padding: 10px;
margin: 0;
border-radius: 6px;
box-shadow: 0 3px 0 rgba(0, 0, 0, .05),
0 10px 30px rgba(0, 0, 0, .05),
inset 0 0 1px 0 rgba(255, 255, 255, .07);
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
}
.linenums {
margin: 0;
margin-left: 10px;
}
.frame-comments {
border-top: none;
margin-top: 15px;
font-size: 12px;
}
.frame-comments.empty {
}
.frame-comments.empty:before {
content: "No comments for this stack frame.";
font-weight: 300;
color: #a29d9d;
}
.frame-comment {
padding: 10px;
color: #e3e3e3;
border-radius: 6px;
background-color: rgba(255, 255, 255, .05);
}
.frame-comment a {
font-weight: bold;
text-decoration: underline;
color: #c6c6c6;
}
.frame-comment:not(:last-child) {
border-bottom: 1px dotted rgba(0, 0, 0, .3);
}
.frame-comment-context {
font-size: 10px;
color: white;
}
.delimiter {
display: inline-block;
}
.data-table-container label {
font-size: 16px;
color: #303030;
font-weight: bold;
margin: 10px 0;
display: block;
margin-bottom: 5px;
padding-bottom: 5px;
}
.data-table {
width: 100%;
margin-bottom: 10px;
}
.data-table tbody {
font: 13px "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace;
}
.data-table thead {
display: none;
}
.data-table tr {
padding: 5px 0;
}
.data-table td:first-child {
width: 20%;
min-width: 130px;
overflow: hidden;
font-weight: bold;
color: #463C54;
padding-right: 5px;
}
.data-table td:last-child {
width: 80%;
-ms-word-break: break-all;
word-break: break-all;
word-break: break-word;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
.data-table span.empty {
color: rgba(0, 0, 0, .3);
font-weight: 300;
}
.data-table label.empty {
display: inline;
}
.handler {
padding: 4px 0;
font: 14px "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace;
}
#plain-exception {
display: none;
}
.rightButton {
cursor: pointer;
border: 0;
opacity: .8;
background: none;
color: rgba(255, 255, 255, 0.1);
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.1);
border-radius: 3px;
outline: none !important;
}
.rightButton:hover {
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.3);
color: rgba(255, 255, 255, 0.3);
}
/* inspired by githubs kbd styles */
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #fcfcfc;
border-color: #ccc #ccc #bbb;
border-image: none;
border-style: solid;
border-width: 1px;
color: #555;
display: inline-block;
font-size: 11px;
line-height: 10px;
padding: 3px 5px;
vertical-align: middle;
}
/* == Media queries */
/* Expand the spacing in the details section */
@media (min-width: 1000px) {
.details, .frame-code {
padding: 20px 40px;
}
.details-container {
left: 32%;
width: 68%;
}
.frames-container {
margin: 5px;
}
.left-panel {
width: 32%;
}
}
/* Stack panels */
@media (max-width: 600px) {
.panel {
position: static;
width: 100%;
}
}
/* Stack details tables */
@media (max-width: 400px) {
.data-table,
.data-table tbody,
.data-table tbody tr,
.data-table tbody td {
display: block;
width: 100%;
}
.data-table tbody tr:first-child {
padding-top: 0;
}
.data-table tbody td:first-child,
.data-table tbody td:last-child {
padding-left: 0;
padding-right: 0;
}
.data-table tbody td:last-child {
padding-top: 3px;
}
}
.tooltipped {
position: relative
}
.tooltipped:after {
position: absolute;
z-index: 1000000;
display: none;
padding: 5px 8px;
color: #fff;
text-align: center;
text-decoration: none;
text-shadow: none;
text-transform: none;
letter-spacing: normal;
word-wrap: break-word;
white-space: pre;
pointer-events: none;
content: attr(aria-label);
background: rgba(0, 0, 0, 0.8);
border-radius: 3px;
-webkit-font-smoothing: subpixel-antialiased
}
.tooltipped:before {
position: absolute;
z-index: 1000001;
display: none;
width: 0;
height: 0;
color: rgba(0, 0, 0, 0.8);
pointer-events: none;
content: "";
border: 5px solid transparent
}
.tooltipped:hover:before,
.tooltipped:hover:after,
.tooltipped:active:before,
.tooltipped:active:after,
.tooltipped:focus:before,
.tooltipped:focus:after {
display: inline-block;
text-decoration: none
}
.tooltipped-s:after {
top: 100%;
right: 50%;
margin-top: 5px
}
.tooltipped-s:before {
top: auto;
right: 50%;
bottom: -5px;
margin-right: -5px;
border-bottom-color: rgba(0, 0, 0, 0.8)
}
pre.sf-dump {
padding: 0px !important;
margin: 0px !important;
}
.search-for-help {
width: 85%;
padding: 0;
margin: 10px 0;
list-style-type: none;
display: inline-block;
}
.search-for-help li {
display: inline-block;
margin-right: 5px;
}
.search-for-help li:last-child {
margin-right: 0;
}
.search-for-help li a {
}
.search-for-help li a i {
width: 16px;
height: 16px;
overflow: hidden;
display: block;
}
.search-for-help li a svg {
fill: #fff;
}
.search-for-help li a svg path {
background-size: contain;
}
.line-numbers-rows span {
pointer-events: auto;
cursor: pointer;
}
.line-numbers-rows span:hover {
text-decoration: underline;
}
================================================
FILE: src/Whoops/Resources/js/prism.js
================================================
/* PrismJS 1.30.0
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+markup-templating+php&plugins=line-highlight+line-numbers */
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case"Object":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)&&(r[l]=e(n[l],t));return r;case"Array":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return"none"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,"gi"),""),e.classList.add("language-"+t)},currentScript:function(){if("undefined"==typeof document)return null;if(document.currentScript&&"SCRIPT"===document.currentScript.tagName)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName("script");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r="no-"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)&&(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u&&n!=e&&(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);"Object"!==u||i[l(s)]?"Array"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};a.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run("before-all-elements-highlight",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o&&"pre"===o.nodeName.toLowerCase()&&a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run("before-insert",s),s.element.innerHTML=s.highlightedCode,a.hooks.run("after-highlight",s),a.hooks.run("complete",s),r&&r.call(s.element)}if(a.hooks.run("before-sanity-check",s),(o=s.element.parentElement)&&"pre"===o.nodeName.toLowerCase()&&!o.hasAttribute("tabindex")&&o.setAttribute("tabindex","0"),!s.code)return a.hooks.run("complete",s),void(r&&r.call(s.element));if(a.hooks.run("before-highlight",s),s.grammar)if(t&&e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run("before-tokenize",r),!r.grammar)throw new Error('The language "'+r.language+'" has no grammar.');return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run("after-tokenize",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t&&t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d<h.length;++d){if(g&&g.cause==f+","+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y&&!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+"g")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail&&!(g&&A>=g.reach);A+=w.value.length,w=w.next){var P=w.value;if(n.length>e.length)return;if(!(P instanceof i)){var E,S=1;if(y){if(!(E=l(b,A,e,m))||E.index>=e.length)break;var L=E.index,O=E.index+E[0].length,C=A;for(C+=w.value.length;L>=C;)C+=(w=w.next).value.length;if(A=C-=w.value.length,w.value instanceof i)continue;for(var j=w;j!==n.tail&&(C<O||"string"==typeof j.value);j=j.next)S++,C+=j.value.length;S--,P=e.slice(A,C),E.index-=A}else if(!(E=l(b,0,P,m)))continue;L=E.index;var N=E[0],_=P.slice(0,L),M=P.slice(L+N.length),W=A+P.length;g&&W>g.reach&&(g.reach=W);var I=w.prev;if(_&&(I=u(n,I,_),A+=_.length),c(n,I,S),w=u(n,I,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),S>1){var T={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,T),g&&T.reach>g.reach&&(g.reach=T.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if("string"==typeof n)return n;if(Array.isArray(n)){var r="";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:"span",classes:["token",n.type],attributes:{},language:t},l=n.alias;l&&(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=" "+s+'="'+(i.attributes[s]||"").replace(/"/g,""")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+o+">"+i.content+"</"+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
Prism.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^<!\[CDATA\[|\]\]>$/i;var t={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:<!\\[CDATA\\[(?:[^\\]]|\\](?!\\]>))*\\]\\]>|(?!<!\\[CDATA\\[)[^])*?(?=</__>)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
!function(e){function n(e,n){return"___"+e.toUpperCase()+n+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(t,a,r,o){if(t.language===a){var c=t.tokenStack=[];t.code=t.code.replace(r,(function(e){if("function"==typeof o&&!o(e))return e;for(var r,i=c.length;-1!==t.code.indexOf(r=n(a,i));)++i;return c[i]=e,r})),t.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(t,a){if(t.language===a&&t.tokenStack){t.grammar=e.languages[a];var r=0,o=Object.keys(t.tokenStack);!function c(i){for(var u=0;u<i.length&&!(r>=o.length);u++){var g=i[u];if("string"==typeof g||g.content&&"string"==typeof g.content){var l=o[r],s=t.tokenStack[l],f="string"==typeof g?g:g.content,p=n(a,l),k=f.indexOf(p);if(k>-1){++r;var m=f.substring(0,k),d=new e.Token(a,e.tokenize(s,t.grammar),"language-"+a,s),h=f.substring(k+p.length),v=[];m&&v.push.apply(v,c([m])),v.push(d),h&&v.push.apply(v,c([h])),"string"==typeof g?i.splice.apply(i,[u,1].concat(v)):g.content=v}}else g.content&&c(g.content)}return i}(t.tokens)}}}})}(Prism);
!function(e){var a=/\/\*[\s\S]*?\*\/|\/\/.*|#(?!\[).*/,t=[{pattern:/\b(?:false|true)\b/i,alias:"boolean"},{pattern:/(::\s*)\b[a-z_]\w*\b(?!\s*\()/i,greedy:!0,lookbehind:!0},{pattern:/(\b(?:case|const)\s+)\b[a-z_]\w*(?=\s*[;=])/i,greedy:!0,lookbehind:!0},/\b(?:null)\b/i,/\b[A-Z_][A-Z0-9_]*\b(?!\s*\()/],i=/\b0b[01]+(?:_[01]+)*\b|\b0o[0-7]+(?:_[0-7]+)*\b|\b0x[\da-f]+(?:_[\da-f]+)*\b|(?:\b\d+(?:_\d+)*\.?(?:\d+(?:_\d+)*)?|\B\.\d+)(?:e[+-]?\d+)?/i,n=/<?=>|\?\?=?|\.{3}|\??->|[!=]=?=?|::|\*\*=?|--|\+\+|&&|\|\||<<|>>|[?~]|[/^|%*&<>.+-]=?/,s=/[{}\[\](),:;]/;e.languages.php={delimiter:{pattern:/\?>$|^<\?(?:php(?=\s)|=)?/i,alias:"important"},comment:a,variable:/\$+(?:\w+\b|(?=\{))/,package:{pattern:/(namespace\s+|use\s+(?:function\s+)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,lookbehind:!0,inside:{punctuation:/\\/}},"class-name-definition":{pattern:/(\b(?:class|enum|interface|trait)\s+)\b[a-z_]\w*(?!\\)\b/i,lookbehind:!0,alias:"class-name"},"function-definition":{pattern:/(\bfunction\s+)[a-z_]\w*(?=\s*\()/i,lookbehind:!0,alias:"function"},keyword:[{pattern:/(\(\s*)\b(?:array|bool|boolean|float|int|integer|object|string)\b(?=\s*\))/i,alias:"type-casting",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|object|self|static|string)\b(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|never|object|self|static|string|void)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/\b(?:array(?!\s*\()|bool|float|int|iterable|mixed|object|string|void)\b/i,alias:"type-declaration",greedy:!0},{pattern:/(\|\s*)(?:false|null)\b|\b(?:false|null)(?=\s*\|)/i,alias:"type-declaration",greedy:!0,lookbehind:!0},{pattern:/\b(?:parent|self|static)(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(\byield\s+)from\b/i,lookbehind:!0},/\bclass\b/i,{pattern:/((?:^|[^\s>:]|(?:^|[^-])>|(?:^|[^:]):)\s*)\b(?:abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|never|new|or|parent|print|private|protected|public|readonly|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield|__halt_compiler)\b/i,lookbehind:!0}],"argument-name":{pattern:/([(,]\s*)\b[a-z_]\w*(?=\s*:(?!:))/i,lookbehind:!0},"class-name":[{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self|\s+static))\s+|\bcatch\s*\()\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/(\|\s*)\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/\b[a-z_]\w*(?!\\)\b(?=\s*\|)/i,greedy:!0},{pattern:/(\|\s*)(?:\\?\b[a-z_]\w*)+\b/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(?:\\?\b[a-z_]\w*)+\b(?=\s*\|)/i,alias:"class-name-fully-qualified",greedy:!0,inside:{punctuation:/\\/}},{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self\b|\s+static\b))\s+|\bcatch\s*\()(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*\$)/i,alias:"type-declaration",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-declaration"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*::)/i,alias:["class-name-fully-qualified","static-context"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/([(,?]\s*)[a-z_]\w*(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-hint"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b[a-z_]\w*(?!\\)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:["class-name-fully-qualified","return-type"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:t,function:{pattern:/(^|[^\\\w])\\?[a-z_](?:[\w\\]*\w)?(?=\s*\()/i,lookbehind:!0,inside:{punctuation:/\\/}},property:{pattern:/(->\s*)\w+/,lookbehind:!0},number:i,operator:n,punctuation:s};var l={pattern:/\{\$(?:\{(?:\{[^{}]+\}|[^{}]+)\}|[^{}])+\}|(^|[^\\{])\$+(?:\w+(?:\[[^\r\n\[\]]+\]|->\w+)?)/,lookbehind:!0,inside:e.languages.php},r=[{pattern:/<<<'([^']+)'[\r\n](?:.*[\r\n])*?\1;/,alias:"nowdoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},{pattern:/<<<(?:"([^"]+)"[\r\n](?:.*[\r\n])*?\1;|([a-z_]\w*)[\r\n](?:.*[\r\n])*?\2;)/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:l}},{pattern:/`(?:\\[\s\S]|[^\\`])*`/,alias:"backtick-quoted-string",greedy:!0},{pattern:/'(?:\\[\s\S]|[^\\'])*'/,alias:"single-quoted-string",greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,alias:"double-quoted-string",greedy:!0,inside:{interpolation:l}}];e.languages.insertBefore("php","variable",{string:r,attribute:{pattern:/#\[(?:[^"'\/#]|\/(?![*/])|\/\/.*$|#(?!\[).*$|\/\*(?:[^*]|\*(?!\/))*\*\/|"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*')+\](?=\s*[a-z$#])/im,greedy:!0,inside:{"attribute-content":{pattern:/^(#\[)[\s\S]+(?=\]$)/,lookbehind:!0,inside:{comment:a,string:r,"attribute-class-name":[{pattern:/([^:]|^)\b[a-z_]\w*(?!\\)\b/i,alias:"class-name",greedy:!0,lookbehind:!0},{pattern:/([^:]|^)(?:\\?\b[a-z_]\w*)+/i,alias:["class-name","class-name-fully-qualified"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:t,number:i,operator:n,punctuation:s}},delimiter:{pattern:/^#\[|\]$/,alias:"punctuation"}}}}),e.hooks.add("before-tokenize",(function(a){/<\?/.test(a.code)&&e.languages["markup-templating"].buildPlaceholders(a,"php",/<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#(?!\[))(?:[^?\n\r]|\?(?!>))*(?=$|\?>|[\r\n])|#\[|\/\*(?:[^*]|\*(?!\/))*(?:\*\/|$))*?(?:\?>|$)/g)})),e.hooks.add("after-tokenize",(function(a){e.languages["markup-templating"].tokenizePlaceholders(a,"php")}))}(Prism);
!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document&&document.querySelector){var e,t="line-numbers",i="linkable-line-numbers",n=/\n(?!$)/g,r=!0;Prism.plugins.lineHighlight={highlightLines:function(o,u,c){var h=(u="string"==typeof u?u:o.getAttribute("data-line")||"").replace(/\s+/g,"").split(",").filter(Boolean),d=+o.getAttribute("data-line-offset")||0,f=(function(){if(void 0===e){var t=document.createElement("div");t.style.fontSize="13px",t.style.lineHeight="1.5",t.style.padding="0",t.style.border="0",t.innerHTML=" <br /> ",document.body.appendChild(t),e=38===t.offsetHeight,document.body.removeChild(t)}return e}()?parseInt:parseFloat)(getComputedStyle(o).lineHeight),p=Prism.util.isActive(o,t),g=o.querySelector("code"),m=p?o:g||o,v=[],y=g.textContent.match(n),b=y?y.length+1:1,A=g&&m!=g?function(e,t){var i=getComputedStyle(e),n=getComputedStyle(t);function r(e){return+e.substr(0,e.length-2)}return t.offsetTop+r(n.borderTopWidth)+r(n.paddingTop)-r(i.paddingTop)}(o,g):0;h.forEach((function(e){var t=e.split("-"),i=+t[0],n=+t[1]||i;if(!((n=Math.min(b+d,n))<i)){var r=o.querySelector('.line-highlight[data-range="'+e+'"]')||document.createElement("div");if(v.push((function(){r.setAttribute("aria-hidden","true"),r.setAttribute("data-range",e),r.className=(c||"")+" line-highlight"})),p&&Prism.plugins.lineNumbers){var s=Prism.plugins.lineNumbers.getLine(o,i),l=Prism.plugins.lineNumbers.getLine(o,n);if(s){var a=s.offsetTop+A+"px";v.push((function(){r.style.top=a}))}if(l){var u=l.offsetTop-s.offsetTop+l.offsetHeight+"px";v.push((function(){r.style.height=u}))}}else v.push((function(){r.setAttribute("data-start",String(i)),n>i&&r.setAttribute("data-end",String(n)),r.style.top=(i-d-1)*f+A+"px",r.textContent=new Array(n-i+2).join(" \n")}));v.push((function(){r.style.width=o.scrollWidth+"px"})),v.push((function(){m.appendChild(r)}))}}));var P=o.id;if(p&&Prism.util.isActive(o,i)&&P){l(o,i)||v.push((function(){o.classList.add(i)}));var E=parseInt(o.getAttribute("data-start")||"1");s(".line-numbers-rows > span",o).forEach((function(e,t){var i=t+E;e.onclick=function(){var e=P+"."+i;r=!1,location.hash=e,setTimeout((function(){r=!0}),1)}}))}return function(){v.forEach(a)}}};var o=0;Prism.hooks.add("before-sanity-check",(function(e){var t=e.element.parentElement;if(u(t)){var i=0;s(".line-highlight",t).forEach((function(e){i+=e.textContent.length,e.parentNode.removeChild(e)})),i&&/^(?: \n)+$/.test(e.code.slice(-i))&&(e.code=e.code.slice(0,-i))}})),Prism.hooks.add("complete",(function e(i){var n=i.element.parentElement;if(u(n)){clearTimeout(o);var r=Prism.plugins.lineNumbers,s=i.plugins&&i.plugins.lineNumbers;l(n,t)&&r&&!s?Prism.hooks.add("line-numbers",e):(Prism.plugins.lineHighlight.highlightLines(n)(),o=setTimeout(c,1))}})),window.addEventListener("hashchange",c),window.addEventListener("resize",(function(){s("pre").filter(u).map((function(e){return Prism.plugins.lineHighlight.highlightLines(e)})).forEach(a)}))}function s(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function l(e,t){return e.classList.contains(t)}function a(e){e()}function u(e){return!!(e&&/pre/i.test(e.nodeName)&&(e.hasAttribute("data-line")||e.id&&Prism.util.isActive(e,i)))}function c(){var e=location.hash.slice(1);s(".temporary.line-highlight").forEach((function(e){e.parentNode.removeChild(e)}));var t=(e.match(/\.([\d,-]+)$/)||[,""])[1];if(t&&!document.getElementById(e)){var i=e.slice(0,e.lastIndexOf(".")),n=document.getElementById(i);n&&(n.hasAttribute("data-line")||n.setAttribute("data-line",""),Prism.plugins.lineHighlight.highlightLines(n,t,"temporary ")(),r&&document.querySelector(".temporary.line-highlight").scrollIntoView())}}}();
!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var e="line-numbers",n=/\n(?!$)/g,t=Prism.plugins.lineNumbers={getLine:function(n,t){if("PRE"===n.tagName&&n.classList.contains(e)){var i=n.querySelector(".line-numbers-rows");if(i){var r=parseInt(n.getAttribute("data-start"),10)||1,s=r+(i.children.length-1);t<r&&(t=r),t>s&&(t=s);var l=t-r;return i.children[l]}}},resize:function(e){r([e])},assumeViewportIndependence:!0},i=void 0;window.addEventListener("resize",(function(){t.assumeViewportIndependence&&i===window.innerWidth||(i=window.innerWidth,r(Array.prototype.slice.call(document.querySelectorAll("pre.line-numbers"))))})),Prism.hooks.add("complete",(function(t){if(t.code){var i=t.element,s=i.parentNode;if(s&&/pre/i.test(s.nodeName)&&!i.querySelector(".line-numbers-rows")&&Prism.util.isActive(i,e)){i.classList.remove(e),s.classList.add(e);var l,o=t.code.match(n),a=o?o.length+1:1,u=new Array(a+1).join("<span></span>");(l=document.createElement("span")).setAttribute("aria-hidden","true"),l.className="line-numbers-rows",l.innerHTML=u,s.hasAttribute("data-start")&&(s.style.counterReset="linenumber "+(parseInt(s.getAttribute("data-start"),10)-1)),t.element.appendChild(l),r([s]),Prism.hooks.run("line-numbers",t)}}})),Prism.hooks.add("line-numbers",(function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0}))}function r(e){if(0!=(e=e.filter((function(e){var n,t=(n=e,n?window.getComputedStyle?getComputedStyle(n):n.currentStyle||null:null)["white-space"];return"pre-wrap"===t||"pre-line"===t}))).length){var t=e.map((function(e){var t=e.querySelector("code"),i=e.querySelector(".line-numbers-rows");if(t&&i){var r=e.querySelector(".line-numbers-sizer"),s=t.textContent.split(n);r||((r=document.createElement("span")).className="line-numbers-sizer",t.appendChild(r)),r.innerHTML="0",r.style.display="block";var l=r.getBoundingClientRect().height;return r.innerHTML="",{element:e,lines:s,lineHeights:[],oneLinerHeight:l,sizer:r}}})).filter(Boolean);t.forEach((function(e){var n=e.sizer,t=e.lines,i=e.lineHeights,r=e.oneLinerHeight;i[t.length-1]=void 0,t.forEach((function(e,t){if(e&&e.length>1){var s=n.appendChild(document.createElement("span"));s.style.display="block",s.textContent=e}else i[t]=r}))})),t.forEach((function(e){for(var n=e.sizer,t=e.lineHeights,i=0,r=0;r<t.length;r++)void 0===t[r]&&(t[r]=n.children[i++].getBoundingClientRect().height)})),t.forEach((function(e){var n=e.sizer,t=e.element.querySelector(".line-numbers-rows");n.style.display="none",n.innerHTML="",e.lineHeights.forEach((function(e,n){t.children[n].style.height=e+"px"}))}))}}}();
================================================
FILE: src/Whoops/Resources/js/whoops.base.js
================================================
Zepto(function($) {
var $leftPanel = $('.left-panel');
var $frameContainer = $('.frames-container');
var $appFramesTab = $('#application-frames-tab');
var $allFramesTab = $('#all-frames-tab');
var $container = $('.details-container');
var $activeLine = $frameContainer.find('.frame.active');
var $activeFrame = $container.find('.frame-code.active');
var $ajaxEditors = $('.editor-link[data-ajax]');
var $header = $('header');
$header.on('mouseenter', function () {
if ($header.find('.exception').height() >= 145) {
$header.addClass('header-expand');
}
});
$header.on('mouseleave', function () {
$header.removeClass('header-expand');
});
/*
* add prettyprint classes to our current active codeblock
* run prettyPrint() to highlight the active code
* scroll to the line when prettyprint is done
* highlight the current line
*/
var renderCurrentCodeblock = function(id) {
Prism.highlightAllUnder(document.querySelector('.frame-code-container .frame-code.active'));
highlightCurrentLine();
}
/*
* Highlight the active and neighboring lines for the current frame
* Adjust the offset to make sure that line is veritcally centered
*/
var highlightCurrentLine = function() {
// We show more code than needed, purely for proper syntax highlighting
// Let’s hide a big chunk of that code and then scroll the remaining block
$activeFrame.find('.code-block').first().css({
maxHeight: 345,
overflow: 'hidden',
});
var line = $activeFrame.find('.code-block .line-highlight').first()[0];
// [internal] frames might not contain a code-block
if (line) {
line.scrollIntoView();
line.parentElement.scrollTop -= 180;
}
$container.scrollTop(0);
}
/*
* click handler for loading codeblocks
*/
$frameContainer.on('click', '.frame', function() {
var $this = $(this);
var id = /frame\-line\-([\d]*)/.exec($this.attr('id'))[1];
var $codeFrame = $('#frame-code-' + id);
if ($codeFrame) {
$activeLine.removeClass('active');
$activeFrame.removeClass('active');
$this.addClass('active');
$codeFrame.addClass('active');
$activeLine = $this;
$activeFrame = $codeFrame;
renderCurrentCodeblock(id);
}
});
var clipboard = new ClipboardJS('.clipboard');
var showTooltip = function(elem, msg) {
elem.classList.add('tooltipped', 'tooltipped-s');
elem.setAttribute('aria-label', msg);
};
clipboard.on('success', function(e) {
e.clearSelection();
showTooltip(e.trigger, 'Copied!');
});
clipboard.on('error', function(e) {
showTooltip(e.trigger, fallbackMessage(e.action));
});
var btn = document.querySelector('.clipboard');
btn.addEventListener('mouseleave', function(e) {
e.currentTarget.classList.remove('tooltipped', 'tooltipped-s');
e.currentTarget.removeAttribute('aria-label');
});
function fallbackMessage(action) {
var actionMsg = '';
var actionKey = (action === 'cut' ? 'X' : 'C');
if (/Mac/i.test(navigator.userAgent)) {
actionMsg = 'Press ⌘-' + actionKey + ' to ' + action;
} else {
actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action;
}
return actionMsg;
}
function scrollIntoView($node, $parent) {
var nodeOffset = $node.offset();
var nodeTop = nodeOffset.top;
var nodeBottom = nodeTop + nodeOffset.height;
var parentScrollTop = $parent.scrollTop();
var parentHeight = $parent.height();
if (nodeTop < 0) {
$parent.scrollTop(parentScrollTop + nodeTop);
} else if (nodeBottom > parentHeight) {
$parent.scrollTop(parentScrollTop + nodeBottom - parentHeight);
}
}
$(document).on('keydown', function(e) {
var applicationFrames = $frameContainer.hasClass('frames-container-application'),
frameClass = applicationFrames ? '.frame.frame-application' : '.frame';
if(e.ctrlKey || e.which === 74 || e.which === 75) {
// CTRL+Arrow-UP/k and Arrow-Down/j support:
// 1) select the next/prev element
// 2) make sure the newly selected element is within the view-scope
// 3) focus the (right) container, so arrow-up/down (without ctrl) scroll the details
if (e.which === 38 /* arrow up */ || e.which === 75 /* k */) {
$activeLine.prev(frameClass).click();
scrollIntoView($activeLine, $leftPanel);
$container.focus();
e.preventDefault();
} else if (e.which === 40 /* arrow down */ || e.which === 74 /* j */) {
$activeLine.next(frameClass).click();
scrollIntoView($activeLine, $leftPanel);
$container.focus();
e.preventDefault();
}
} else if (e.which == 78 /* n */) {
if ($appFramesTab.length) {
setActiveFramesTab($('.frames-tab:not(.frames-tab-active)'));
}
}
});
// Avoid to quit the page with some protocol (e.g. IntelliJ Platform REST API)
$ajaxEditors.on('click', function(e){
e.preventDefault();
$.get(this.href);
});
// Symfony VarDumper: Close the by default expanded objects
$('.sf-dump-expanded')
.removeClass('sf-dump-expanded')
.addClass('sf-dump-compact');
$('.sf-dump-toggle span').html('▶');
// Make the given frames-tab active
function setActiveFramesTab($tab) {
$tab.addClass('frames-tab-active');
if ($tab.attr('id') == 'application-frames-tab') {
$frameContainer.addClass('frames-container-application');
$allFramesTab.removeClass('frames-tab-active');
} else {
$frameContainer.removeClass('frames-container-application');
$appFramesTab.removeClass('frames-tab-active');
}
}
$('a.frames-tab').on('click', function(e) {
e.preventDefault();
setActiveFramesTab($(this));
});
// Open editor from code block rows number
$(document).delegate('.line-numbers-rows > span', 'click', function(e) {
var linkTag = $(this).closest('.frame-code').find('.editor-link');
if (!linkTag) return;
var editorUrl = linkTag.attr('href');
var requiresAjax = linkTag.data('ajax');
var lineOffset = $(this).closest('[data-line-offset]').data('line-offset');
var lineNumber = lineOffset + $(this).index();
var realLine = $(this).closest('[data-line]').data('line');
if (!realLine) return;
var fileUrl = editorUrl.replace(
new RegExp('([:=])' + realLine),
'$1' + lineNumber
);
if (requiresAjax) {
$.get(fileUrl);
} else {
$('<a>').attr('href', fileUrl).trigger('click');
}
});
// Render late enough for highlightCurrentLine to be ready
renderCurrentCodeblock();
});
================================================
FILE: src/Whoops/Resources/views/env_details.html.php
================================================
<?php /* List data-table values, i.e: $_SERVER, $_GET, .... */ ?>
<div class="details">
<h2 class="details-heading">Environment & details:</h2>
<div class="data-table-container" id="data-tables">
<?php foreach ($tables as $label => $data): ?>
<div class="data-table" id="sg-<?php echo $tpl->escape($tpl->slug($label)) ?>">
<?php if (!empty($data)): ?>
<label><?php echo $tpl->escape($label) ?></label>
<table class="data-table">
<thead>
<tr>
<td class="data-table-k">Key</td>
<td class="data-table-v">Value</td>
</tr>
</thead>
<?php foreach ($data as $k => $value): ?>
<tr>
<td><?php echo $tpl->escape($k) ?></td>
<td><?php echo $tpl->dump($value) ?></td>
</tr>
<?php endforeach ?>
</table>
<?php else: ?>
<label class="empty"><?php echo $tpl->escape($label) ?></label>
<span class="empty">empty</span>
<?php endif ?>
</div>
<?php endforeach ?>
</div>
<?php /* List registered handlers, in order of first to last registered */ ?>
<div class="data-table-container" id="handlers">
<label>Registered Handlers</label>
<?php foreach ($handlers as $i => $h): ?>
<div class="handler <?php echo ($h === $handler) ? 'active' : ''?>">
<?php echo $i ?>. <?php echo $tpl->escape(get_class($h)) ?>
</div>
<?php endforeach ?>
</div>
</div>
================================================
FILE: src/Whoops/Resources/views/frame_code.html.php
================================================
<?php /* Display a code block for all frames in the stack.
* @todo: This should PROBABLY be done on-demand, lest
* we get 200 frames to process. */ ?>
<div class="frame-code-container <?php echo (!$has_frames ? 'empty' : '') ?>">
<?php foreach ($frames as $i => $frame): ?>
<?php $line = $frame->getLine(); ?>
<div class="frame-code <?php echo ($i == 0 ) ? 'active' : '' ?>" id="frame-code-<?php echo $i ?>">
<div class="frame-file">
<?php $filePath = $frame->getFile(); ?>
<?php if ($filePath && $editorHref = $handler->getEditorHref($filePath, (int) $line)): ?>
<a href="<?php echo $editorHref ?>" class="editor-link"<?php echo ($handler->getEditorAjax($filePath, (int) $line) ? ' data-ajax' : '') ?>>
Open:
<strong><?php echo $tpl->breakOnDelimiter('/', $tpl->escape($filePath ?: '<#unknown>')) ?></strong>
</a>
<?php else: ?>
<strong><?php echo $tpl->breakOnDelimiter('/', $tpl->escape($filePath ?: '<#unknown>')) ?></strong>
<?php endif ?>
</div>
<?php
// Do nothing if there's no line to work off
if ($line !== null):
// the $line is 1-indexed, we nab -1 where needed to account for this
$range = $frame->getFileLines($line - 20, 40);
// getFileLines can return null if there is no source code
if ($range):
$range = array_map(function ($line) { return empty($line) ? ' ' : $line;}, $range);
$start = key($range) + 1;
$code = join("\n", $range);
?>
<pre class="code-block line-numbers"
data-line="<?php echo $line ?>"
data-line-offset="<?php echo $start ?>"
data-start="<?php echo $start ?>"
><code class="language-php"><?php echo $tpl->escape($code) ?></code></pre>
<?php endif ?>
<?php endif ?>
<?php $frameArgs = $tpl->dumpArgs($frame); ?>
<?php if ($frameArgs): ?>
<div class="frame-file">
Arguments
</div>
<div id="frame-code-args-<?=$i?>" class="code-block frame-args">
<?php echo $frameArgs; ?>
</div>
<?php endif ?>
<?php
// Append comments for this frame
$comments = $frame->getComments();
?>
<div class="frame-comments <?php echo empty($comments) ? 'empty' : '' ?>">
<?php foreach ($comments as $commentNo => $comment): ?>
<?php extract($comment) ?>
<div class="frame-comment" id="comment-<?php echo $i . '-' . $commentNo ?>">
<span class="frame-comment-context"><?php echo $tpl->escape($context) ?></span>
<?php echo $tpl->escapeButPreserveUris($comment) ?>
</div>
<?php endforeach ?>
</div>
</div>
<?php endforeach ?>
</div>
================================================
FILE: src/Whoops/Resources/views/frame_list.html.php
================================================
<?php /* List file names & line numbers for all stack frames;
clicking these links/buttons will display the code view
for that particular frame */ ?>
<?php foreach ($frames as $i => $frame): ?>
<div class="frame <?php echo ($i == 0 ? 'active' : '') ?> <?php echo ($frame->isApplication() ? 'frame-application' : '') ?>" id="frame-line-<?php echo $i ?>">
<span class="frame-index"><?php echo (count($frames) - $i - 1) ?></span>
<div class="frame-method-info">
<span class="frame-class"><?php echo $tpl->breakOnDelimiter('\\', $tpl->escape($frame->getClass() ?: '')) ?></span>
<span class="frame-function"><?php echo $tpl->breakOnDelimiter('\\', $tpl->escape($frame->getFunction() ?: '')) ?></span>
</div>
<div class="frame-file">
<?php echo $frame->getFile() ? $tpl->breakOnDelimiter('/', $tpl->shorten($tpl->escape($frame->getFile()))) : '<#unknown>' ?><!--
--><span class="frame-line">:<?php echo (int) $frame->getLine() ?></span>
</div>
</div>
<?php endforeach;
================================================
FILE: src/Whoops/Resources/views/frames_container.html.php
================================================
<div class="frames-container <?php echo $active_frames_tab == 'application' ? 'frames-container-application' : '' ?>">
<?php $tpl->render($frame_list) ?>
</div>
================================================
FILE: src/Whoops/Resources/views/frames_description.html.php
================================================
<div class="frames-description <?php echo $has_frames_tabs ? 'frames-description-application' : '' ?>">
<?php if ($has_frames_tabs): ?>
<a href="#" id="application-frames-tab" class="frames-tab <?php echo $active_frames_tab == 'application' ? 'frames-tab-active' : '' ?>">
Application frames (<?php echo $frames->countIsApplication() ?>)
</a>
<a href="#" id="all-frames-tab" class="frames-tab <?php echo $active_frames_tab == 'all' ? 'frames-tab-active' : '' ?>">
All frames (<?php echo count($frames) ?>)
</a>
<?php else: ?>
<span>
Stack frames (<?php echo count($frames) ?>)
</span>
<?php endif; ?>
</div>
================================================
FILE: src/Whoops/Resources/views/header.html.php
================================================
<div class="exception">
<div class="exc-title">
<?php foreach ($name as $i => $nameSection): ?>
<?php if ($i == count($name) - 1): ?>
<span class="exc-title-primary"><?php echo $tpl->escape($nameSection) ?></span>
<?php else: ?>
<?php echo $tpl->escape($nameSection) . ' \\' ?>
<?php endif ?>
<?php endforeach ?>
<?php if ($code): ?>
<span title="Exception Code">(<?php echo $tpl->escape($code) ?>)</span>
<?php endif ?>
</div>
<div class="exc-message">
<?php if (!empty($message)): ?>
<span><?php echo $tpl->escape($message) ?></span>
<?php if (count($previousMessages)): ?>
<div class="exc-title prev-exc-title">
<span class="exc-title-secondary">Previous exceptions</span>
</div>
<ul>
<?php foreach ($previousMessages as $i => $previousMessage): ?>
<li>
<?php echo $tpl->escape($previousMessage) ?>
<span class="prev-exc-code">(<?php echo $previousCodes[$i] ?>)</span>
</li>
<?php endforeach; ?>
</ul>
<?php endif ?>
<?php else: ?>
<span class="exc-message-empty-notice">No message</span>
<?php endif ?>
<ul class="search-for-help">
<?php if (!empty($docref_url)): ?>
<li>
<a rel="noopener noreferrer" target="_blank" href="<?php echo $docref_url; ?>" title="Search for help in the PHP manual.">
<!-- PHP icon by Icons Solid -->
<!-- https://www.iconfinder.com/icons/322421/book_icon -->
<!-- Free for commercial use -->
<svg height="16px" id="Layer_1" style="enable-background:new 0 0 32 32;" version="1.1" viewBox="0 0 32 32" width="16px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(240 0)"><path d="M-211,4v26h-24c-1.104,0-2-0.895-2-2s0.896-2,2-2h22V0h-22c-2.209,0-4,1.791-4,4v24c0,2.209,1.791,4,4,4h26V4H-211z M-235,8V2h20v22h-20V8z M-219,6h-12V4h12V6z M-223,10h-8V8h8V10z M-227,14h-4v-2h4V14z"/></g></svg>
</a>
</li>
<?php endif ?>
<li>
<a rel="noopener noreferrer" target="_blank" href="https://google.com/search?q=<?php echo urlencode(implode('\\', $name).' '.$message) ?>" title="Search for help on Google.">
<!-- Google icon by Alfredo H, from https://www.iconfinder.com/alfredoh -->
<!-- Creative Commons (Attribution 3.0 Unported) -->
<!-- http://creativecommons.org/licenses/by/3.0/ -->
<svg class="google" height="16" viewBox="0 0 512 512" width="16" xmlns="http://www.w3.org/2000/svg">
<path d="M457.732 216.625c2.628 14.04 4.063 28.743 4.063 44.098C461.795 380.688 381.48 466 260.205 466c-116.024 0-210-93.977-210-210s93.976-210 210-210c56.703 0 104.076 20.867 140.44 54.73l-59.205 59.197v-.135c-22.046-21.002-50-31.762-81.236-31.762-69.297 0-125.604 58.537-125.604 127.84 0 69.29 56.306 127.97 125.604 127.97 62.87 0 105.653-35.966 114.46-85.313h-114.46v-81.902h197.528z"/>
</svg>
</a>
</li>
<li>
<a rel="noopener noreferrer" target="_blank" href="https://duckduckgo.com/?q=<?php echo urlencode(implode('\\', $name).' '.$message) ?>" title="Search for help on DuckDuckGo.">
<!-- DuckDuckGo icon by IconBaandar Team, from https://www.iconfinder.com/iconbaandar -->
<!-- Creative Commons (Attribution 3.0 Unported) -->
<!-- http://creativecommons.org/licenses/by/3.0/ -->
<svg class="duckduckgo" height="16" viewBox="150 150 1675 1675" width="16" xmlns="http://www.w3.org/2000/svg">
<path d="M1792 1024c0 204.364-80.472 398.56-224.955 543.04-144.483 144.48-338.68 224.95-543.044 224.95-204.36 0-398.56-80.47-543.04-224.95-144.48-144.482-224.95-338.676-224.95-543.04 0-204.365 80.47-398.562 224.96-543.045C625.44 336.47 819.64 256 1024 256c204.367 0 398.565 80.47 543.05 224.954C1711.532 625.437 1792 819.634 1792 1024zm-270.206 497.787C1654.256 1389.327 1728 1211.36 1728 1024c0-187.363-73.74-365.332-206.203-497.796C1389.332 393.74 1211.363 320 1024 320s-365.33 73.742-497.795 206.205C393.742 658.67 320 836.637 320 1024c0 187.36 73.744 365.326 206.206 497.787C658.67 1654.25 836.638 1727.99 1024 1727.99c187.362 0 365.33-73.74 497.794-206.203z"/>
<path d="M1438.64 1177.41c0-.03-.005-.017-.01.004l.01-.004z"/>
<path d="M1499.8 976.878c.03-.156-.024-.048-.11.107l.11-.107z"/>
<path d="M1105.19 991.642zm-68.013-376.128c-8.087-10.14-18.028-19.965-29.89-29.408-13.29-10.582-29-20.76-47.223-30.443-35.07-18.624-74.482-31.61-115.265-38.046-39.78-6.28-80.84-6.256-120.39.917l1.37 31.562c1.8.164 7.7 3.9 14.36 8.32-20.68 5.94-39.77 14.447-39.48 39.683l.2 17.48 17.3-1.73c29.38-2.95 60.17-2.06 90.32 2.61 9.21 1.42 18.36 3.2 27.38 5.32l-4.33 1.15c-20.45 5.58-38.93 12.52-54.25 20.61-46.28 24.32-75.51 60.85-90.14 108.37-14.14 45.95-14.27 101.81-2.72 166.51l.06.06c15.14 84.57 64.16 316.39 104.11 505.39 19.78 93.59 37.38 176.83 47.14 224.4 3.26 15.84 5.03 31.02 5.52 45.52.3 9.08.09 17.96-.58 26.62-.45 5.8-1.11 11.51-1.96 17.112l31.62 4.75c.71-4.705 1.3-9.494 1.76-14.373 48.964 10.517 99.78 16.05 151.88 16.05 60.68 0 119.61-7.505 175.91-21.64 3.04 6.08 6.08 12.19 9.11 18.32l28.62-14.128c-2.11-4.27-4.235-8.55-6.37-12.84-23.005-46.124-47.498-93.01-68.67-133.534-15.39-29.466-29.01-55.53-39.046-75.58-26.826-53.618-53.637-119.47-68.28-182.368-8.78-37.705-13.128-74.098-10.308-105.627-15.31-6.28-26.69-11.8-31.968-15.59l-.01.015c-14.22-10.2-31.11-28.12-41.82-49.717-8.618-17.376-13.4-37.246-10.147-57.84 3.17-19.84 27.334-46.714 57.843-67.46v-.063c26.554-18.05 58.75-32.506 86.32-34.31 7.835-.51 16.31-1.008 23.99-1.45 33.45-1.95 50.243-2.93 84.475-11.42 10.88-2.697 26.19-6.56 43.53-11.09 2.364-40.7-5.947-87.596-21.04-133.234-22.004-66.53-58.68-131.25-97.627-170.21-12.543-12.55-28.17-22.79-45.9-30.933-16.88-7.753-35.64-13.615-55.436-17.782zm-10.658 178.553s6.77-42.485 58.39-33.977c27.96 4.654 37.89 29.833 37.89 29.833s-25.31-14.46-44.95-14.198c-40.33.53-51.35 18.342-51.35 18.342zm-240.45-18.802c48.49-19.853 72.11 11.298 72.11 11.298s-35.21-15.928-69.46 5.59c-34.19 21.477-32.92 43.452-32.92 43.452s-18.17-40.5 30.26-60.34zm296.5 95.4c0-6.677 2.68-12.694 7.01-17.02 4.37-4.37 10.42-7.074 17.1-7.074 6.73 0 12.79 2.7 17.15 7.05 4.33 4.33 7.01 10.36 7.01 17.05 0 6.74-2.7 12.81-7.07 17.18-4.33 4.33-10.37 7.01-17.1 7.01-6.68 0-12.72-2.69-17.05-7.03-4.36-4.37-7.07-10.43-7.07-17.16zm-268.42 51.27c0-8.535 3.41-16.22 8.93-21.738 5.55-5.55 13.25-8.982 21.81-8.982 8.51 0 16.18 3.415 21.7 8.934 5.55 5.55 8.98 13.25 8.98 21.78 0 8.53-3.44 16.23-8.98 21.79-5.52 5.52-13.19 8.93-21.71 8.93-8.55 0-16.26-3.43-21.82-8.99-5.52-5.52-8.93-13.2-8.93-21.74z"/>
<path d="M1102.48 986.34zm390.074-64.347c-28.917-11.34-74.89-12.68-93.32-3.778-11.5 5.567-35.743 13.483-63.565 21.707-25.75 7.606-53.9 15.296-78.15 21.702-17.69 4.67-33.3 8.66-44.4 11.435-34.92 8.76-52.05 9.77-86.17 11.78-7.84.46-16.48.97-24.48 1.5-28.12 1.86-60.97 16.77-88.05 35.4v.06c-31.12 21.4-55.77 49.12-59.01 69.59-3.32 21.24 1.56 41.74 10.35 59.67 10.92 22.28 28.15 40.77 42.66 51.29l.01-.02c5.38 3.9 16.98 9.6 32.6 16.08 26.03 10.79 63.2 23.76 101.25 34.23 43.6 11.99 89.11 21.05 121.69 20.41 34.26-.69 77.73-10.52 114.54-24.67 22.15-8.52 42.21-18.71 56.88-29.58 17.85-13.22 28.7-28.42 28.4-44.74-.07-3.89-.72-7.63-1.97-11.21l-.02.01c-11.6-33.06-50.37-23.59-105.53-10.12-46.86 11.445-107.94 26.365-169.01 20.434-32.56-3.167-54.45-10.61-67.88-20.133-5.96-4.224-9.93-8.67-12.18-13.11-1.96-3.865-2.68-7.84-2.33-11.714.39-4.42 2.17-9.048 5.1-13.57l-.05-.03c7.86-12.118 23.082-9.72 43.93-6.43 25.91 4.08 58.2 9.172 99.013-3.61 39.63-12.378 87.76-29.9 131.184-47.39 42.405-17.08 80.08-34.078 100.74-46.18 25.46-14.87 37.57-29.428 40.59-42.866 2.725-12.152-.89-22.48-8.903-31.07-5.87-6.29-14.254-11.31-23.956-15.115z"/>
</svg>
</a>
</li>
<li>
<a rel="noopener noreferrer" target="_blank" href="https://stackoverflow.com/search?q=<?php echo urlencode(implode('\\', $name).' '.$message) ?>" title="Search for help on Stack Overflow.">
<!-- Stack Overflow icon by Picons.me, from https://www.iconfinder.com/Picons -->
<!-- Free for commercial use -->
<svg class="stackoverflow" height="16" viewBox="-1163 1657.697 56.693 56.693" width="16" xmlns="http://www.w3.org/2000/svg">
<path d="M-1126.04 1689.533l-16.577-9.778 2.088-3.54 16.578 9.778zM-1127.386 1694.635l-18.586-4.996 1.068-3.97 18.586 4.995zM-1127.824 1700.137l-19.165-1.767.378-4.093 19.165 1.767zM-1147.263 1701.293h19.247v4.11h-19.247z"/>
<path d="M-1121.458 1710.947s0 .96-.032.96v.016h-30.796s-.96 0-.96-.016h-.032v-20.03h3.288v16.805h25.244v-16.804h3.288v19.07zM-1130.667 1667.04l10.844 15.903-3.396 2.316-10.843-15.903zM-1118.313 1663.044l3.29 18.963-4.05.703-3.29-18.963z"/>
</svg>
</a>
</li>
</ul>
<span id="plain-exception"><?php echo $tpl->escape($plain_exception) ?></span>
<button id="copy-button" class="rightButton clipboard" data-clipboard-text="<?php echo $tpl->escape($plain_exception) ?>" title="Copy exception details to clipboard">
COPY
</button>
<button id="hide-error" class="rightButton" title="Hide error message" onclick="document.getElementsByClassName('Whoops')[0].style.display = 'none';">
HIDE
</button>
</div>
</div>
================================================
FILE: src/Whoops/Resources/views/header_outer.html.php
================================================
<header>
<?php $tpl->render($header) ?>
</header>
================================================
FILE: src/Whoops/Resources/views/layout.html.php
================================================
<?php
/**
* Layout template file for Whoops's pretty error output.
*/
?>
<!DOCTYPE html><?php echo $preface; ?>
<html>
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<title><?php echo $tpl->escape($page_title) ?></title>
<style><?php echo $stylesheet ?></style>
<style><?php echo $prismCss ?></style>
</head>
<body>
<div class="Whoops container">
<div class="stack-container">
<?php $tpl->render($panel_left_outer) ?>
<?php $tpl->render($panel_details_outer) ?>
</div>
</div>
<script data-manual><?php echo $prismJs ?></script>
<script><?php echo $zepto ?></script>
<script><?php echo $clipboard ?></script>
<script><?php echo $javascript ?></script>
</body>
</html>
================================================
FILE: src/Whoops/Resources/views/panel_details.html.php
================================================
<?php $tpl->render($frame_code) ?>
<?php $tpl->render($env_details) ?>
================================================
FILE: src/Whoops/Resources/views/panel_details_outer.html.php
================================================
<div class="panel details-container cf">
<?php $tpl->render($panel_details) ?>
</div>
================================================
FILE: src/Whoops/Resources/views/panel_left.html.php
================================================
<?php
$tpl->render($header_outer);
$tpl->render($frames_description);
$tpl->render($frames_container);
================================================
FILE: src/Whoops/Resources/views/panel_left_outer.html.php
================================================
<div class="panel left-panel cf <?php echo (!$has_frames ? 'empty' : '') ?>">
<?php $tpl->render($panel_left) ?>
</div>
================================================
FILE: src/Whoops/Run.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops;
use InvalidArgumentException;
use Throwable;
use Whoops\Exception\ErrorException;
use Whoops\Handler\CallbackHandler;
use Whoops\Handler\Handler;
use Whoops\Handler\HandlerInterface;
use Whoops\Inspector\CallableInspectorFactory;
use Whoops\Inspector\InspectorFactory;
use Whoops\Inspector\InspectorFactoryInterface;
use Whoops\Inspector\InspectorInterface;
use Whoops\Util\Misc;
use Whoops\Util\SystemFacade;
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;
/**
* The inspector factory to create inspectors.
*
* @var InspectorFactoryInterface
*/
private $inspectorFactory;
/**
* @var array<callable>
*/
private $frameFilters = [];
public function __construct(?SystemFacade $system = null)
{
$this->system = $system ?: new SystemFacade;
$this->inspectorFactory = new InspectorFactory();
}
public function __destruct()
{
$this->unregister();
}
/**
* 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;
}
public function getFrameFilters()
{
return $this->frameFilters;
}
public function clearFrameFilters()
{
$this->frameFilters = [];
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");
class_exists("\\Whoops\\Inspector\\InspectorFactory");
$this->system->setErrorHandler([$this, self::ERROR_HANDLER]);
$this->system->setExceptionHandler([$this, self::EXCEPTION_HANDLER]);
$this->system->registerShutdownFunction([$this, self::SHUTDOWN_HANDLER]);
$this->isRegistered = true;
}
return $this;
}
/**
* Unregisters all handlers registered by this Whoops\Run 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 < 0 || 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_HANDLER, Handler::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;
// If we are not currently registered, we should not do anything
if (!$this->isRegistered) {
return;
}
$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 InspectorFactoryInterface $factory
*
* @return void
*/
public function setInspectorFactory(InspectorFactoryInterface $factory)
{
$this->inspectorFactory = $factory;
}
public function addFrameFilter($filterCallback)
{
if (!is_callable($filterCallback)) {
throw new \InvalidArgumentException(sprintf(
"A frame filter must be of type callable, %s type given.",
gettype($filterCallback)
));
}
$this->frameFilters[] = $filterCallback;
return $this;
}
/**
* @param Throwable $exception
*
* @return InspectorInterface
*/
private function getInspector($exception)
{
return $this->inspectorFactory->create($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;
}
}
================================================
FILE: src/Whoops/RunInterface.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops;
use InvalidArgumentException;
use Whoops\Exception\ErrorException;
use Whoops\Handler\HandlerInterface;
interface RunInterface
{
const EXCEPTION_HANDLER = "handleException";
const ERROR_HANDLER = "handleError";
const SHUTDOWN_HANDLER = "handleShutdown";
/**
* Pushes a handler to the end of the stack
*
* @throws InvalidArgumentException If argument is not callable or instance of HandlerInterface
* @param Callable|HandlerInterface $handler
* @return Run
*/
public function pushHandler($handler);
/**
* Removes the last handler in the stack and returns it.
* Returns null if there"s nothing else to pop.
*
* @return null|HandlerInterface
*/
public function popHandler();
/**
* Returns an array with all handlers, in the
* order they were added to the stack.
*
* @return array
*/
public function getHandlers();
/**
* Clears all handlers in the handlerStack, including
* the default PrettyPage handler.
*
* @return Run
*/
public function clearHandlers();
/**
* @return array<callable>
*/
public function getFrameFilters();
/**
* @return Run
*/
public function clearFrameFilters();
/**
* Registers this instance as an error handler.
*
* @return Run
*/
public function register();
/**
* Unregisters all handlers registered by this Whoops\Run instance
*
* @return Run
*/
public function unregister();
/**
* Should Whoops allow Handlers to force the script to quit?
*
* @param bool|int $exit
* @return bool
*/
public function allowQuit($exit = null);
/**
* 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 \Whoops\Run
*/
public function silenceErrorsInPaths($patterns, $levels = 10240);
/**
* 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
*/
public function sendHttpCode($code = null);
/**
* 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
*/
public function sendExitCode($code = null);
/**
* 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);
/**
* Handles an exception, ultimately generating a Whoops error
* page.
*
* @param \Throwable $exception
* @return string Output generated by handlers
*/
public function handleException($exception);
/**
* 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 $file
* @param int $line
*
* @return bool
* @throws ErrorException
*/
public function handleError($level, $message, $file = null, $line = null);
/**
* Special case to deal with Fatal errors and the like.
*/
public function handleShutdown();
/**
* Registers a filter callback in the frame filters stack.
*
* @param callable $filterCallback
* @return \Whoops\Run
*/
public function addFrameFilter($filterCallback);
}
================================================
FILE: src/Whoops/Util/HtmlDumperOutput.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Util;
/**
* Used as output callable for Symfony\Component\VarDumper\Dumper\HtmlDumper::dump()
*
* @see TemplateHelper::dump()
*/
class HtmlDumperOutput
{
private $output;
public function __invoke($line, $depth)
{
// A negative depth means "end of dump"
if ($depth >= 0) {
// Adds a two spaces indentation to the line
$this->output .= str_repeat(' ', $depth) . $line . "\n";
}
}
public function getOutput()
{
return $this->output;
}
public function clear()
{
$this->output = null;
}
}
================================================
FILE: src/Whoops/Util/Misc.php
================================================
<?php
/**
* Whoops - php errors for cool kids
* @au
gitextract_qw9s0v9n/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── tests.yml
├── .gitignore
├── .mailmap
├── .scrutinizer.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── SECURITY.md
├── composer.json
├── docs/
│ ├── API Documentation.md
│ ├── Framework Integration.md
│ ├── Open Files In An Editor.md
│ └── Replay Errors.md
├── examples/
│ ├── example-ajax-only.php
│ ├── example.php
│ └── lib.php
├── phpunit.xml.dist
├── src/
│ └── Whoops/
│ ├── Exception/
│ │ ├── ErrorException.php
│ │ ├── Formatter.php
│ │ ├── Frame.php
│ │ ├── FrameCollection.php
│ │ └── Inspector.php
│ ├── Handler/
│ │ ├── CallbackHandler.php
│ │ ├── Handler.php
│ │ ├── HandlerInterface.php
│ │ ├── JsonResponseHandler.php
│ │ ├── PlainTextHandler.php
│ │ ├── PrettyPageHandler.php
│ │ └── XmlResponseHandler.php
│ ├── Inspector/
│ │ ├── InspectorFactory.php
│ │ ├── InspectorFactoryInterface.php
│ │ └── InspectorInterface.php
│ ├── Resources/
│ │ ├── css/
│ │ │ ├── prism.css
│ │ │ └── whoops.base.css
│ │ ├── js/
│ │ │ ├── prism.js
│ │ │ └── whoops.base.js
│ │ └── views/
│ │ ├── env_details.html.php
│ │ ├── frame_code.html.php
│ │ ├── frame_list.html.php
│ │ ├── frames_container.html.php
│ │ ├── frames_description.html.php
│ │ ├── header.html.php
│ │ ├── header_outer.html.php
│ │ ├── layout.html.php
│ │ ├── panel_details.html.php
│ │ ├── panel_details_outer.html.php
│ │ ├── panel_left.html.php
│ │ └── panel_left_outer.html.php
│ ├── Run.php
│ ├── RunInterface.php
│ └── Util/
│ ├── HtmlDumperOutput.php
│ ├── Misc.php
│ ├── SystemFacade.php
│ └── TemplateHelper.php
└── tests/
├── Whoops/
│ ├── Exception/
│ │ ├── FormatterTest.php
│ │ ├── FrameCollectionTest.php
│ │ ├── FrameTest.php
│ │ └── InspectorTest.php
│ ├── Handler/
│ │ ├── CallbackHandlerTest.php
│ │ ├── JsonResponseHandlerTest.php
│ │ ├── PlainTextHandlerTest.php
│ │ ├── PrettyPageHandlerTest.php
│ │ └── XmlResponseHandlerTest.php
│ ├── RunTest.php
│ ├── TestCase.php
│ └── Util/
│ ├── HtmlDumperOutputTest.php
│ ├── MiscTest.php
│ ├── SystemFacadeTest.php
│ └── TemplateHelperTest.php
├── bootstrap.php
└── fixtures/
├── frame.lines-test.php
└── template.php
SYMBOL INDEX (449 symbols across 40 files)
FILE: examples/example.php
class Exception (line 25) | class Exception extends BaseException
function fooBar (line 71) | function fooBar()
function bar (line 76) | function bar()
FILE: examples/lib.php
function whoops_add_stack_frame (line 2) | function whoops_add_stack_frame($callback){
FILE: src/Whoops/Exception/ErrorException.php
class ErrorException (line 15) | class ErrorException extends BaseErrorException
FILE: src/Whoops/Exception/Formatter.php
class Formatter (line 11) | class Formatter
method formatExceptionAsDataArray (line 21) | public static function formatExceptionAsDataArray(InspectorInterface $...
method formatExceptionPlain (line 53) | public static function formatExceptionPlain(InspectorInterface $inspec...
FILE: src/Whoops/Exception/Frame.php
class Frame (line 12) | class Frame implements Serializable
method __construct (line 34) | public function __construct(array $frame)
method getFile (line 43) | public function getFile($shortened = false)
method getLine (line 75) | public function getLine()
method getClass (line 83) | public function getClass()
method getFunction (line 91) | public function getFunction()
method getArgs (line 99) | public function getArgs()
method getFileContents (line 109) | public function getFileContents()
method addComment (line 140) | public function addComment($comment, $context = 'global')
method getComments (line 156) | public function getComments($filter = null)
method getRawFrame (line 175) | public function getRawFrame()
method getFileLines (line 198) | public function getFileLines($start = 0, $length = null)
method serialize (line 231) | public function serialize()
method __serialize (line 241) | public function __serialize()
method unserialize (line 257) | public function unserialize($serializedFrame)
method __unserialize (line 269) | public function __unserialize($frame)
method equals (line 284) | public function equals(Frame $frame)
method isApplication (line 297) | public function isApplication()
method setApplication (line 307) | public function setApplication($application)
FILE: src/Whoops/Exception/FrameCollection.php
class FrameCollection (line 21) | class FrameCollection implements ArrayAccess, IteratorAggregate, Seriali...
method __construct (line 28) | public function __construct(array $frames)
method filter (line 41) | public function filter($callable)
method map (line 53) | public function map($callable)
method getArray (line 81) | public function getArray()
method getIterator (line 90) | #[ReturnTypeWillChange]
method offsetExists (line 100) | #[ReturnTypeWillChange]
method offsetGet (line 110) | #[ReturnTypeWillChange]
method offsetSet (line 120) | #[ReturnTypeWillChange]
method offsetUnset (line 130) | #[ReturnTypeWillChange]
method count (line 140) | #[ReturnTypeWillChange]
method countIsApplication (line 151) | public function countIsApplication()
method serialize (line 162) | #[ReturnTypeWillChange]
method unserialize (line 172) | #[ReturnTypeWillChange]
method __serialize (line 178) | public function __serialize()
method __unserialize (line 183) | public function __unserialize(array $serializedFrames)
method prependFrames (line 191) | public function prependFrames(array $frames)
method topDiff (line 202) | public function topDiff(FrameCollection $parentFrames)
FILE: src/Whoops/Exception/Inspector.php
class Inspector (line 13) | class Inspector implements InspectorInterface
method __construct (line 44) | public function __construct($exception, $factory = null)
method getException (line 53) | public function getException()
method getExceptionName (line 61) | public function getExceptionName()
method getExceptionMessage (line 69) | public function getExceptionMessage()
method getPreviousExceptionMessages (line 77) | public function getPreviousExceptionMessages()
method getPreviousExceptionCodes (line 88) | public function getPreviousExceptionCodes()
method getExceptionDocrefUrl (line 101) | public function getExceptionDocrefUrl()
method extractDocrefUrl (line 106) | private function extractDocrefUrl($message)
method hasPreviousException (line 133) | public function hasPreviousException()
method getPreviousExceptionInspector (line 143) | public function getPreviousExceptionInspector()
method getPreviousExceptions (line 161) | public function getPreviousExceptions()
method getFrames (line 184) | public function getFrames(array $frameFilters = [])
method getTrace (line 260) | protected function getTrace($e)
method getFrameFromException (line 291) | protected function getFrameFromException($exception)
method getFrameFromError (line 309) | protected function getFrameFromError(ErrorException $exception)
method isValidNextFrame (line 325) | protected function isValidNextFrame(array $frame)
FILE: src/Whoops/Handler/CallbackHandler.php
class CallbackHandler (line 16) | class CallbackHandler extends Handler
method __construct (line 27) | public function __construct($callable)
method handle (line 41) | public function handle()
FILE: src/Whoops/Handler/Handler.php
class Handler (line 15) | abstract class Handler implements HandlerInterface
method setRun (line 51) | public function setRun(RunInterface $run)
method getRun (line 59) | protected function getRun()
method setInspector (line 67) | public function setInspector(InspectorInterface $inspector)
method getInspector (line 75) | protected function getInspector()
method setException (line 83) | public function setException($exception)
method getException (line 91) | protected function getException()
FILE: src/Whoops/Handler/HandlerInterface.php
type HandlerInterface (line 12) | interface HandlerInterface
method handle (line 17) | public function handle();
method setRun (line 23) | public function setRun(RunInterface $run);
method setException (line 29) | public function setException($exception);
method setInspector (line 35) | public function setInspector(InspectorInterface $inspector);
FILE: src/Whoops/Handler/JsonResponseHandler.php
class JsonResponseHandler (line 16) | class JsonResponseHandler extends Handler
method setJsonApi (line 33) | public function setJsonApi($jsonApi = false)
method addTraceToOutput (line 43) | public function addTraceToOutput($returnFrames = null)
method handle (line 56) | public function handle()
method contentType (line 86) | public function contentType()
FILE: src/Whoops/Handler/PlainTextHandler.php
class PlainTextHandler (line 20) | class PlainTextHandler extends Handler
method __construct (line 64) | public function __construct($logger = null)
method setLogger (line 74) | public function setLogger($logger = null)
method getLogger (line 91) | public function getLogger()
method setDumper (line 102) | public function setDumper(callable $dumper)
method addTraceToOutput (line 113) | public function addTraceToOutput($addTraceToOutput = null)
method addPreviousToOutput (line 128) | public function addPreviousToOutput($addPreviousToOutput = null)
method addTraceFunctionArgsToOutput (line 144) | public function addTraceFunctionArgsToOutput($addTraceFunctionArgsToOu...
method setTraceFunctionArgsOutputLimit (line 165) | public function setTraceFunctionArgsOutputLimit($traceFunctionArgsOutp...
method generateResponse (line 175) | public function generateResponse()
method getTraceFunctionArgsOutputLimit (line 198) | public function getTraceFunctionArgsOutputLimit()
method loggerOnly (line 208) | public function loggerOnly($loggerOnly = null)
method canOutput (line 222) | private function canOutput()
method getFrameArgsOutput (line 233) | private function getFrameArgsOutput(Frame $frame, $line)
method dump (line 266) | protected function dump($var)
method getTraceOutput (line 279) | private function getTraceOutput()
method getExceptionOutput (line 321) | private function getExceptionOutput($exception)
method handle (line 335) | public function handle()
method contentType (line 355) | public function contentType()
FILE: src/Whoops/Handler/PrettyPageHandler.php
class PrettyPageHandler (line 18) | class PrettyPageHandler extends Handler
method __construct (line 139) | public function __construct()
method handle (line 185) | public function handle()
method getExceptionFrames (line 308) | protected function getExceptionFrames()
method getExceptionCode (line 331) | protected function getExceptionCode()
method contentType (line 347) | public function contentType()
method addDataTable (line 362) | public function addDataTable($label, array $data)
method addDataTableCallback (line 382) | public function addDataTableCallback($label, /* callable */ $callback)
method getDataTables (line 413) | public function getDataTables($label = null)
method handleUnconditionally (line 434) | public function handleUnconditionally($value = null)
method addEditor (line 464) | public function addEditor($identifier, $resolver)
method setEditor (line 487) | public function setEditor($editor)
method getEditorHref (line 510) | public function getEditorHref($filePath, $line)
method getEditorAjax (line 542) | public function getEditorAjax($filePath, $line)
method getEditor (line 563) | protected function getEditor($filePath, $line)
method setPageTitle (line 610) | public function setPageTitle($title)
method getPageTitle (line 621) | public function getPageTitle()
method addResourcePath (line 635) | public function addResourcePath($path)
method addCustomCss (line 654) | public function addCustomCss($name)
method addCustomJs (line 667) | public function addCustomJs($name)
method getResourcePaths (line 676) | public function getResourcePaths()
method getResource (line 694) | protected function getResource($resource)
method getResourcesPath (line 726) | public function getResourcesPath()
method setResourcesPath (line 741) | public function setResourcesPath($resourcesPath)
method getApplicationPaths (line 752) | public function getApplicationPaths()
method setApplicationPaths (line 762) | public function setApplicationPaths(array $applicationPaths)
method setApplicationRootPath (line 774) | public function setApplicationRootPath($applicationRootPath)
method blacklist (line 789) | public function blacklist($superGlobalName, $key)
method hideSuperglobalKey (line 802) | public function hideSuperglobalKey($superGlobalName, $key)
method masked (line 820) | private function masked($superGlobal, $superGlobalName)
FILE: src/Whoops/Handler/XmlResponseHandler.php
class XmlResponseHandler (line 17) | class XmlResponseHandler extends Handler
method addTraceToOutput (line 28) | public function addTraceToOutput($returnFrames = null)
method handle (line 41) | public function handle()
method contentType (line 59) | public function contentType()
method addDataToNode (line 69) | private static function addDataToNode(\SimpleXMLElement $node, $data)
method toXml (line 100) | private static function toXml($data)
FILE: src/Whoops/Inspector/InspectorFactory.php
class InspectorFactory (line 11) | class InspectorFactory implements InspectorFactoryInterface
method create (line 17) | public function create($exception)
FILE: src/Whoops/Inspector/InspectorFactoryInterface.php
type InspectorFactoryInterface (line 9) | interface InspectorFactoryInterface
method create (line 15) | public function create($exception);
FILE: src/Whoops/Inspector/InspectorInterface.php
type InspectorInterface (line 9) | interface InspectorInterface
method getException (line 14) | public function getException();
method getExceptionName (line 19) | public function getExceptionName();
method getExceptionMessage (line 24) | public function getExceptionMessage();
method getPreviousExceptionMessages (line 29) | public function getPreviousExceptionMessages();
method getPreviousExceptionCodes (line 34) | public function getPreviousExceptionCodes();
method getExceptionDocrefUrl (line 41) | public function getExceptionDocrefUrl();
method hasPreviousException (line 47) | public function hasPreviousException();
method getPreviousExceptionInspector (line 54) | public function getPreviousExceptionInspector();
method getPreviousExceptions (line 60) | public function getPreviousExceptions();
method getFrames (line 70) | public function getFrames(array $frameFilters = []);
FILE: src/Whoops/Resources/js/prism.js
function u (line 3) | function u(e){s.highlightedCode=e,a.hooks.run("before-insert",s),s.eleme...
function i (line 3) | function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=...
function l (line 3) | function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a...
function o (line 3) | function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var...
function s (line 3) | function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e...
function u (line 3) | function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a...
function c (line 3) | function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.ne...
function f (line 3) | function f(){a.manual||a.highlightAll()}
function n (line 5) | function n(e,n){return"___"+e.toUpperCase()+n+"___"}
function r (line 7) | function r(e){return+e.substr(0,e.length-2)}
function s (line 7) | function s(e,t){return Array.prototype.slice.call((t||document).querySel...
function l (line 7) | function l(e,t){return e.classList.contains(t)}
function a (line 7) | function a(e){e()}
function u (line 7) | function u(e){return!!(e&&/pre/i.test(e.nodeName)&&(e.hasAttribute("data...
function c (line 7) | function c(){var e=location.hash.slice(1);s(".temporary.line-highlight")...
function r (line 8) | function r(e){if(0!=(e=e.filter((function(e){var n,t=(n=e,n?window.getCo...
FILE: src/Whoops/Resources/js/whoops.base.js
function fallbackMessage (line 105) | function fallbackMessage(action) {
function scrollIntoView (line 118) | function scrollIntoView($node, $parent) {
function setActiveFramesTab (line 172) | function setActiveFramesTab($tab) {
FILE: src/Whoops/Run.php
class Run (line 22) | final class Run implements RunInterface
method __construct (line 84) | public function __construct(?SystemFacade $system = null)
method __destruct (line 90) | public function __destruct()
method appendHandler (line 102) | public function appendHandler($handler)
method prependHandler (line 115) | public function prependHandler($handler)
method pushHandler (line 130) | public function pushHandler($handler)
method popHandler (line 143) | public function popHandler()
method removeFirstHandler (line 153) | public function removeFirstHandler()
method removeLastHandler (line 163) | public function removeLastHandler()
method getHandlers (line 173) | public function getHandlers()
method clearHandlers (line 183) | public function clearHandlers()
method getFrameFilters (line 189) | public function getFrameFilters()
method clearFrameFilters (line 194) | public function clearFrameFilters()
method register (line 205) | public function register()
method unregister (line 231) | public function unregister()
method allowQuit (line 250) | public function allowQuit($exit = null)
method silenceErrorsInPaths (line 267) | public function silenceErrorsInPaths($patterns, $levels = 10240)
method getSilenceErrorsInPaths (line 290) | public function getSilenceErrorsInPaths()
method sendHttpCode (line 306) | public function sendHttpCode($code = null)
method sendExitCode (line 339) | public function sendExitCode($code = null)
method writeToOutput (line 362) | public function writeToOutput($send = null)
method handleException (line 378) | public function handleException($exception)
method handleError (line 468) | public function handleError($level, $message, $file = null, $line = null)
method handleShutdown (line 503) | public function handleShutdown()
method setInspectorFactory (line 535) | public function setInspectorFactory(InspectorFactoryInterface $factory)
method addFrameFilter (line 540) | public function addFrameFilter($filterCallback)
method getInspector (line 558) | private function getInspector($exception)
method resolveHandler (line 572) | private function resolveHandler($handler)
method writeToOutputNow (line 595) | private function writeToOutputNow($output)
FILE: src/Whoops/RunInterface.php
type RunInterface (line 13) | interface RunInterface
method pushHandler (line 26) | public function pushHandler($handler);
method popHandler (line 34) | public function popHandler();
method getHandlers (line 42) | public function getHandlers();
method clearHandlers (line 50) | public function clearHandlers();
method getFrameFilters (line 55) | public function getFrameFilters();
method clearFrameFilters (line 60) | public function clearFrameFilters();
method register (line 67) | public function register();
method unregister (line 74) | public function unregister();
method allowQuit (line 82) | public function allowQuit($exit = null);
method silenceErrorsInPaths (line 91) | public function silenceErrorsInPaths($patterns, $levels = 10240);
method sendHttpCode (line 101) | public function sendHttpCode($code = null);
method sendExitCode (line 110) | public function sendExitCode($code = null);
method writeToOutput (line 119) | public function writeToOutput($send = null);
method handleException (line 128) | public function handleException($exception);
method handleError (line 144) | public function handleError($level, $message, $file = null, $line = nu...
method handleShutdown (line 149) | public function handleShutdown();
method addFrameFilter (line 157) | public function addFrameFilter($filterCallback);
FILE: src/Whoops/Util/HtmlDumperOutput.php
class HtmlDumperOutput (line 14) | class HtmlDumperOutput
method __invoke (line 18) | public function __invoke($line, $depth)
method getOutput (line 27) | public function getOutput()
method clear (line 32) | public function clear()
FILE: src/Whoops/Util/Misc.php
class Misc (line 9) | class Misc
method canSendHeaders (line 21) | public static function canSendHeaders()
method isAjaxRequest (line 26) | public static function isAjaxRequest()
method isCommandLine (line 37) | public static function isCommandLine()
method translateErrorCode (line 48) | public static function translateErrorCode($error_code)
method isLevelFatal (line 67) | public static function isLevelFatal($level)
FILE: src/Whoops/Util/SystemFacade.php
class SystemFacade (line 9) | class SystemFacade
method startOutputBuffering (line 16) | public function startOutputBuffering()
method setErrorHandler (line 27) | public function setErrorHandler(callable $handler, $types = 'use-php-d...
method setExceptionHandler (line 41) | public function setExceptionHandler(callable $handler)
method restoreExceptionHandler (line 49) | public function restoreExceptionHandler()
method restoreErrorHandler (line 57) | public function restoreErrorHandler()
method registerShutdownFunction (line 67) | public function registerShutdownFunction(callable $function)
method cleanOutputBuffer (line 75) | public function cleanOutputBuffer()
method getOutputBufferLevel (line 83) | public function getOutputBufferLevel()
method endOutputBuffering (line 91) | public function endOutputBuffering()
method flushOutputBuffer (line 99) | public function flushOutputBuffer()
method getErrorReportingLevel (line 107) | public function getErrorReportingLevel()
method getLastError (line 115) | public function getLastError()
method setHttpResponseCode (line 125) | public function setHttpResponseCode($httpCode)
method stopExecution (line 140) | public function stopExecution($exitStatus)
FILE: src/Whoops/Util/TemplateHelper.php
class TemplateHelper (line 18) | class TemplateHelper
method __construct (line 46) | public function __construct()
method escape (line 58) | public function escape($raw)
method escapeButPreserveUris (line 86) | public function escapeButPreserveUris($raw)
method breakOnDelimiter (line 103) | public function breakOnDelimiter($delimiter, $s)
method shorten (line 119) | public function shorten($path)
method getDumper (line 128) | private function getDumper()
method dump (line 161) | public function dump($value)
method dumpArgs (line 195) | public function dumpArgs(Frame $frame)
method slug (line 222) | public function slug($original)
method render (line 236) | public function render($template, ?array $additionalVariables = null)
method setVariables (line 257) | public function setVariables(array $variables)
method setVariable (line 268) | public function setVariable($variableName, $variableValue)
method getVariable (line 281) | public function getVariable($variableName, $defaultValue = null)
method delVariable (line 292) | public function delVariable($variableName)
method getVariables (line 302) | public function getVariables()
method setCloner (line 312) | public function setCloner($cloner)
method getCloner (line 322) | public function getCloner()
method setApplicationRootPath (line 335) | public function setApplicationRootPath($applicationRootPath)
method getApplicationRootPath (line 345) | public function getApplicationRootPath()
FILE: tests/Whoops/Exception/FormatterTest.php
class FormatterTest (line 11) | class FormatterTest extends TestCase
method testPlain (line 13) | public function testPlain()
FILE: tests/Whoops/Exception/FrameCollectionTest.php
class FrameCollectionTest (line 11) | class FrameCollectionTest extends TestCase
method getFrameData (line 23) | public function getFrameData()
method getFrameDataList (line 39) | public function getFrameDataList($total)
method getFrameCollectionInstance (line 54) | private function getFrameCollectionInstance($frames = null)
method testArrayAccessExists (line 66) | public function testArrayAccessExists()
method testArrayAccessGet (line 75) | public function testArrayAccessGet()
method testArrayAccessSet (line 84) | public function testArrayAccessSet()
method testArrayAccessUnset (line 96) | public function testArrayAccessUnset()
method testFilterFrames (line 109) | public function testFilterFrames()
method testMapFrames (line 124) | public function testMapFrames()
method testMapFramesEnforceType (line 140) | public function testMapFramesEnforceType()
method testGetArray (line 155) | public function testGetArray()
method testGetArrayImmutable (line 169) | public function testGetArrayImmutable()
method testCollectionIsIterable (line 181) | public function testCollectionIsIterable()
method testCollectionIsSerializable (line 193) | public function testCollectionIsSerializable()
method testTopDiff (line 207) | public function testTopDiff()
FILE: tests/Whoops/Exception/FrameTest.php
class FrameTest (line 11) | class FrameTest extends TestCase
method getFrameData (line 16) | private function getFrameData()
method getFrameInstance (line 31) | private function getFrameInstance($data = null)
method testGetFile (line 43) | public function testGetFile()
method testGetLine (line 54) | public function testGetLine()
method testGetClass (line 65) | public function testGetClass()
method testGetFunction (line 76) | public function testGetFunction()
method testGetArgs (line 87) | public function testGetArgs()
method testGetFileContents (line 98) | public function testGetFileContents()
method testGetFileContentsWhenFrameIsNotRelatedToSpecificFile (line 112) | public function testGetFileContentsWhenFrameIsNotRelatedToSpecificFile...
method testGetFileLines (line 123) | public function testGetFileLines()
method testGetFileLinesRange (line 135) | public function testGetFileLinesRange()
method testGetComments (line 151) | public function testGetComments()
method testGetFilteredComments (line 177) | public function testGetFilteredComments()
method testFrameIsSerializable (line 201) | public function testFrameIsSerializable()
method testEquals (line 226) | public function testEquals()
FILE: tests/Whoops/Exception/InspectorTest.php
class InspectorTest (line 12) | class InspectorTest extends TestCase
method getException (line 20) | protected function getException($message = "", $code = 0, $previous = ...
method getInspectorInstance (line 29) | protected function getInspectorInstance($exception = null)
method testCorrectNestedFrames (line 37) | public function testCorrectNestedFrames($value = '')
method testDoesNotFailOnPHP7ErrorObject (line 50) | public function testDoesNotFailOnPHP7ErrorObject()
method testReturnsCorrectExceptionName (line 67) | public function testReturnsCorrectExceptionName()
method testExceptionIsStoredAndReturned (line 79) | public function testExceptionIsStoredAndReturned()
method testGetFramesReturnsCollection (line 90) | public function testGetFramesReturnsCollection()
method testGetFramesWithFiltersReturnsCollection (line 101) | public function testGetFramesWithFiltersReturnsCollection()
method testGetFramesWithFiltersReturnsEmptyCollection (line 119) | public function testGetFramesWithFiltersReturnsEmptyCollection()
method testPreviousException (line 138) | public function testPreviousException()
method testNegativeHasPreviousException (line 151) | public function testNegativeHasPreviousException()
method testGetPreviousExceptionsReturnsListOfExceptions (line 162) | public function testGetPreviousExceptionsReturnsListOfExceptions()
method testGetPreviousExceptionsReturnsEmptyListIfThereAreNoPreviousExceptions (line 179) | public function testGetPreviousExceptionsReturnsEmptyListIfThereAreNoP...
method testGetPreviousExceptionMessages (line 191) | public function testGetPreviousExceptionMessages()
method testGetPreviousExceptionCodes (line 209) | public function testGetPreviousExceptionCodes()
FILE: tests/Whoops/Handler/CallbackHandlerTest.php
class CallbackHandlerTest (line 7) | class CallbackHandlerTest extends TestCase
method testSimplifiedBacktrace (line 9) | public function testSimplifiedBacktrace()
FILE: tests/Whoops/Handler/JsonResponseHandlerTest.php
class JsonResponseHandlerTest (line 12) | class JsonResponseHandlerTest extends TestCase
method getHandler (line 17) | private function getHandler()
method getException (line 25) | public function getException($message = 'test message')
method getJsonResponseFromHandler (line 34) | private function getJsonResponseFromHandler($withTrace = false, $jsonA...
method testReturnsWithoutFrames (line 59) | public function testReturnsWithoutFrames()
method testReturnsWithFrames (line 82) | public function testReturnsWithFrames()
method testReturnsJsonApi (line 102) | public function testReturnsJsonApi()
FILE: tests/Whoops/Handler/PlainTextHandlerTest.php
class PlainTextHandlerTest (line 14) | class PlainTextHandlerTest extends TestCase
method getHandler (line 24) | private function getHandler($logger = null)
method getException (line 32) | public function getException($message = 'test message')
method getPlainTextFromHandler (line 46) | private function getPlainTextFromHandler(
method testConstructor (line 79) | public function testConstructor()
method testSetLogger (line 89) | public function testSetLogger()
method testAddTraceToOutput (line 99) | public function testAddTraceToOutput()
method testAddTraceFunctionArgsToOutput (line 128) | public function testAddTraceFunctionArgsToOutput()
method testGetSetTraceFunctionArgsOutputLimit (line 158) | public function testGetSetTraceFunctionArgsOutputLimit()
method testLoggerOnly (line 177) | public function testLoggerOnly()
method testReturnsWithoutFramesOutput (line 207) | public function testReturnsWithoutFramesOutput()
method testReturnsWithoutPreviousExceptions (line 230) | public function testReturnsWithoutPreviousExceptions()
method testReturnsWithPreviousExceptions (line 253) | public function testReturnsWithPreviousExceptions()
method testReturnsWithFramesOutput (line 288) | public function testReturnsWithFramesOutput()
method testReturnsWithFramesAndArgsOutput (line 322) | public function testReturnsWithFramesAndArgsOutput()
method testReturnsWithFramesAndLimitedArgsOutput (line 369) | public function testReturnsWithFramesAndLimitedArgsOutput()
method testReturnsWithLoggerOnlyOutput (line 408) | public function testReturnsWithLoggerOnlyOutput()
method testGetFrameArgsOutputUsesDumper (line 424) | public function testGetFrameArgsOutputUsesDumper()
FILE: tests/Whoops/Handler/PrettyPageHandlerTest.php
class PrettyPageHandlerTest (line 13) | class PrettyPageHandlerTest extends TestCase
method getHandler (line 18) | private function getHandler()
method getException (line 28) | public function getException()
method testHandleWithoutErrors (line 38) | public function testHandleWithoutErrors()
method testGetSetPageTitle (line 57) | public function testGetSetPageTitle()
method testGetSetResourcePaths (line 70) | public function testGetSetResourcePaths()
method testSetInvalidResourcesPath (line 85) | public function testSetInvalidResourcesPath()
method testGetSetDataTables (line 96) | public function testGetSetDataTables()
method testSetCallbackDataTables (line 134) | public function testSetCallbackDataTables()
method testSetEditorSimple (line 209) | public function testSetEditorSimple()
method testSetEditorCallable (line 235) | public function testSetEditorCallable()
method testAddEditor (line 306) | public function testAddEditor()
method testEditorXdebug (line 322) | public function testEditorXdebug()
FILE: tests/Whoops/Handler/XmlResponseHandlerTest.php
class XmlResponseHandlerTest (line 11) | class XmlResponseHandlerTest extends TestCase
method testSimpleValid (line 13) | public function testSimpleValid()
method testSimpleValidFile (line 33) | public function testSimpleValidFile(\SimpleXMLElement $xml)
method testSimpleValidLine (line 41) | public function testSimpleValidLine(\SimpleXMLElement $xml)
method testSimpleValidType (line 49) | public function testSimpleValidType(\SimpleXMLElement $xml)
method checkField (line 57) | private function checkField(\SimpleXMLElement $xml, $field, $value)
method getException (line 64) | private function getException()
method isValidXml (line 74) | private function isValidXml($data)
FILE: tests/Whoops/RunTest.php
class RunTest (line 20) | class RunTest extends TestCase
method testImplementsRunInterface (line 22) | public function testImplementsRunInterface()
method testConstantsAreAccessibleFromTheClass (line 27) | public function testConstantsAreAccessibleFromTheClass()
method getException (line 38) | protected function getException($message = "")
method getHandler (line 49) | protected function getHandler()
method testClearHandlers (line 68) | public function testClearHandlers()
method testPushHandler (line 81) | public function testPushHandler()
method testPushInvalidHandler (line 102) | public function testPushInvalidHandler()
method testPushClosureBecomesHandler (line 114) | public function testPushClosureBecomesHandler()
method testPopHandler (line 125) | public function testPopHandler()
method testRemoveHandler (line 155) | public function testRemoveHandler()
method testRegisterHandler (line 177) | public function testRegisterHandler()
method testUnregisterHandler (line 190) | public function testUnregisterHandler()
method testHandlerHoldsOrder (line 209) | public function testHandlerHoldsOrder()
method testHandlersGonnaHandle (line 236) | public function testHandlersGonnaHandle()
method testLastHandler (line 267) | public function testLastHandler()
method testErrorSuppression (line 315) | public function testErrorSuppression()
method testErrorCatching (line 336) | public function testErrorCatching()
method testErrorReporting (line 365) | public function testErrorReporting()
method testSilenceErrorsInPaths (line 391) | public function testSilenceErrorsInPaths()
method testGetSilencedError (line 415) | public function testGetSilencedError()
method testErrorWrappedInException (line 434) | public function testErrorWrappedInException()
method testOutputIsSent (line 453) | public function testOutputIsSent()
method testOutputIsNotSent (line 469) | public function testOutputIsNotSent()
method testSendHttpCode (line 485) | public function testSendHttpCode()
method testSendHttpCodeNullCode (line 495) | public function testSendHttpCodeNullCode()
method testSendHttpCodeWrongCode (line 504) | public function testSendHttpCodeWrongCode()
method testSendExitCode (line 514) | public function testSendExitCode()
method testSendExitCodeDefaultCode (line 524) | public function testSendExitCodeDefaultCode()
method testSendExitCodeWrongCode (line 533) | public function testSendExitCodeWrongCode()
method testAddFrameFilter (line 544) | public function testAddFrameFilter()
method testClearFrameFilters (line 567) | public function testClearFrameFilters()
method testShutdownHandlerDoesNotRunIfUnregistered (line 578) | public function testShutdownHandlerDoesNotRunIfUnregistered()
FILE: tests/Whoops/TestCase.php
class TestCase (line 11) | class TestCase extends BaseTestCase
method getRunInstance (line 16) | protected function getRunInstance()
method expectExceptionOfType (line 28) | protected function expectExceptionOfType($class)
method assertStringContains (line 42) | protected function assertStringContains($a, $b)
method assertStringNotContains (line 56) | protected function assertStringNotContains($a, $b)
method callPrivateMethod (line 71) | public static function callPrivateMethod($class_or_object, $method, $a...
FILE: tests/Whoops/Util/HtmlDumperOutputTest.php
class HtmlDumperOutputTest (line 11) | class HtmlDumperOutputTest extends TestCase
method testOutput (line 17) | public function testOutput()
method testClear (line 35) | public function testClear()
FILE: tests/Whoops/Util/MiscTest.php
class MiscTest (line 11) | class MiscTest extends TestCase
method testTranslateException (line 18) | public function testTranslateException($expected_output, $exception_code)
method provideTranslateException (line 24) | public function provideTranslateException()
FILE: tests/Whoops/Util/SystemFacadeTest.php
class SystemFacadeTest (line 6) | class SystemFacadeTest extends TestCase
method delegate (line 18) | public static function delegate($fn, array $args = [])
method getReady (line 28) | public function getReady()
method finishUp (line 37) | public function finishUp()
method test_it_delegates_output_buffering_to_the_native_implementation (line 43) | public function test_it_delegates_output_buffering_to_the_native_imple...
method test_it_delegates_cleaning_output_buffering_to_the_native_implementation (line 50) | public function test_it_delegates_cleaning_output_buffering_to_the_nat...
method test_it_delegates_getting_the_current_buffer_level_to_the_native_implementation (line 57) | public function test_it_delegates_getting_the_current_buffer_level_to_...
method test_it_delegates_ending_the_current_buffer_to_the_native_implementation (line 64) | public function test_it_delegates_ending_the_current_buffer_to_the_nat...
method test_it_delegates_flushing_the_current_buffer_to_the_native_implementation (line 71) | public function test_it_delegates_flushing_the_current_buffer_to_the_n...
method test_it_delegates_error_handling_to_the_native_implementation (line 78) | public function test_it_delegates_error_handling_to_the_native_impleme...
method test_it_delegates_error_handling_with_level_to_the_native_implementation (line 85) | public function test_it_delegates_error_handling_with_level_to_the_nat...
method test_it_delegates_exception_handling_to_the_native_implementation (line 92) | public function test_it_delegates_exception_handling_to_the_native_imp...
method test_it_delegates_restoring_the_exception_handler_to_the_native_implementation (line 99) | public function test_it_delegates_restoring_the_exception_handler_to_t...
method test_it_delegates_restoring_the_error_handler_to_the_native_implementation (line 106) | public function test_it_delegates_restoring_the_error_handler_to_the_n...
method test_it_delegates_registering_a_shutdown_function_to_the_native_implementation (line 113) | public function test_it_delegates_registering_a_shutdown_function_to_t...
method test_it_delegates_error_reporting_to_the_native_implementation (line 120) | public function test_it_delegates_error_reporting_to_the_native_implem...
method test_it_delegates_getting_the_last_error_to_the_native_implementation (line 127) | public function test_it_delegates_getting_the_last_error_to_the_native...
method test_it_delegates_sending_an_http_response_code_to_the_native_implementation (line 134) | public function test_it_delegates_sending_an_http_response_code_to_the...
function ob_start (line 144) | function ob_start()
function ob_get_clean (line 149) | function ob_get_clean()
function ob_get_level (line 154) | function ob_get_level()
function ob_end_clean (line 159) | function ob_end_clean()
function flush (line 164) | function flush()
function set_error_handler (line 169) | function set_error_handler(callable $handler, $types = 'use-php-defaults')
function set_exception_handler (line 178) | function set_exception_handler(callable $handler)
function restore_exception_handler (line 183) | function restore_exception_handler()
function restore_error_handler (line 188) | function restore_error_handler()
function register_shutdown_function (line 193) | function register_shutdown_function()
function error_reporting (line 198) | function error_reporting($level = null)
function error_get_last (line 203) | function error_get_last()
function header_remove (line 208) | function header_remove($header = null)
function headers_sent (line 213) | function headers_sent(&$filename = null, &$line = null)
function http_response_code (line 218) | function http_response_code($code = null)
FILE: tests/Whoops/Util/TemplateHelperTest.php
class TemplateHelperTest (line 11) | class TemplateHelperTest extends TestCase
method getReady (line 21) | public function getReady()
method testEscape (line 30) | public function testEscape()
method testEscapeBrokenUtf8 (line 40) | public function testEscapeBrokenUtf8()
method testEscapeButPreserveUris (line 56) | public function testEscapeButPreserveUris()
method testBreakOnDelimiter (line 69) | public function testBreakOnDelimiter()
method testShorten (line 80) | public function testShorten()
method testSlug (line 93) | public function testSlug()
method testRender (line 102) | public function testRender()
method testTemplateVariables (line 123) | public function testTemplateVariables()
Condensed preview — 76 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (307K chars).
[
{
"path": ".editorconfig",
"chars": 246,
"preview": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_tr"
},
{
"path": ".gitattributes",
"chars": 287,
"preview": "docs/ export-ignore\nexamples/ export-ignore\ntests/ export-ignore\n.github/ export-ignore\n.editorconfig export-ignore\n.git"
},
{
"path": ".github/FUNDING.yml",
"chars": 22,
"preview": "github: denis-sokolov\n"
},
{
"path": ".github/workflows/tests.yml",
"chars": 1500,
"preview": "name: Tests\n\non:\n push:\n pull_request:\n\njobs:\n tests:\n name: PHP ${{ matrix.php }}\n runs-on: ubuntu-22.04\n\n "
},
{
"path": ".gitignore",
"chars": 40,
"preview": "composer.lock\nphpunit.xml\nreport\nvendor\n"
},
{
"path": ".mailmap",
"chars": 70,
"preview": "Denis Sokolov <denis@sokolov.cc>\nFilipe Dobreira <dobreira@gmail.com>\n"
},
{
"path": ".scrutinizer.yml",
"chars": 220,
"preview": "imports:\n - php\n\nfilter:\n paths: [src/*]\n\ntools:\n php_cs_fixer:\n config: { level: psr1 }\n php_hhvm: true\n php_me"
},
{
"path": "CHANGELOG.md",
"chars": 3066,
"preview": "# CHANGELOG\n\n## v2.18.0\n\n* Line numbers are now clickable.\n\n## v2.17.0\n\n* Support cursor IDE.\n\n## v2.16.0\n\n* Support PHP"
},
{
"path": "CONTRIBUTING.md",
"chars": 946,
"preview": "If you want to give me some feedback or make a suggestion, create an [issue on GitHub](https://github.com/filp/whoops/is"
},
{
"path": "LICENSE.md",
"chars": 1042,
"preview": "# The MIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and ass"
},
{
"path": "README.md",
"chars": 5864,
"preview": "# whoops\nPHP errors for cool kids\n\n[](https://pac"
},
{
"path": "SECURITY.md",
"chars": 304,
"preview": "# Security Policy\n\n## Supported Versions\n\nOnly the latest released version of Whoops is supported.\nTo facilitate upgrade"
},
{
"path": "composer.json",
"chars": 1300,
"preview": "{\n \"name\": \"filp/whoops\",\n \"license\": \"MIT\",\n \"description\": \"php error handling for cool kids\",\n \"keywords\""
},
{
"path": "docs/API Documentation.md",
"chars": 14838,
"preview": "# API Documentation\n\n### Core Classes:\n- [`Whoops\\Run`](#whoops-run) - The main `Whoops` class - represents the stack an"
},
{
"path": "docs/Framework Integration.md",
"chars": 1014,
"preview": "# Contributing an integration with a framework\n\nLately we're preferring to keep integration libraries out of the Whoops "
},
{
"path": "docs/Open Files In An Editor.md",
"chars": 2192,
"preview": "# Open Files In An Editor\n\nWhen using the pretty error page feature, whoops comes with the ability to\nopen referenced fi"
},
{
"path": "docs/Replay Errors.md",
"chars": 449,
"preview": "# Replay Errors\n\nYou can replay Errors that happened in production if you serialize them and store them to later replay "
},
{
"path": "examples/example-ajax-only.php",
"chars": 1519,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n *\n * Run this exampl"
},
{
"path": "examples/example.php",
"chars": 1927,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n *\n * Run this exampl"
},
{
"path": "examples/lib.php",
"chars": 69,
"preview": "<?php\nfunction whoops_add_stack_frame($callback){\n $callback();\n}\n"
},
{
"path": "phpunit.xml.dist",
"chars": 751,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit backupGlobals=\"true\" beStrictAboutTestsThatDoNotTestAnything=\"false\" bo"
},
{
"path": "src/Whoops/Exception/ErrorException.php",
"chars": 355,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Exception/Formatter.php",
"chars": 2458,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Exception/Frame.php",
"chars": 8177,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Exception/FrameCollection.php",
"chars": 5161,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Exception/Inspector.php",
"chars": 9693,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Handler/CallbackHandler.php",
"chars": 1352,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Handler/Handler.php",
"chars": 1914,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Handler/HandlerInterface.php",
"chars": 746,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Handler/JsonResponseHandler.php",
"chars": 2109,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Handler/PlainTextHandler.php",
"chars": 8994,
"preview": "<?php\n/**\n* Whoops - php errors for cool kids\n* @author Filipe Dobreira <http://github.com/filp>\n* Plaintext handler for"
},
{
"path": "src/Whoops/Handler/PrettyPageHandler.php",
"chars": 25852,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Handler/XmlResponseHandler.php",
"chars": 2723,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Inspector/InspectorFactory.php",
"chars": 416,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Inspector/InspectorFactoryInterface.php",
"chars": 299,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Inspector/InspectorInterface.php",
"chars": 1566,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Resources/css/prism.css",
"chars": 3210,
"preview": "/* PrismJS 1.30.0\nhttps://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+markup-templating+php&plugins"
},
{
"path": "src/Whoops/Resources/css/whoops.base.css",
"chars": 10328,
"preview": "body {\n font: 12px \"Helvetica Neue\", helvetica, arial, sans-serif;\n color: #131313;\n background: #eeeeee;\n padding:0"
},
{
"path": "src/Whoops/Resources/js/prism.js",
"chars": 24165,
"preview": "/* PrismJS 1.30.0\nhttps://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+markup-templating+php&plugins"
},
{
"path": "src/Whoops/Resources/js/whoops.base.js",
"chars": 6659,
"preview": "Zepto(function($) {\n var $leftPanel = $('.left-panel');\n var $frameContainer = $('.frames-container');\n var $app"
},
{
"path": "src/Whoops/Resources/views/env_details.html.php",
"chars": 1561,
"preview": "<?php /* List data-table values, i.e: $_SERVER, $_GET, .... */ ?>\n<div class=\"details\">\n <h2 class=\"details-heading\">En"
},
{
"path": "src/Whoops/Resources/views/frame_code.html.php",
"chars": 2927,
"preview": "<?php /* Display a code block for all frames in the stack.\n * @todo: This should PROBABLY be done on-demand, lest\n"
},
{
"path": "src/Whoops/Resources/views/frame_list.html.php",
"chars": 1040,
"preview": "<?php /* List file names & line numbers for all stack frames;\n clicking these links/buttons will display the cod"
},
{
"path": "src/Whoops/Resources/views/frames_container.html.php",
"chars": 162,
"preview": "<div class=\"frames-container <?php echo $active_frames_tab == 'application' ? 'frames-container-application' : '' ?>\">\n "
},
{
"path": "src/Whoops/Resources/views/frames_description.html.php",
"chars": 660,
"preview": "<div class=\"frames-description <?php echo $has_frames_tabs ? 'frames-description-application' : '' ?>\">\n <?php if ($has"
},
{
"path": "src/Whoops/Resources/views/header.html.php",
"chars": 9417,
"preview": "<div class=\"exception\">\n <div class=\"exc-title\">\n <?php foreach ($name as $i => $nameSection): ?>\n <?php if ($i"
},
{
"path": "src/Whoops/Resources/views/header_outer.html.php",
"chars": 52,
"preview": "<header>\n <?php $tpl->render($header) ?>\n</header>\n"
},
{
"path": "src/Whoops/Resources/views/layout.html.php",
"chars": 877,
"preview": "<?php\n/**\n* Layout template file for Whoops's pretty error output.\n*/\n?>\n<!DOCTYPE html><?php echo $preface; ?>\n<html>\n "
},
{
"path": "src/Whoops/Resources/views/panel_details.html.php",
"chars": 70,
"preview": "<?php $tpl->render($frame_code) ?>\n<?php $tpl->render($env_details) ?>"
},
{
"path": "src/Whoops/Resources/views/panel_details_outer.html.php",
"chars": 87,
"preview": "<div class=\"panel details-container cf\">\n <?php $tpl->render($panel_details) ?>\n</div>"
},
{
"path": "src/Whoops/Resources/views/panel_left.html.php",
"chars": 104,
"preview": "<?php \n$tpl->render($header_outer);\n$tpl->render($frames_description);\n$tpl->render($frames_container);\n"
},
{
"path": "src/Whoops/Resources/views/panel_left_outer.html.php",
"chars": 121,
"preview": "<div class=\"panel left-panel cf <?php echo (!$has_frames ? 'empty' : '') ?>\">\n <?php $tpl->render($panel_left) ?>\n</div"
},
{
"path": "src/Whoops/Run.php",
"chars": 16511,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/RunInterface.php",
"chars": 4034,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Util/HtmlDumperOutput.php",
"chars": 718,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Util/Misc.php",
"chars": 1987,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Util/SystemFacade.php",
"chars": 2703,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "src/Whoops/Util/TemplateHelper.php",
"chars": 9476,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Exception/FormatterTest.php",
"chars": 487,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Exception/FrameCollectionTest.php",
"chars": 5844,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Exception/FrameTest.php",
"chars": 6506,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Exception/InspectorTest.php",
"chars": 7390,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Handler/CallbackHandlerTest.php",
"chars": 475,
"preview": "<?php\n\nnamespace Whoops\\Handler;\n\nuse Whoops\\TestCase;\n\nclass CallbackHandlerTest extends TestCase\n{\n public function"
},
{
"path": "tests/Whoops/Handler/JsonResponseHandlerTest.php",
"chars": 3947,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Handler/PlainTextHandlerTest.php",
"chars": 14214,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Handler/PrettyPageHandlerTest.php",
"chars": 11434,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Handler/XmlResponseHandlerTest.php",
"chars": 1899,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n */\n\nnamespace Whoops\\Handler;\n\nuse RuntimeException;\nuse Whoops\\TestCase"
},
{
"path": "tests/Whoops/RunTest.php",
"chars": 17378,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/TestCase.php",
"chars": 1998,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Util/HtmlDumperOutputTest.php",
"chars": 957,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Util/MiscTest.php",
"chars": 919,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/Whoops/Util/SystemFacadeTest.php",
"chars": 5868,
"preview": "<?php\nnamespace Whoops\\Util;\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass SystemFacadeTest extends TestCase\n{\n /**\n * "
},
{
"path": "tests/Whoops/Util/TemplateHelperTest.php",
"chars": 4026,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n */\n\nnamespace Whoops"
},
{
"path": "tests/bootstrap.php",
"chars": 216,
"preview": "<?php\n/**\n * Whoops - php errors for cool kids\n * @author Filipe Dobreira <http://github.com/filp>\n *\n * Bootstrapper fo"
},
{
"path": "tests/fixtures/frame.lines-test.php",
"chars": 97,
"preview": "<?php\n// Line 2\n// Line 3\n// Line 4\n// Line 5\n// Line 6\n// Line 7\n// Line 8\n// Line 9\n// Line 10\n"
},
{
"path": "tests/fixtures/template.php",
"chars": 88,
"preview": "<?php echo $tpl->slug(\"hello world!\"); ?>\n\nMy name is <?php echo $tpl->escape($name) ?>\n"
}
]
About this extraction
This page contains the full source code of the filp/whoops GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 76 files (283.3 KB), approximately 78.0k tokens, and a symbol index with 449 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.