the whole shebang

This commit is contained in:
2014-11-25 16:42:40 +01:00
parent 7f74c0613e
commit ab1334c0cf
3686 changed files with 496409 additions and 1 deletions

4
vendor/filp/whoops/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
vendor/*
report/*
phpunit.xml
*.swp

6
vendor/filp/whoops/.travis.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
language: php
php:
- 5.4
- 5.3
before_script:
- composer install --dev

19
vendor/filp/whoops/LICENSE.md vendored Normal file
View File

@@ -0,0 +1,19 @@
#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.

225
vendor/filp/whoops/README.md vendored Normal file
View File

@@ -0,0 +1,225 @@
# whoops
php errors for cool kids
[![Build Status](https://travis-ci.org/filp/whoops.png?branch=master)](https://travis-ci.org/filp/whoops)
-----
![Whoops!](http://i.imgur.com/xiZ1tUU.png)
**whoops** is an error handler base/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.
## (current) 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
- **NEW** Includes the ability to open referenced files directly in your editor and IDE
- Includes a Silex Service Provider for painless integration with [Silex](http://silex.sensiolabs.org/)
- Includes a Phalcon Service Provider for painless integration with [Phalcon](http://phalconphp.com/)
- Includes a Module for equally painless integration with [Zend Framework 2](http://framework.zend.com/)
- Easy to extend and integrate with existing libraries
- Clean, well-structured & tested code-base (well, except `pretty-template.php`, for now...)
## Installing
- Install [Composer](http://getcomposer.org) and place the executable somewhere in your `$PATH` (for the rest of this README,
I'll reference it as just `composer`)
- Add `filp/whoops` to your project's `composer.json:
```json
{
"require": {
"filp/whoops": "1.*"
}
}
```
- Install/update your dependencies
```bash
$ cd my_project
$ composer install
```
And you're good to go! Have a look at the **example files** in `examples/` to get a feel for how things work.
I promise it's really simple!
## API Documentation
Initial API documentation of the whoops library is available here:
https://github.com/filp/whoops/wiki/API-Documentation
## Usage
### Integrating with Silex
**whoops** comes packaged with a Silex Service Provider: `Whoops\Provider\Silex\WhoopsServiceProvider`. Using it
in your existing Silex project is easy:
```php
require 'vendor/autoload.php';
use Silex\Application;
// ... some awesome code here ...
if($app['debug']) {
$app->register(new Whoops\Provider\Silex\WhoopsServiceProvider);
}
// ...
$app->run();
```
And that's about it. By default, you'll get the pretty error pages if something goes awry in your development
environment, but you also have full access to the **whoops** library, obviously. For example, adding a new handler
into your app is as simple as extending `whoops`:
```php
$app['whoops'] = $app->extend('whoops', function($whoops) {
$whoops->pushHandler(new DeleteWholeProjectHandler);
return $whoops;
});
```
### Integrating with Phalcon
**whoops** comes packaged with a Phalcon Service Provider: `Whoops\Provider\Phalcon\WhoopsServiceProvider`. Using it
in your existing Phalcon project is easy. The provider uses the default Phalcon DI unless you pass a DI instance into the constructor.
```php
new Whoops\Provider\Phalcon\WhoopsServiceProvider;
// --- or ---
$di = Phalcon\DI\FactoryDefault;
new Whoops\Provider\Phalcon\WhoopsServiceProvider($di);
```
### Integrating with Laravel 4/Illuminate
If you're using Laravel 4, as of [this commit to laravel/framework](https://github.com/laravel/framework/commit/64f3a79aae254b71550a8097880f0b0e09062d24), you're already using Whoops! Yay!
### Integrating with Laravel 3
User [@hdias](https://github.com/hdias) contributed a simple guide/example to help you integrate **whoops** with Laravel 3's IoC container, available at:
https://gist.github.com/hdias/5169713#file-start-php
### Integrating with Zend Framework 2
User [@zsilbi](https://github.com/zsilbi) contributed a provider for ZF2 integration,
available in the following location:
https://github.com/filp/whoops/tree/master/src/Whoops/Provider/Zend
**Instructions:**
- Add Whoops as a module to you app (/vendor/Whoops)
- Whoops must be the first module:
```php
'modules' => array(
'Whoops',
'Application'
)
```
- Move Module.php from /Whoops/Provider/Zend/Module.php to /Whoops/Module.php
- Use optional configurations in your controller config:
```php
return array(
'view_manager' => array(
'display_not_found_reason' => true,
'display_exceptions' => true,
'json_exceptions' => array(
'display' => true,
'ajax_only' => true,
'show_trace' => true
)
),
);
```
- NOTE: ob_clean(); is used to remove previous output, so you may use ob_start(); at the beginning of your app (index.php)
### Opening referenced files with your favorite editor or IDE
When using the pretty error page feature, whoops comes with the ability to
open referenced files directly in your IDE or editor.
```php
<?php
use Whoops\Handler\PrettyPageHandler;
$handler = new PrettyPageHandler;
$handler->setEditor('sublime');
```
The following editors are currently supported by default.
- `sublime` - Sublime Text 2
- `emacs` - Emacs
- `textmate` - Textmate
- `macvim` - MacVim
- `xdebug` - xdebug (uses [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";
});
```
### 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
- [`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.
## Contributing
If you want to give me some feedback or make a suggestion, send me a message through
twitter: [@imfilp](https://twitter.com/imfilp)
If you want to get your hands dirty, great! Here's a couple of steps/guidelines:
- Fork/clone this repo, and update dev dependencies using Composer
```bash
$ git clone git@github.com:filp/whoops.git
$ cd whoops
$ composer install --dev
```
- Create a new branch for your feature or fix
```bash
$ git checkout -b feature/flames-on-the-side
```
- Add your changes & tests for those changes (in `tests/`).
- Remember to stick to the existing code style as best as possible. When in doubt, follow `PSR-2`.
- Send me a pull request!
If you don't want to go through all this, but still found something wrong or missing, please
let me know, and/or **open a new issue report** so that I or others may take care of it.
## Authors
This library was primarily developed by [Filipe Dobreira](https://github.com/filp).
A lot of awesome fixes and enhancements were also sent in by contributors, which you can find **[in this page right here](https://github.com/filp/whoops/contributors)**.

27
vendor/filp/whoops/composer.json vendored Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "filp/whoops",
"license": "MIT",
"description": "php error handling for cool kids",
"version": "1.0.7",
"keywords": ["library", "error", "handling", "exception", "silex-provider", "whoops", "zf2"],
"homepage": "https://github.com/filp/whoops",
"authors": [
{
"name": "Filipe Dobreira",
"homepage": "https://github.com/filp",
"role": "Developer"
}
],
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"mockery/mockery": "dev-master",
"silex/silex": "1.0.*@dev"
},
"autoload": {
"psr-0": {
"Whoops": "src/"
}
}
}

452
vendor/filp/whoops/composer.lock generated vendored Normal file
View File

@@ -0,0 +1,452 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "6e72d0b30a42469a5cef281fc82e77eb",
"packages": [
],
"packages-dev": [
{
"name": "mockery/mockery",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/padraic/mockery.git",
"reference": "70739803f85065cc2ec00fae662bdcaaa4df487e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/padraic/mockery/zipball/70739803f85065cc2ec00fae662bdcaaa4df487e",
"reference": "70739803f85065cc2ec00fae662bdcaaa4df487e",
"shasum": ""
},
"require": {
"lib-pcre": ">=7.0",
"php": ">=5.3.2"
},
"require-dev": {
"hamcrest/hamcrest": "1.1.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Mockery": "library/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Pádraic Brady",
"email": "padraic.brady@gmail.com",
"homepage": "http://blog.astrumfutura.com"
}
],
"description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succint API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.",
"homepage": "http://github.com/padraic/mockery",
"keywords": [
"BDD",
"TDD",
"library",
"mock",
"mock objects",
"mockery",
"stub",
"test",
"test double",
"testing"
],
"time": "2013-05-29 09:13:17"
},
{
"name": "pimple/pimple",
"version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Pimple.git",
"reference": "v1.0.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Pimple/zipball/v1.0.2",
"reference": "v1.0.2",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2013-03-08 08:21:40"
},
{
"name": "psr/log",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log",
"reference": "1.0.0"
},
"dist": {
"type": "zip",
"url": "https://github.com/php-fig/log/archive/1.0.0.zip",
"reference": "1.0.0",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-0": {
"Psr\\Log\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2012-12-21 11:40:51"
},
{
"name": "symfony/debug",
"version": "v2.3.0",
"target-dir": "Symfony/Component/Debug",
"source": {
"type": "git",
"url": "https://github.com/symfony/Debug.git",
"reference": "v2.3.0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Debug/zipball/v2.3.0",
"reference": "v2.3.0",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/http-foundation": ">=2.1,<3.0",
"symfony/http-kernel": ">=2.1,<3.0"
},
"suggest": {
"symfony/class-loader": "",
"symfony/http-foundation": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Debug\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Debug Component",
"homepage": "http://symfony.com",
"time": "2013-06-02 11:58:44"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.3.0",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "v2.3.0-RC1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.0-RC1",
"reference": "v2.3.0-RC1",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/dependency-injection": ">=2.0,<3.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2013-05-13 14:36:40"
},
{
"name": "symfony/http-foundation",
"version": "v2.3.0",
"target-dir": "Symfony/Component/HttpFoundation",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpFoundation.git",
"reference": "v2.3.0-RC1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.3.0-RC1",
"reference": "v2.3.0-RC1",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\HttpFoundation\\": ""
},
"classmap": [
"Symfony/Component/HttpFoundation/Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony HttpFoundation Component",
"homepage": "http://symfony.com",
"time": "2013-05-10 06:00:03"
},
{
"name": "symfony/http-kernel",
"version": "v2.3.0",
"target-dir": "Symfony/Component/HttpKernel",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpKernel.git",
"reference": "v2.3.0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/v2.3.0",
"reference": "v2.3.0",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"psr/log": ">=1.0,<2.0",
"symfony/debug": ">=2.3,<3.0",
"symfony/event-dispatcher": ">=2.1,<3.0",
"symfony/http-foundation": ">=2.2,<3.0"
},
"require-dev": {
"symfony/browser-kit": "2.2.*",
"symfony/class-loader": ">=2.1,<3.0",
"symfony/config": ">=2.0,<3.0",
"symfony/console": "2.2.*",
"symfony/dependency-injection": ">=2.0,<3.0",
"symfony/finder": ">=2.0,<3.0",
"symfony/process": ">=2.0,<3.0",
"symfony/routing": ">=2.2,<3.0",
"symfony/stopwatch": ">=2.2,<3.0"
},
"suggest": {
"symfony/browser-kit": "",
"symfony/class-loader": "",
"symfony/config": "",
"symfony/console": "",
"symfony/dependency-injection": "",
"symfony/finder": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\HttpKernel\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony HttpKernel Component",
"homepage": "http://symfony.com",
"time": "2013-06-03 14:13:35"
},
{
"name": "symfony/routing",
"version": "v2.3.0",
"target-dir": "Symfony/Component/Routing",
"source": {
"type": "git",
"url": "https://github.com/symfony/Routing.git",
"reference": "v2.3.0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.0",
"reference": "v2.3.0",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"doctrine/common": ">=2.2,<3.0",
"psr/log": ">=1.0,<2.0",
"symfony/config": ">=2.2,<3.0",
"symfony/yaml": ">=2.0,<3.0"
},
"suggest": {
"doctrine/common": "",
"symfony/config": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Routing\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Routing Component",
"homepage": "http://symfony.com",
"time": "2013-05-20 08:57:26"
}
],
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": {
"mockery/mockery": 20,
"silex/silex": 20
},
"platform": {
"php": ">=5.3.0"
},
"platform-dev": [
]
}

View File

@@ -0,0 +1,46 @@
<?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/example/example-ajax-only.php through your browser
*
* Or just run it through apache/nginx/what-have-yous as usual.
*/
namespace Whoops\Example;
use Whoops\Run;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Handler\JsonResponseHandler;
use RuntimeException;
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.
$jsonHandler = new JsonResponseHandler;
// Make sure it only triggers for AJAX requests:
$jsonHandler->onlyForAjaxRequests(true);
// You can also tell JsonResponseHandler to give you a full stack trace:
// $jsonHandler->addTraceToOutput(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!");

View File

@@ -0,0 +1,36 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*
* NOTE: Requires silex/silex, can be installed with composer
* within this project using the --dev flag:
*
* $ composer install --dev
*
* 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-silex.php through your browser
*
* Or just run it through apache/nginx/what-have-yous as usual.
*/
require __DIR__ . '/../vendor/autoload.php';
use Whoops\Provider\Silex\WhoopsServiceProvider;
use Silex\Application;
$app = new Application;
$app['debug'] = true;
if($app['debug']) {
$app->register(new WhoopsServiceProvider);
}
$app->get('/', function() use($app) {
throw new RuntimeException("Oh no!");
});
$app->run();

63
vendor/filp/whoops/examples/example.php vendored Normal file
View File

@@ -0,0 +1,63 @@
<?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/example/example.php through your browser
*
* Or just run it through apache/nginx/what-have-yous as usual.
*/
namespace Whoops\Example;
use Whoops\Run;
use Whoops\Handler\PrettyPageHandler;
use Exception as BaseException;
require __DIR__ . '/../vendor/autoload.php';
class Exception extends BaseException {}
$run = new Run;
$handler = new PrettyPageHandler;
// Add a custom table to the layout:
$handler->addDataTable('Ice-cream I like', array(
'Chocolate' => 'yes',
'Coffee & chocolate' => 'a lot',
'Strawberry & chocolate' => 'it\'s alright',
'Vanilla' => 'ew'
));
$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()
{
fooBar();
}
bar();

15
vendor/filp/whoops/phpunit.xml.dist vendored Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Whoops Tests Suite">
<directory>tests/Whoops/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/Whoops/</directory>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,14 @@
<?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 {}

View File

@@ -0,0 +1,228 @@
<?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 = array();
/**
* @param array[]
*/
public function __construct(array $frame)
{
$this->frame = $frame;
}
/**
* @param bool $shortened
* @return string|null
*/
public function getFile($shortened = false)
{
$file = !empty($this->frame['file']) ? $this->frame['file'] : null;
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__))))));
$file = str_replace($dirname, "", $file);
$file = str_replace("/", "/&shy;", $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'] : array();
}
/**
* 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()) {
$this->fileContentsCache = file_get_contents($filePath);
}
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[] = array(
'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( 10 => '...', 11 => '...')
*
* @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);
}
/**
* 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;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Exception\Frame;
use UnexpectedValueException;
use IteratorAggregate;
use ArrayIterator;
use Serializable;
use Countable;
/**
* Exposes a fluent interface for dealing with an ordered list
* of stack-trace frames.
*/
class FrameCollection implements IteratorAggregate, Serializable, Countable
{
/**
* @var array[]
*/
private $frames;
/**
* @param array $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 Whoops\Exception\FrameCollection
*/
public function filter($callable)
{
$this->frames = array_filter($this->frames, $callable);
return $this;
}
/**
* Map the collection of frames
*
* @param callable $callable
* @return Whoops\Exception\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 " . __METHOD__ . " 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 Whoops\Exception\FrameCollection::getIterator
* @return array
*/
public function getArray()
{
return $this->frames;
}
/**
* @see IteratorAggregate::getIterator
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->frames);
}
/**
* @see Countable::count
* @return int
*/
public function count()
{
return count($this->frames);
}
/**
* @see Serializable::serialize
* @return string
*/
public function serialize()
{
return serialize($this->frames);
}
/**
* @see Serializable::unserialize
* @param string $serializedFrames
*/
public function unserialize($serializedFrames)
{
$this->frames = unserialize($serializedFrames);
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Exception\FrameCollection;
use Whoops\Exception\ErrorException;
use Exception;
class Inspector
{
/**
* @var Exception
*/
private $exception;
/**
* @var Whoops\Exception\FrameCollection
*/
private $frames;
/**
* @param Exception $exception The exception to inspect
*/
public function __construct(Exception $exception)
{
$this->exception = $exception;
}
/**
* @return Exception
*/
public function getException()
{
return $this->exception;
}
/**
* @return string
*/
public function getExceptionName()
{
return get_class($this->exception);
}
/**
* @return string
*/
public function getExceptionMessage()
{
return $this->exception->getMessage();
}
/**
* Returns an iterator for the inspected exception's
* frames.
* @return Whoops\Exception\FrameCollection
*/
public function getFrames()
{
if($this->frames === null) {
$frames = $this->exception->getTrace();
// If we're handling an ErrorException thrown by Whoops,
// get rid of the last frame, which matches the handleError method,
// and do not add the current exception to trace. We ensure that
// the next frame does have a filename / linenumber, though.
if($this->exception instanceof ErrorException && empty($frames[1]['line'])) {
$frames = array($this->getFrameFromError($this->exception));
} else {
$firstFrame = $this->getFrameFromException($this->exception);
array_unshift($frames, $firstFrame);
}
$this->frames = new FrameCollection($frames);
}
return $this->frames;
}
/**
* Given an exception, generates an array in the format
* generated by Exception::getTrace()
* @param Exception $exception
* @return array
*/
protected function getFrameFromException(Exception $exception)
{
return array(
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'class' => get_class($exception),
'args' => array(
$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 array(
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'class' => null,
'args' => array()
);
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Handler\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;
/**
* @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();
return call_user_func($this->callable, $exception, $inspector, $run);
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Handler\HandlerInterface;
use Whoops\Exception\Inspector;
use Whoops\Run;
use Exception;
/**
* 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
const LAST_HANDLER = 0x20;
const QUIT = 0x30;
/**
* @var Whoops\Run
*/
private $run;
/**
* @var Whoops\Exception\Inspector $inspector
*/
private $inspector;
/**
* @var Exception $exception
*/
private $exception;
/**
* @param Whoops\Run $run
*/
public function setRun(Run $run)
{
$this->run = $run;
}
/**
* @return Whoops\Run
*/
protected function getRun()
{
return $this->run;
}
/**
* @param Whoops\Exception\Inspector $inspector
*/
public function setInspector(Inspector $inspector)
{
$this->inspector = $inspector;
}
/**
* @return Whoops\Run
*/
protected function getInspector()
{
return $this->inspector;
}
/**
* @param Exception $exception
*/
public function setException(Exception $exception)
{
$this->exception = $exception;
}
/**
* @return Exception
*/
protected function getException()
{
return $this->exception;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Exception\Inspector;
use Whoops\Run;
use Exception;
interface HandlerInterface
{
/**
* @return int|null A handler may return nothing, or a Handler::HANDLE_* constant
*/
public function handle();
/**
* @param Whoops\Run $run
*/
public function setRun(Run $run);
/**
* @param Exception $exception
*/
public function setException(Exception $exception);
/**
* @param Whoops\Exception\Inspector $run
*/
public function setInspector(Inspector $inspector);
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Handler\Handler;
/**
* 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 $onlyForAjaxRequests = false;
/**
* @param bool|null $returnFrames
* @return null|bool
*/
public function addTraceToOutput($returnFrames = null)
{
if(func_num_args() == 0) {
return $this->returnFrames;
}
$this->returnFrames = (bool) $returnFrames;
}
/**
* @param bool|null $onlyForAjaxRequests
* @return null|bool
*/
public function onlyForAjaxRequests($onlyForAjaxRequests = null)
{
if(func_num_args() == 0) {
return $this->onlyForAjaxRequests;
}
$this->onlyForAjaxRequests = (bool) $onlyForAjaxRequests;
}
/**
* Check, if possible, that this execution was triggered by an AJAX request.
* @param bool
*/
private function isAjaxRequest()
{
return (
!empty($_SERVER['HTTP_X_REQUESTED_WITH'])
&& strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
;
}
/**
* @return int
*/
public function handle()
{
if($this->onlyForAjaxRequests() && !$this->isAjaxRequest()) {
return Handler::DONE;
}
$exception = $this->getException();
$response = array(
'error' => array(
'type' => get_class($exception),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine()
)
);
if($this->addTraceToOutput()) {
$inspector = $this->getInspector();
$frames = $inspector->getFrames();
$frameData = array();
foreach($frames as $frame) {
$frameData[] = array(
'file' => $frame->getFile(),
'line' => $frame->getLine(),
'function' => $frame->getFunction(),
'class' => $frame->getClass(),
'args' => $frame->getArgs()
);
}
$response['error']['trace'] = $frameData;
}
echo json_encode($response);
return Handler::QUIT;
}
}

View File

@@ -0,0 +1,328 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Handler\Handler;
use InvalidArgumentException;
class PrettyPageHandler extends Handler
{
/**
* @var string
*/
private $resourcesPath;
/**
* @var array[]
*/
private $extraTables = array();
/**
* @var string
*/
private $pageTitle = 'Whoops! There was an error.';
/**
* A string identifier for a known IDE/text editor, 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
* "txmt://open?url=%file&line=%line"
* @var mixed $editor
*/
protected $editor;
/**
* A list of known editor strings
* @var array
*/
protected $editors = array(
'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'
);
/**
* Constructor.
*/
public function __construct()
{
if (extension_loaded('xdebug')) {
// Register editor using xdebug's file_link_format option.
$this->editors['xdebug'] = function($file, $line) {
return str_replace(array('%f', '%l'), array($file, $line), ini_get('xdebug.file_link_format'));
};
}
}
/**
* @return int|null
*/
public function handle()
{
// Check conditions for outputting HTML:
// @todo: make this more robust
if(php_sapi_name() === 'cli' && !isset($_ENV['whoops-test'])) {
return Handler::DONE;
}
// Get the 'pretty-template.php' template file
// @todo: this can be made more dynamic &&|| cleaned-up
if(!($resources = $this->getResourcesPath())) {
$resources = __DIR__ . '/../Resources';
}
$templateFile = "$resources/pretty-template.php";
// @todo: Make this more reliable,
// possibly by adding methods to append CSS & JS to the page
$cssFile = "$resources/pretty-page.css";
// Prepare the $v global variable that will pass relevant
// information to the template
$inspector = $this->getInspector();
$frames = $inspector->getFrames();
$v = (object) array(
'title' => $this->getPageTitle(),
'name' => explode('\\', $inspector->getExceptionName()),
'message' => $inspector->getException()->getMessage(),
'frames' => $frames,
'hasFrames' => !!count($frames),
'handler' => $this,
'handlers' => $this->getRun()->getHandlers(),
'pageStyle' => file_get_contents($cssFile),
'tables' => array(
'Server/Request Data' => $_SERVER,
'GET Data' => $_GET,
'POST Data' => $_POST,
'Files' => $_FILES,
'Cookies' => $_COOKIE,
'Session' => isset($_SESSION) ? $_SESSION: array(),
'Environment Variables' => $_ENV
)
);
$extraTables = array_map(function($table) {
return $table instanceof \Closure ? $table() : $table;
}, $this->getDataTables());
// Add extra entries list of data tables:
$v->tables = array_merge($extraTables, $v->tables);
call_user_func(function() use($templateFile, $v) {
// $e -> cleanup output, optionally preserving URIs as anchors:
$e = function($_, $allowLinks = false) {
$escaped = htmlspecialchars($_, ENT_QUOTES, 'UTF-8');
// convert URIs to clickable anchor elements:
if($allowLinks) {
$escaped = preg_replace(
'@([A-z]+?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@',
"<a href=\"$1\" target=\"_blank\">$1</a>", $escaped
);
}
return $escaped;
};
// $slug -> sluggify string (i.e: Hello world! -> hello-world)
$slug = function($_) {
$_ = str_replace(" ", "-", $_);
$_ = preg_replace('/[^\w\d\-\_]/i', '', $_);
return strtolower($_);
};
require $templateFile;
});
return Handler::QUIT;
}
/**
* 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
* @param array $data
*/
public function addDataTable($label, array $data)
{
$this->extraTables[$label] = $data;
}
/**
* 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
*/
public function addDataTableCallback($label, /* callable */ $callback)
{
if (!is_callable($callback)) {
throw new InvalidArgumentException('Expecting callback argument to be callable');
}
$this->extraTables[$label] = function() use ($callback) {
try {
$result = call_user_func($callback);
// Only return the result if it can be iterated over by foreach().
return is_array($result) || $result instanceof \Traversable ? $result : array();
} catch (\Exception $e) {
// Don't allow failiure to break the rendering of the original exception.
return array();
}
};
}
/**
* 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[]
*/
public function getDataTables($label = null)
{
if($label !== null) {
return isset($this->extraTables[$label]) ?
$this->extraTables[$label] : array();
}
return $this->extraTables;
}
/**
* Adds an editor resolver, identified by a string
* name, and that may be a string path, or a callable
* resolver. If the callable returns a string, it will
* be set as the file reference's href attribute.
*
* @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 $resolver
*/
public function addEditor($identifier, $resolver)
{
$this->editors[$identifier] = $resolver;
}
/**
* Set the editor to use to open referenced files, by a string
* identifier, or a callable that will be executed for every
* file reference, with a $file and $line argument, and should
* return a string.
*
* @example
* $run->setEditor(function($file, $line) { return "file:///{$file}"; });
* @example
* $run->setEditor('sublime');
*
* @param string|callable $editor
*/
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;
}
/**
* Given a string file path, and an integer file line,
* executes the editor resolver and returns, if available,
* a string that may be used as the href property for that
* file reference.
*
* @param string $filePath
* @param int $line
* @return string|false
*/
public function getEditorHref($filePath, $line)
{
if($this->editor === null) {
return false;
}
$editor = $this->editor;
if(is_string($editor)) {
$editor = $this->editors[$editor];
}
if(is_callable($editor)) {
$editor = call_user_func($editor, $filePath, $line);
}
// Check that the editor is a string, and replace the
// %line and %file placeholders:
if(!is_string($editor)) {
throw new InvalidArgumentException(
__METHOD__ . " should always resolve to a string; got something else instead"
);
}
$editor = str_replace("%line", rawurlencode($line), $editor);
$editor = str_replace("%file", rawurlencode($filePath), $editor);
return $editor;
}
/**
* @var string
*/
public function setPageTitle($title)
{
$this->pageTitle = (string) $title;
}
/**
* @return string
*/
public function getPageTitle()
{
return $this->pageTitle;
}
/**
* @return string
*/
public function getResourcesPath()
{
return $this->resourcesPath;
}
/**
* @param string $resourcesPath
*/
public function setResourcesPath($resourcesPath)
{
if(!is_dir($resourcesPath)) {
throw new InvalidArgumentException(
"$resourcesPath is not a valid directory"
);
}
$this->resourcesPath = $resourcesPath;
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Provider\Phalcon;
use Whoops\Run;
use Whoops\Handler\PrettyPageHandler;
use Phalcon\DI;
use Phalcon\DI\Exception;
class WhoopsServiceProvider
{
/**
* @param Phalcon\DI $di
*/
public function __construct(DI $di = null)
{
if (!$di) {
$di = DI::getDefault();
}
// There's only ever going to be one error page...right?
$di->setShared('whoops.error_page_handler', function() {
return new PrettyPageHandler;
});
// Retrieves info on the Phalcon environment and ships it off
// to the PrettyPageHandler's data tables:
// This works by adding a new handler to the stack that runs
// before the error page, retrieving the shared page handler
// instance, and working with it to add new data tables
$phalcon_info_handler = function() use($di) {
try {
$request = $di['request'];
} catch (Exception $e) {
// This error occurred too early in the application's life
// and the request instance is not yet available.
return;
}
// Request info:
$di['whoops.error_page_handler']->addDataTable('Phalcon Application (Request)', array(
'URI' => $request->getScheme().'://'.$request->getServer('HTTP_HOST').$request->getServer('REQUEST_URI'),
'Request URI' => $request->getServer('REQUEST_URI'),
'Path Info' => $request->getServer('PATH_INFO'),
'Query String'=> $request->getServer('QUERY_STRING') ?: '<none>',
'HTTP Method' => $request->getMethod(),
'Script Name' => $request->getServer('SCRIPT_NAME'),
//'Base Path' => $request->getBasePath(),
//'Base URL' => $request->getBaseUrl(),
'Scheme' => $request->getScheme(),
'Port' => $request->getServer('SERVER_PORT'),
'Host' => $request->getServerName(),
));
};
$di->setShared('whoops', function() use($di, $phalcon_info_handler) {
$run = new Run;
$run->pushHandler($di['whoops.error_page_handler']);
$run->pushHandler($phalcon_info_handler);
return $run;
});
$di['whoops']->register();
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Provider\Silex;
use Whoops\Run;
use Whoops\Handler\PrettyPageHandler;
use Silex\ServiceProviderInterface;
use Silex\Application;
use RuntimeException;
class WhoopsServiceProvider implements ServiceProviderInterface
{
/**
* @see Silex\ServiceProviderInterface::register
* @param Silex\Application $app
*/
public function register(Application $app)
{
// There's only ever going to be one error page...right?
$app['whoops.error_page_handler'] = $app->share(function() {
return new PrettyPageHandler;
});
// Retrieves info on the Silex environment and ships it off
// to the PrettyPageHandler's data tables:
// This works by adding a new handler to the stack that runs
// before the error page, retrieving the shared page handler
// instance, and working with it to add new data tables
$app['whoops.silex_info_handler'] = $app->protect(function() use($app) {
try {
$request = $app['request'];
} catch (RuntimeException $e) {
// This error occurred too early in the application's life
// and the request instance is not yet available.
return;
}
// General application info:
$app['whoops.error_page_handler']->addDataTable('Silex Application', array(
'Charset' => $app['charset'],
'Locale' => $app['locale'],
'Route Class' => $app['route_class'],
'Dispatcher Class' => $app['dispatcher_class'],
'Application Class'=> get_class($app)
));
// Request info:
$app['whoops.error_page_handler']->addDataTable('Silex Application (Request)', array(
'URI' => $request->getUri(),
'Request URI' => $request->getRequestUri(),
'Path Info' => $request->getPathInfo(),
'Query String'=> $request->getQueryString() ?: '<none>',
'HTTP Method' => $request->getMethod(),
'Script Name' => $request->getScriptName(),
'Base Path' => $request->getBasePath(),
'Base URL' => $request->getBaseUrl(),
'Scheme' => $request->getScheme(),
'Port' => $request->getPort(),
'Host' => $request->getHost(),
));
});
$app['whoops'] = $app->share(function() use($app) {
$run = new Run;
$run->pushHandler($app['whoops.error_page_handler']);
$run->pushHandler($app['whoops.silex_info_handler']);
return $run;
});
$app->error(array($app['whoops'], Run::EXCEPTION_HANDLER));
$app['whoops']->register();
}
/**
* @see Silex\ServiceProviderInterface::boot
*/
public function boot(Application $app) {}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* ZF2 Integration for Whoops
* @author Balázs Németh <zsilbi@zsilbi.hu>
*/
namespace Whoops\Provider\Zend;
use Whoops\Run;
use Zend\Mvc\View\Http\ExceptionStrategy as BaseExceptionStrategy;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Application;
class ExceptionStrategy extends BaseExceptionStrategy {
protected $run;
public function __construct(Run $run) {
$this->run = $run;
return $this;
}
public function prepareExceptionViewModel(MvcEvent $event) {
// Do nothing if no error in the event
$error = $event->getError();
if (empty($error)) {
return;
}
// Do nothing if the result is a response object
$result = $event->getResult();
if ($result instanceof Response) {
return;
}
switch ($error) {
case Application::ERROR_CONTROLLER_NOT_FOUND:
case Application::ERROR_CONTROLLER_INVALID:
case Application::ERROR_ROUTER_NO_MATCH:
// Specifically not handling these
return;
case Application::ERROR_EXCEPTION:
default:
$response = $event->getResponse();
if (!$response || $response->getStatusCode() === 200) {
header('HTTP/1.0 500 Internal Server Error', true, 500);
}
ob_clean();
$this->run->handleException($event->getParam('exception'));
break;
}
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* ZF2 Integration for Whoops
* @author Balázs Németh <zsilbi@zsilbi.hu>
*
* The Whoops directory should be added as a module to ZF2 (/vendor/Whoops)
*
* Whoops must be added as the first module
* For example:
* 'modules' => array(
* 'Whoops',
* 'Application',
* ),
*
* This file should be moved next to Whoops/Run.php (/vendor/Whoops/Module.php)
*
*/
namespace Whoops;
use Whoops\Run;
use Whoops\Provider\Zend\ExceptionStrategy;
use Whoops\Provider\Zend\RouteNotFoundStrategy;
use Whoops\Handler\JsonResponseHandler;
use Whoops\Handler\PrettyPageHandler;
use Zend\EventManager\EventInterface;
use Zend\Console\Request as ConsoleRequest;
class Module
{
protected $run;
public function onBootstrap(EventInterface $event)
{
$prettyPageHandler = new PrettyPageHandler();
// Set editor
$config = $event->getApplication()->getServiceManager()->get('Config');
if (isset($config['view_manager']['editor'])) {
$prettyPageHandler->setEditor($config['view_manager']['editor']);
}
$this->run = new Run();
$this->run->register();
$this->run->pushHandler($prettyPageHandler);
$this->attachListeners($event);
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
private function attachListeners(EventInterface $event)
{
$request = $event->getRequest();
$application = $event->getApplication();
$services = $application->getServiceManager();
$events = $application->getEventManager();
$config = $services->get('Config');
//Display exceptions based on configuration and console mode
if ($request instanceof ConsoleRequest || empty($config['view_manager']['display_exceptions']))
return;
$jsonHandler = new JsonResponseHandler();
if (!empty($config['view_manager']['json_exceptions']['show_trace'])) {
//Add trace to the JSON output
$jsonHandler->addTraceToOutput(true);
}
if (!empty($config['view_manager']['json_exceptions']['ajax_only'])) {
//Only return JSON response for AJAX requests
$jsonHandler->onlyForAjaxRequests(true);
}
if (!empty($config['view_manager']['json_exceptions']['display'])) {
//Turn on JSON handler
$this->run->pushHandler($jsonHandler);
}
//Attach the Whoops ExceptionStrategy
$exceptionStrategy = new ExceptionStrategy($this->run);
$exceptionStrategy->attach($events);
//Attach the Whoops RouteNotFoundStrategy
$routeNotFoundStrategy = new RouteNotFoundStrategy($this->run);
$routeNotFoundStrategy->attach($events);
//Detach default ExceptionStrategy
$services->get('Zend\Mvc\View\Http\ExceptionStrategy')->detach($events);
//Detach default RouteNotFoundStrategy
$services->get('Zend\Mvc\View\Http\RouteNotFoundStrategy')->detach($events);
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* ZF2 Integration for Whoops
* @author Balázs Németh <zsilbi@zsilbi.hu>
*/
namespace Whoops\Provider\Zend;
use Whoops\Run;
use Zend\Mvc\View\Http\RouteNotFoundStrategy as BaseRouteNotFoundStrategy;
use Zend\Mvc\MvcEvent;
use Zend\Stdlib\ResponseInterface as Response;
use Zend\View\Model\ViewModel;
class RouteNotFoundStrategy extends BaseRouteNotFoundStrategy {
protected $run;
public function __construct(Run $run) {
$this->run = $run;
}
public function prepareNotFoundViewModel(MvcEvent $e) {
$vars = $e->getResult();
if ($vars instanceof Response) {
// Already have a response as the result
return;
}
$response = $e->getResponse();
if ($response->getStatusCode() != 404) {
// Only handle 404 responses
return;
}
if (!$vars instanceof ViewModel) {
$model = new ViewModel();
if (is_string($vars)) {
$model->setVariable('message', $vars);
} else {
$model->setVariable('message', 'Page not found.');
}
} else {
$model = $vars;
if ($model->getVariable('message') === null) {
$model->setVariable('message', 'Page not found.');
}
}
// If displaying reasons, inject the reason
$this->injectNotFoundReason($model, $e);
// If displaying exceptions, inject
$this->injectException($model, $e);
// Inject controller if we're displaying either the reason or the exception
$this->injectController($model, $e);
ob_clean();
throw new \Exception($model->getVariable('message') . ' ' . $model->getVariable('reason'));
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* ZF2 Integration for Whoops
* @author Balázs Németh <zsilbi@zsilbi.hu>
*
* Example controller configuration
*/
return array(
'view_manager' => array(
'editor' => 'sublime',
'display_not_found_reason' => true,
'display_exceptions' => true,
'json_exceptions' => array(
'display' => true,
'ajax_only' => true,
'show_trace' => true
)
),
);

View File

@@ -0,0 +1,319 @@
.cf:before, .cf:after {content: " ";display: table;} .cf:after {clear: both;} .cf {*zoom: 1;}
body {
font: 14px helvetica, arial, sans-serif;
color: #2B2B2B;
background-color: #D4D4D4;
padding:0;
margin: 0;
max-height: 100%;
}
a {
text-decoration: none;
}
.container{
height: 100%;
width: 100%;
position: fixed;
margin: 0;
padding: 0;
left: 0;
top: 0;
}
.branding {
position: absolute;
top: 10px;
right: 20px;
color: #777777;
font-size: 10px;
z-index: 100;
}
.branding a {
color: #CD3F3F;
}
header {
padding: 30px 20px;
color: white;
background: #272727;
box-sizing: border-box;
border-left: 5px solid #CD3F3F;
}
.exc-title {
margin: 0;
color: #616161;
text-shadow: 0 1px 2px rgba(0, 0, 0, .1);
}
.exc-title-primary { color: #CD3F3F; }
.exc-message {
font-size: 32px;
margin: 5px 0;
word-wrap: break-word;
}
.stack-container {
height: 100%;
position: relative;
}
.details-container {
height: 100%;
overflow: auto;
float: right;
width: 70%;
background: #DADADA;
}
.details {
padding: 10px;
padding-left: 5px;
border-left: 5px solid rgba(0, 0, 0, .1);
}
.frames-container {
height: 100%;
overflow: auto;
float: left;
width: 30%;
background: #FFF;
}
.frame {
padding: 14px;
background: #F3F3F3;
border-right: 1px solid rgba(0, 0, 0, .2);
cursor: pointer;
}
.frame.active {
background-color: #4288CE;
color: #F3F3F3;
box-shadow: inset -2px 0 0 rgba(255, 255, 255, .1);
text-shadow: 0 1px 0 rgba(0, 0, 0, .2);
}
.frame:not(.active):hover {
background: #BEE9EA;
}
.frame-class, .frame-function, .frame-index {
font-weight: bold;
}
.frame-index {
font-size: 11px;
color: #BDBDBD;
}
.frame-class {
color: #4288CE;
}
.active .frame-class {
color: #BEE9EA;
}
.frame-file {
font-family: consolas, monospace;
word-wrap:break-word;
}
.frame-file .editor-link {
color: #272727;
}
.frame-line {
font-weight: bold;
color: #4288CE;
}
.active .frame-line { color: #BEE9EA; }
.frame-line:before {
content: ":";
}
.frame-code {
padding: 10px;
padding-left: 5px;
background: #BDBDBD;
display: none;
border-left: 5px solid #4288CE;
}
.frame-code.active {
display: block;
}
.frame-code .frame-file {
background: #C6C6C6;
color: #525252;
text-shadow: 0 1px 0 #E7E7E7;
padding: 10px 10px 5px 10px;
border-top-right-radius: 6px;
border-top-left-radius: 6px;
border: 1px solid rgba(0, 0, 0, .1);
border-bottom: none;
box-shadow: inset 0 1px 0 #DADADA;
}
.code-block {
padding: 10px;
margin: 0;
box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
}
.linenums {
margin: 0;
margin-left: 10px;
}
.frame-comments {
box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
border: 1px solid rgba(0, 0, 0, .2);
border-top: none;
border-bottom-right-radius: 6px;
border-bottom-left-radius: 6px;
padding: 5px;
font-size: 12px;
background: #404040;
}
.frame-comments.empty {
padding: 8px 15px;
}
.frame-comments.empty:before {
content: "No comments for this stack frame.";
font-style: italic;
color: #828282;
}
.frame-comment {
padding: 10px;
color: #D2D2D2;
}
.frame-comment a {
color: #BEE9EA;
font-weight: bold;
text-decoration: none;
}
.frame-comment a:hover {
color: #4bb1b1;
}
.frame-comment:not(:last-child) {
border-bottom: 1px dotted rgba(0, 0, 0, .3);
}
.frame-comment-context {
font-size: 10px;
font-weight: bold;
color: #86D2B6;
}
.data-table-container label {
font-size: 16px;
font-weight: bold;
color: #4288CE;
margin: 10px 0;
padding: 10px 0;
display: block;
margin-bottom: 5px;
padding-bottom: 5px;
border-bottom: 1px dotted rgba(0, 0, 0, .2);
}
.data-table {
width: 100%;
margin: 10px 0;
}
.data-table tbody {
font: 13px consolas, 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 .empty {
color: rgba(0, 0, 0, .3);
font-style: italic;
}
.handler {
padding: 10px;
font: 14px monospace;
}
.handler.active {
color: #BBBBBB;
background: #989898;
font-weight: bold;
}
/* prettify code style
Uses the Doxy theme as a base */
pre .str, code .str { color: #BCD42A; } /* string */
pre .kwd, code .kwd { color: #4bb1b1; font-weight: bold; } /* keyword*/
pre .com, code .com { color: #888; font-weight: bold; } /* comment */
pre .typ, code .typ { color: #ef7c61; } /* type */
pre .lit, code .lit { color: #BCD42A; } /* literal */
pre .pun, code .pun { color: #fff; font-weight: bold; } /* punctuation */
pre .pln, code .pln { color: #e9e4e5; } /* plaintext */
pre .tag, code .tag { color: #4bb1b1; } /* html/xml tag */
pre .htm, code .htm { color: #dda0dd; } /* html tag */
pre .xsl, code .xsl { color: #d0a0d0; } /* xslt tag */
pre .atn, code .atn { color: #ef7c61; font-weight: normal;} /* html/xml attribute name */
pre .atv, code .atv { color: #bcd42a; } /* html/xml attribute value */
pre .dec, code .dec { color: #606; } /* decimal */
pre.prettyprint, code.prettyprint {
font-family: 'Source Code Pro', Monaco, Consolas, "Lucida Console", monospace;;
background: #333;
color: #e9e4e5;
}
pre.prettyprint {
white-space: pre-wrap;
}
pre.prettyprint a, code.prettyprint a {
text-decoration:none;
}
.linenums li {
color: #A5A5A5;
}
.linenums li.current{
background: rgba(255, 100, 100, .07);
padding-top: 4px;
padding-left: 1px;
}
.linenums li.current.active {
background: rgba(255, 100, 100, .17);
}

View File

@@ -0,0 +1,205 @@
<?php
/**
* Template file for Whoops's pretty error output.
* Check the $v global variable (stdClass) for what's available
* to work with.
* @var $v
*/
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title><?php echo $e($v->title) ?></title>
<style><?php echo $v->pageStyle ?></style>
</head>
<body>
<div class="container">
<div class="stack-container">
<div class="frames-container cf <?php echo (!$v->hasFrames ? 'empty' : '') ?>">
<?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($v->frames as $i => $frame): ?>
<div class="frame <?php echo ($i == 0 ? 'active' : '') ?>" id="frame-line-<?php echo $i ?>">
<div class="frame-method-info">
<span class="frame-index"><?php echo (count($v->frames) - $i - 1) ?>.</span>
<span class="frame-class"><?php echo $e($frame->getClass() ?: '') ?></span>
<span class="frame-function"><?php echo $e($frame->getFunction() ?: '') ?></span>
</div>
<span class="frame-file">
<?php echo ($frame->getFile(true) ?: '<#unknown>') ?><!--
--><span class="frame-line"><?php echo (int) $frame->getLine() ?></span>
</span>
</div>
<?php endforeach ?>
</div>
<div class="details-container cf">
<header>
<div class="exception">
<h3 class="exc-title">
<?php foreach($v->name as $i => $nameSection): ?>
<?php if($i == count($v->name) - 1): ?>
<span class="exc-title-primary"><?php echo $e($nameSection) ?></span>
<?php else: ?>
<?php echo $e($nameSection) . ' \\' ?>
<?php endif ?>
<?php endforeach ?>
</h3>
<p class="exc-message">
<?php echo $e($v->message) ?>
</p>
</div>
</header>
<?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 (!$v->hasFrames ? 'empty' : '') ?>">
<?php foreach($v->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 = $v->handler->getEditorHref($filePath, (int) $line)): ?>
Open:
<a href="<?php echo $editorHref ?>" class="editor-link">
<strong><?php echo $e($filePath ?: '<#unknown>') ?></strong>
</a>
<?php else: ?>
<strong><?php echo $e($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 - 8, 10);
$range = array_map(function($line){ return empty($line) ? ' ' : $line;}, $range);
$start = key($range) + 1;
$code = join("\n", $range);
?>
<pre class="code-block prettyprint linenums:<?php echo $start ?>"><?php echo $e($code) ?></pre>
<?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 $e($context) ?></span>
<?php echo $e($comment, true) ?>
</div>
<?php endforeach ?>
</div>
</div>
<?php endforeach ?>
</div>
<?php /* List data-table values, i.e: $_SERVER, $_GET, .... */ ?>
<div class="details">
<div class="data-table-container" id="data-tables">
<?php foreach($v->tables as $label => $data): ?>
<div class="data-table" id="sg-<?php echo $e($slug($label)) ?>">
<label><?php echo $e($label) ?></label>
<?php if(!empty($data)): ?>
<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 $e($k) ?></td>
<td><?php echo $e(print_r($value, true)) ?></td>
</tr>
<?php endforeach ?>
</table>
<?php else: ?>
<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($v->handlers as $i => $handler): ?>
<div class="handler <?php echo ($handler === $v->handler) ? 'active' : ''?>">
<?php echo $i ?>. <?php echo $e(get_class($handler)) ?>
</div>
<?php endforeach ?>
</div>
</div> <!-- .details -->
</div>
</div>
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function() {
prettyPrint();
var $frameLines = $('[id^="frame-line-"]');
var $activeLine = $('.frames-container .active');
var $activeFrame = $('.active[id^="frame-code-"]').show();
var $container = $('.details-container');
var headerHeight = $('header').css('height');
var highlightCurrentLine = function() {
// Highlight the active and neighboring lines for this frame:
var activeLineNumber = +($activeLine.find('.frame-line').text());
var $lines = $activeFrame.find('.linenums li');
var firstLine = +($lines.first().val());
$($lines[activeLineNumber - firstLine - 1]).addClass('current');
$($lines[activeLineNumber - firstLine]).addClass('current active');
$($lines[activeLineNumber - firstLine + 1]).addClass('current');
}
// Highlight the active for the first frame:
highlightCurrentLine();
$frameLines.click(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;
highlightCurrentLine();
$container.animate({ scrollTop: headerHeight }, "fast");
}
});
});
</script>
</body>
</html>

253
vendor/filp/whoops/src/Whoops/Run.php vendored Normal file
View File

@@ -0,0 +1,253 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops;
use Whoops\Handler\HandlerInterface;
use Whoops\Handler\Handler;
use Whoops\Handler\CallbackHandler;
use Whoops\Exception\Inspector;
use Whoops\Exception\ErrorException;
use InvalidArgumentException;
use Exception;
class Run
{
const EXCEPTION_HANDLER = 'handleException';
const ERROR_HANDLER = 'handleError';
const SHUTDOWN_HANDLER = 'handleShutdown';
protected $isRegistered;
protected $allowQuit = true;
protected $sendOutput = true;
/**
* @var Whoops\Handler\HandlerInterface[]
*/
protected $handlerStack = array();
/**
* Pushes a handler to the end of the stack.
* @param Whoops\HandlerInterface $handler
* @return Whoops\Run
*/
public function pushHandler($handler)
{
if(is_callable($handler)) {
$handler = new CallbackHandler($handler);
}
if(!$handler instanceof HandlerInterface) {
throw new InvalidArgumentException(
'Argument to ' . __METHOD__ . ' must be a callable, or instance of'
. 'Whoops\\Handler\\HandlerInterface'
);
}
$this->handlerStack[] = $handler;
return $this;
}
/**
* Removes the last handler in the stack and returns it.
* Returns null if there's nothing else to pop.
* @return null|Whoops\Handler\HandlerInterface
*/
public function popHandler()
{
return array_pop($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 Whoops\Run
*/
public function clearHandlers()
{
$this->handlerStack = array();
return $this;
}
/**
* @param Exception $exception
* @return Whoops\Exception\Inspector
*/
protected function getInspector(Exception $exception)
{
return new Inspector($exception);
}
/**
* Registers this instance as an error handler.
* @return Whoops\Run
*/
public function register()
{
if(!$this->isRegistered) {
set_error_handler(array($this, self::ERROR_HANDLER));
set_exception_handler(array($this, self::EXCEPTION_HANDLER));
register_shutdown_function(array($this, self::SHUTDOWN_HANDLER));
$this->isRegistered = true;
}
return $this;
}
/**
* Unregisters all handlers registered by this Whoops\Run instance
* @return Whoops\Run
*/
public function unregister()
{
if($this->isRegistered) {
restore_exception_handler();
restore_error_handler();
$this->isRegistered = false;
}
return $this;
}
/**
* Should Whoops allow Handlers to force the script to quit?
* @param bool|num $exit
* @return bool
*/
public function allowQuit($exit = null)
{
if(func_num_args() == 0) {
return $this->allowQuit;
}
return $this->allowQuit = (bool) $exit;
}
/**
* Should Whoops push output directly to the client?
* If this is false, output will be returned by handleException
* @param bool|num $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 Exception $exception
* @return string Output generated by handlers
*/
public function handleException(Exception $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.
ob_start();
for($i = count($this->handlerStack) - 1; $i >= 0; $i--) {
$handler = $this->handlerStack[$i];
$handler->setRun($this);
$handler->setInspector($inspector);
$handler->setException($exception);
$handlerResponse = $handler->handle($exception);
if(in_array($handlerResponse, array(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;
}
}
$output = ob_get_clean();
// 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 we're going to quit execution, cleanup all other output
// buffers before sending our own output:
if($handlerResponse == Handler::QUIT && $this->allowQuit()) {
while (ob_get_level() > 0) ob_end_clean();
}
echo $output;
}
// Handlers are done! Check if we got here because of Handler::QUIT
// ($handlerResponse will be the response from the last queried handler)
// and if so, try to quit execution.
if($handlerResponse == Handler::QUIT && $this->allowQuit()) {
exit;
}
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 $file
* @param int $line
*/
public function handleError($level, $message, $file = null, $line = null)
{
if ($level & error_reporting()) {
$this->handleException(
new ErrorException(
$message, $level, 0, $file, $line
)
);
}
}
/**
* Special case to deal with Fatal errors and the like.
*/
public function handleShutdown()
{
if($error = error_get_last()) {
$this->handleError(
$error['type'],
$error['message'],
$error['file'],
$error['line']
);
}
}
}

View File

@@ -0,0 +1,150 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Exception\FrameCollection;
use Whoops\TestCase;
use Mockery as m;
class FrameCollectionTest extends TestCase
{
/**
* Stupid little counter for tagging frames
* with a unique but predictable id
* @var int
*/
private $frameIdCounter = 0;
/**
* @return array
*/
public function getFrameData()
{
$id = ++$this->frameIdCounter;
return array(
'file' => __DIR__ . '/../../fixtures/frame.lines-test.php',
'line' => $id,
'function' => 'test-' . $id,
'class' => 'MyClass',
'args' => array(true, 'hello')
);
}
/**
* @param int $total
* @return array
*/
public function getFrameDataList($total)
{
$total = max((int) $total, 1);
$self = $this;
$frames = array_map(function() use($self) {
return $self->getFrameData();
}, range(1, $total));
return $frames;
}
/**
* @param array $frames
* @return Whoops\Exception\FrameCollection
*/
private function getFrameCollectionInstance($frames = null)
{
if($frames === null) {
$frames = $this->getFrameDataList(10);
}
return new FrameCollection($frames);
}
/**
* @covers Whoops\Exception\FrameCollection::filter
* @covers Whoops\Exception\FrameCollection::count
*/
public function testFilterFrames()
{
$frames = $this->getFrameCollectionInstance();
// Filter out all frames with a line number under 6
$frames->filter(function($frame) {
return $frame->getLine() <= 5;
});
$this->assertCount(5, $frames);
}
/**
* @covers Whoops\Exception\FrameCollection::map
*/
public function testMapFrames()
{
$frames = $this->getFrameCollectionInstance();
// Filter out all frames with a line number under 6
$frames->map(function($frame) {
$frame->addComment("This is cool", "test");
return $frame;
});
$this->assertCount(10, $frames);
}
/**
* @covers Whoops\Exception\FrameCollection::map
* @expectedException UnexpectedValueException
*/
public function testMapFramesEnforceType()
{
$frames = $this->getFrameCollectionInstance();
// Filter out all frames with a line number under 6
$frames->map(function($frame) {
return "bajango";
});
}
/**
* @covers Whoops\Exception\FrameCollection::getArray
*/
public function testGetArray()
{
$frames = $this->getFrameCollectionInstance();
$frames = $frames->getArray();
$this->assertCount(10, $frames);
foreach($frames as $frame) {
$this->assertInstanceOf('Whoops\\Exception\\Frame', $frame);
}
}
/**
* @covers Whoops\Exception\FrameCollection::getIterator
*/
public function testCollectionIsIterable()
{
$frames = $this->getFrameCollectionInstance();
foreach($frames as $frame) {
$this->assertInstanceOf('Whoops\\Exception\\Frame', $frame);
}
}
/**
* @covers Whoops\Exception\FrameCollection::serialize
* @covers Whoops\Exception\FrameCollection::unserialize
*/
public function testCollectionIsSerializable()
{
$frames = $this->getFrameCollectionInstance();
$serializedFrames = serialize($frames);
$newFrames = unserialize($serializedFrames);
foreach($newFrames as $frame) {
$this->assertInstanceOf('Whoops\\Exception\\Frame', $frame);
}
}
}

View File

@@ -0,0 +1,209 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Exception\Frame;
use Whoops\TestCase;
use Mockery as m;
class FrameTest extends TestCase
{
/**
* @return array
*/
private function getFrameData()
{
return array(
'file' => __DIR__ . '/../../fixtures/frame.lines-test.php',
'line' => 0,
'function' => 'test',
'class' => 'MyClass',
'args' => array(true, 'hello')
);
}
/**
* @param array $data
* @return Whoops\Exception\Frame
*/
private function getFrameInstance($data = null)
{
if($data === null) {
$data = $this->getFrameData();
}
return new Frame($data);
}
/**
* @covers Whoops\Exception\Frame::getFile
*/
public function testGetFile()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance($data);
$this->assertEquals($frame->getFile(), $data['file']);
}
/**
* @covers Whoops\Exception\Frame::getLine
*/
public function testGetLine()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance($data);
$this->assertEquals($frame->getLine(), $data['line']);
}
/**
* @covers Whoops\Exception\Frame::getClass
*/
public function testGetClass()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance($data);
$this->assertEquals($frame->getClass(), $data['class']);
}
/**
* @covers Whoops\Exception\Frame::getFunction
*/
public function testGetFunction()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance($data);
$this->assertEquals($frame->getFunction(), $data['function']);
}
/**
* @covers Whoops\Exception\Frame::getArgs
*/
public function testGetArgs()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance($data);
$this->assertEquals($frame->getArgs(), $data['args']);
}
/**
* @covers Whoops\Exception\Frame::getFileContents
*/
public function testGetFileContents()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance($data);
$this->assertEquals($frame->getFileContents(), file_get_contents($data['file']));
}
/**
* @covers Whoops\Exception\Frame::getFileLines
*/
public function testGetFileLines()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance($data);
$lines = explode("\n", $frame->getFileContents());
$this->assertEquals($frame->getFileLines(), $lines);
}
/**
* @covers Whoops\Exception\Frame::getFileLines
*/
public function testGetFileLinesRange()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance($data);
$lines = $frame->getFileLines(0, 3);
$this->assertEquals($lines[0], '<?php');
$this->assertEquals($lines[1], '// Line 2');
$this->assertEquals($lines[2], '// Line 3');
}
/**
* @covers Whoops\Exception\Frame::addComment
* @covers Whoops\Exception\Frame::getComments
*/
public function testGetComments()
{
$frame = $this->getFrameInstance();
$testComments = array(
'Dang, yo!',
'Errthangs broken!',
'Dayumm!'
);
$frame->addComment($testComments[0]);
$frame->addComment($testComments[1]);
$frame->addComment($testComments[2]);
$comments = $frame->getComments();
$this->assertCount(3, $comments);
$this->assertEquals($comments[0]['comment'], $testComments[0]);
$this->assertEquals($comments[1]['comment'], $testComments[1]);
$this->assertEquals($comments[2]['comment'], $testComments[2]);
}
/**
* @covers Whoops\Exception\Frame::addComment
* @covers Whoops\Exception\Frame::getComments
*/
public function testGetFilteredComments()
{
$frame = $this->getFrameInstance();
$testComments = array(
array('Dang, yo!', 'test'),
array('Errthangs broken!', 'test'),
'Dayumm!'
);
$frame->addComment($testComments[0][0], $testComments[0][1]);
$frame->addComment($testComments[1][0], $testComments[1][1]);
$frame->addComment($testComments[2][0], $testComments[2][1]);
$comments = $frame->getComments('test');
$this->assertCount(2, $comments);
$this->assertEquals($comments[0]['comment'], $testComments[0][0]);
$this->assertEquals($comments[1]['comment'], $testComments[1][0]);
}
/**
* @covers Whoops\Exception\Frame::serialize
* @covers Whoops\Exception\Frame::unserialize
*/
public function testFrameIsSerializable()
{
$data = $this->getFrameData();
$frame = $this->getFrameInstance();
$commentText = "Gee I hope this works";
$commentContext = "test";
$frame->addComment($commentText, $commentContext);
$serializedFrame = serialize($frame);
$newFrame = unserialize($serializedFrame);
$this->assertInstanceOf('Whoops\\Exception\\Frame', $newFrame);
$this->assertEquals($newFrame->getFile(), $data['file']);
$this->assertEquals($newFrame->getLine(), $data['line']);
$comments = $newFrame->getComments();
$this->assertCount(1, $comments);
$this->assertEquals($comments[0]["comment"], $commentText);
$this->assertEquals($comments[0]["context"], $commentContext);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Exception\Inspector;
use Whoops\TestCase;
use RuntimeException;
use Exception;
use Mockery as m;
class InspectorTest extends TestCase
{
/**
* @param string $message
* @return Exception
*/
protected function getException($message = null)
{
return m::mock('Exception', array($message));
}
/**
* @param Exception $exception|null
* @return Whoops\Exception\Inspector
*/
protected function getInspectorInstance(Exception $exception = null)
{
return new Inspector($exception);
}
/**
* @covers Whoops\Exception\Inspector::getExceptionName
*/
public function testReturnsCorrectExceptionName()
{
$exception = $this->getException();
$inspector = $this->getInspectorInstance($exception);
$this->assertEquals(get_class($exception), $inspector->getExceptionName());
}
/**
* @covers Whoops\Exception\Inspector::__construct
* @covers Whoops\Exception\Inspector::getException
*/
public function testExceptionIsStoredAndReturned()
{
$exception = $this->getException();
$inspector = $this->getInspectorInstance($exception);
$this->assertSame($exception, $inspector->getException());
}
/**
* @covers Whoops\Exception\Inspector::getFrames
*/
public function testGetFramesReturnsCollection()
{
$exception = $this->getException();
$inspector = $this->getInspectorInstance($exception);
$this->assertInstanceOf('Whoops\\Exception\\FrameCollection', $inspector->getFrames());
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\TestCase;
use Whoops\Handler\JsonResponseHandler;
use RuntimeException;
class JsonResponseHandlerTest extends TestCase
{
/**
* @return Whoops\Handler\JsonResponseHandler
*/
private function getHandler()
{
return new JsonResponseHandler;
}
/**
* @return RuntimeException
*/
public function getException($message = 'test message')
{
return new RuntimeException($message);
}
/**
* @param bool $withTrace
* @return array
*/
private function getJsonResponseFromHandler($withTrace = false)
{
$handler = $this->getHandler();
$handler->addTraceToOutput($withTrace);
$run = $this->getRunInstance();
$run->pushHandler($handler);
$run->register();
$exception = $this->getException();
ob_start();
$run->handleException($exception);
$json = json_decode(ob_get_clean(), true);
// Check that the json response is parse-able:
$this->assertEquals(json_last_error(), JSON_ERROR_NONE);
return $json;
}
/**
* @covers Whoops\Handler\JsonResponseHandler::addTraceToOutput
* @covers Whoops\Handler\JsonResponseHandler::handle
*/
public function testReturnsWithoutFrames()
{
$json = $this->getJsonResponseFromHandler($withTrace = false);
// Check that the response has the expected keys:
$this->assertArrayHasKey('error', $json);
$this->assertArrayHasKey('type', $json['error']);
$this->assertArrayHasKey('file', $json['error']);
$this->assertArrayHasKey('line', $json['error']);
// Check the field values:
$this->assertEquals($json['error']['file'], __FILE__);
$this->assertEquals($json['error']['message'], 'test message');
$this->assertEquals($json['error']['type'], get_class($this->getException()));
// Check that the trace is NOT returned:
$this->assertArrayNotHasKey('trace', $json['error']);
}
/**
* @covers Whoops\Handler\JsonResponseHandler::addTraceToOutput
* @covers Whoops\Handler\JsonResponseHandler::handle
*/
public function testReturnsWithFrames()
{
$json = $this->getJsonResponseFromHandler($withTrace = true);
// Check that the trace is returned:
$this->assertArrayHasKey('trace', $json['error']);
// Check that a random frame has the expected fields
$traceFrame = reset($json['error']['trace']);
$this->assertArrayHasKey('file', $traceFrame);
$this->assertArrayHasKey('line', $traceFrame);
$this->assertArrayHasKey('function', $traceFrame);
$this->assertArrayHasKey('class', $traceFrame);
$this->assertArrayHasKey('args', $traceFrame);
}
}

View File

@@ -0,0 +1,268 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\TestCase;
use Whoops\Handler\PrettyPageHandler;
use RuntimeException;
use InvalidArgumentException;
class PrettyPageHandlerTest extends TestCase
{
/**
* @return Whoops\Handler\JsonResponseHandler
*/
private function getHandler()
{
return new PrettyPageHandler;
}
/**
* @return RuntimeException
*/
public function getException()
{
return new RuntimeException;
}
/**
* Test that PrettyPageHandle handles the template without
* any errors.
* @covers Whoops\Handler\PrettyPageHandler::handle
*/
public function testHandleWithoutErrors()
{
$run = $this->getRunInstance();
$handler = $this->getHandler();
$run->pushHandler($handler);
ob_start();
$run->handleException($this->getException());
ob_get_clean();
}
/**
* @covers Whoops\Handler\PrettyPageHandler::setPageTitle
* @covers Whoops\Handler\PrettyPageHandler::getPageTitle
*/
public function testGetSetPageTitle()
{
$title = 'My Cool Error Handler';
$handler = $this->getHandler();
$handler->setPageTitle($title);
$this->assertEquals($title, $handler->getPagetitle());
}
/**
* @covers Whoops\Handler\PrettyPageHandler::setResourcesPath
* @covers Whoops\Handler\PrettyPageHandler::getResourcesPath
*/
public function testGetSetResourcesPath()
{
$path = __DIR__; // guaranteed to be valid!
$handler = $this->getHandler();
$handler->setResourcesPath($path);
$this->assertEquals($path, $handler->getResourcesPath());
}
/**
* @covers Whoops\Handler\PrettyPageHandler::setResourcesPath
* @expectedException InvalidArgumentException
*/
public function testSetInvalidResourcesPath()
{
$path = __DIR__ . '/ZIMBABWE'; // guaranteed to be invalid!
$this->getHandler()->setResourcesPath($path);
}
/**
* @covers Whoops\Handler\PrettyPageHandler::getDataTables
* @covers Whoops\Handler\PrettyPageHandler::addDataTable
*/
public function testGetSetDataTables()
{
$handler = $this->getHandler();
// should have no tables by default:
$this->assertEmpty($handler->getDataTables());
$tableOne = array(
'ice' => 'cream',
'ice-ice' => 'baby'
);
$tableTwo = array(
'dolan' =>'pls',
'time' => time()
);
$handler->addDataTable('table 1', $tableOne);
$handler->addDataTable('table 2', $tableTwo);
// should contain both tables:
$tables = $handler->getDataTables();
$this->assertCount(2, $tables);
$this->assertEquals($tableOne, $tables['table 1']);
$this->assertEquals($tableTwo, $tables['table 2']);
// should contain only table 1
$this->assertEquals($tableOne, $handler->getDataTables('table 1'));
// should return an empty table:
$this->assertEmpty($handler->getDataTables('ZIMBABWE!'));
}
/**
* @covers Whoops\Handler\PrettyPageHandler::getDataTables
* @covers Whoops\Handler\PrettyPageHandler::addDataTableCallback
*/
public function testSetCallbackDataTables()
{
$handler = $this->getHandler();
$this->assertEmpty($handler->getDataTables());
$table1 = function() {
return array(
'hammer' => 'time',
'foo' => 'bar',
);
};
$expected1 = array('hammer' => 'time', 'foo' => 'bar');
$table2 = function() use ($expected1) {
return array(
'another' => 'table',
'this' => $expected1,
);
};
$expected2 = array('another' => 'table', 'this' => $expected1);
$table3 = create_function('', 'return array("oh my" => "how times have changed!");');
$expected3 = array('oh my' => 'how times have changed!');
// Sanity check, make sure expected values really are correct.
$this->assertSame($expected1, $table1());
$this->assertSame($expected2, $table2());
$this->assertSame($expected3, $table3());
$handler->addDataTableCallback('table1', $table1);
$handler->addDataTableCallback('table2', $table2);
$handler->addDataTableCallback('table3', $table3);
$tables = $handler->getDataTables();
$this->assertCount(3, $tables);
// Supplied callable is wrapped in a closure
$this->assertInstanceOf('Closure', $tables['table1']);
$this->assertInstanceOf('Closure', $tables['table2']);
$this->assertInstanceOf('Closure', $tables['table3']);
// Run each wrapped callable and check results against expected output.
$this->assertEquals($expected1, $tables['table1']());
$this->assertEquals($expected2, $tables['table2']());
$this->assertEquals($expected3, $tables['table3']());
$this->assertSame($tables['table1'], $handler->getDataTables('table1'));
$this->assertSame($expected1, call_user_func($handler->getDataTables('table1')));
}
/**
* @covers Whoops\Handler\PrettyPageHandler::setEditor
* @covers Whoops\Handler\PrettyPageHandler::getEditorHref
*/
public function testSetEditorSimple()
{
$handler = $this->getHandler();
$handler->setEditor('sublime');
$this->assertEquals(
$handler->getEditorHref('/foo/bar.php', 10),
'subl://open?url=file://%2Ffoo%2Fbar.php&line=10'
);
$this->assertEquals(
$handler->getEditorHref('/foo/with space?.php', 2324),
'subl://open?url=file://%2Ffoo%2Fwith%20space%3F.php&line=2324'
);
$this->assertEquals(
$handler->getEditorHref('/foo/bar/with-dash.php', 0),
'subl://open?url=file://%2Ffoo%2Fbar%2Fwith-dash.php&line=0'
);
}
/**
* @covers Whoops\Handler\PrettyPageHandler::setEditor
* @covers Whoops\Handler\PrettyPageHandler::getEditorHref
*/
public function testSetEditorCallable()
{
$handler = $this->getHandler();
$handler->setEditor(function($file, $line) {
$file = rawurlencode($file);
$line = rawurlencode($line);
return "http://google.com/search/?q=$file:$line";
});
$this->assertEquals(
$handler->getEditorHref('/foo/bar.php', 10),
'http://google.com/search/?q=%2Ffoo%2Fbar.php:10'
);
}
/**
* @covers Whoops\Handler\PrettyPageHandler::setEditor
* @covers Whoops\Handler\PrettyPageHandler::addEditor
* @covers Whoops\Handler\PrettyPageHandler::getEditorHref
*/
public function testAddEditor()
{
$handler = $this->getHandler();
$handler->addEditor('test-editor', function($file, $line) {
return "cool beans $file:$line";
});
$handler->setEditor('test-editor');
$this->assertEquals(
$handler->getEditorHref('hello', 20),
'cool beans hello:20'
);
}
public function testEditorXdebug()
{
if (!extension_loaded('xdebug')) {
$this->markTestSkipped('xdebug is not available');
}
$originalValue = ini_get('xdebug.file_link_format');
$handler = $this->getHandler();
$handler->setEditor('xdebug');
ini_set('xdebug.file_link_format', '%f:%l');
$this->assertEquals(
'/foo/bar.php:10',
$handler->getEditorHref('/foo/bar.php', 10)
);
ini_set('xdebug.file_link_format', 'subl://open?url=%f&line=%l');
// xdebug doesn't do any URL encoded, matching that behaviour.
$this->assertEquals(
'subl://open?url=/foo/with space?.php&line=2324',
$handler->getEditorHref('/foo/with space?.php', 2324)
);
ini_set('xdebug.file_link_format', $originalValue);
}
}

330
vendor/filp/whoops/tests/Whoops/RunTest.php vendored Executable file
View File

@@ -0,0 +1,330 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops;
use Whoops\TestCase;
use Whoops\Run;
use Whoops\Handler\Handler;
use RuntimeException;
use ArrayObject;
use Mockery as m;
class RunTest extends TestCase
{
/**
* @param string $message
* @return Exception
*/
protected function getException($message = null)
{
return m::mock('Exception', array($message));
}
/**
* @return Whoops\Handler\Handler
*/
protected function getHandler()
{
return m::mock('Whoops\\Handler\\Handler')
->shouldReceive('setRun')
->andReturn(null)
->mock()
->shouldReceive('setInspector')
->andReturn(null)
->mock()
->shouldReceive('setException')
->andReturn(null)
->mock()
;
}
/**
* @covers Whoops\Run::clearHandlers
*/
public function testClearHandlers()
{
$run = $this->getRunInstance();
$run->clearHandlers();
$handlers = $run->getHandlers();
$this->assertEmpty($handlers);
}
/**
* @covers Whoops\Run::pushHandler
*/
public function testPushHandler()
{
$run = $this->getRunInstance();
$run->clearHandlers();
$handlerOne = $this->getHandler();
$handlerTwo = $this->getHandler();
$run->pushHandler($handlerOne);
$run->pushHandler($handlerTwo);
$handlers = $run->getHandlers();
$this->assertCount(2, $handlers);
$this->assertContains($handlerOne, $handlers);
$this->assertContains($handlerTwo, $handlers);
}
/**
* @expectedException InvalidArgumentException
* @covers Whoops\Run::pushHandler
*/
public function testPushInvalidHandler()
{
$run = $this->getRunInstance();
$run->pushHandler($banana = 'actually turnip');
}
/**
* @covers Whoops\Run::pushHandler
*/
public function testPushClosureBecomesHandler()
{
$run = $this->getRunInstance();
$run->pushHandler(function() {});
$this->assertInstanceOf('Whoops\\Handler\\CallbackHandler', $run->popHandler());
}
/**
* @covers Whoops\Run::popHandler
* @covers Whoops\Run::getHandlers
*/
public function testPopHandler()
{
$run = $this->getRunInstance();
$handlerOne = $this->getHandler();
$handlerTwo = $this->getHandler();
$handlerThree = $this->getHandler();
$run->pushHandler($handlerOne);
$run->pushHandler($handlerTwo);
$run->pushHandler($handlerThree);
$this->assertSame($handlerThree, $run->popHandler());
$this->assertSame($handlerTwo, $run->popHandler());
$this->assertSame($handlerOne, $run->popHandler());
// Should return null if there's nothing else in
// the stack
$this->assertNull($run->popHandler());
// Should be empty since we popped everything off
// the stack:
$this->assertEmpty($run->getHandlers());
}
/**
* @covers Whoops\Run::register
*/
public function testRegisterHandler()
{
$this->markTestSkipped("Need to test exception handler");
$run = $this->getRunInstance();
$run->register();
$handler = $this->getHandler();
$run->pushHandler($handler);
throw $this->getException();
$this->assertCount(2, $handler->exceptions);
}
/**
* @covers Whoops\Run::unregister
* @expectedException Exception
*/
public function testUnregisterHandler()
{
$run = $this->getRunInstance();
$run->register();
$handler = $this->getHandler();
$run->pushHandler($handler);
$run->unregister();
throw $this->getException("I'm not supposed to be caught!");
}
/**
* @covers Whoops\Run::pushHandler
* @covers Whoops\Run::getHandlers
*/
public function testHandlerHoldsOrder()
{
$run = $this->getRunInstance();
$handlerOne = $this->getHandler();
$handlerTwo = $this->getHandler();
$handlerThree = $this->getHandler();
$handlerFour = $this->getHandler();
$run->pushHandler($handlerOne);
$run->pushHandler($handlerTwo);
$run->pushHandler($handlerThree);
$run->pushHandler($handlerFour);
$handlers = $run->getHandlers();
$this->assertSame($handlers[0], $handlerOne);
$this->assertSame($handlers[1], $handlerTwo);
$this->assertSame($handlers[2], $handlerThree);
$this->assertSame($handlers[3], $handlerFour);
}
/**
* @todo possibly split this up a bit and move
* some of this test to Handler unit tests?
* @covers Whoops\Run::handleException
*/
public function testHandlersGonnaHandle()
{
$run = $this->getRunInstance();
$exception = $this->getException();
$order = new ArrayObject;
$handlerOne = $this->getHandler();
$handlerTwo = $this->getHandler();
$handlerThree = $this->getHandler();
$handlerOne->shouldReceive('handle')
->andReturnUsing(function() use($order) { $order[] = 1; });
$handlerTwo->shouldReceive('handle')
->andReturnUsing(function() use($order) { $order[] = 2; });
$handlerThree->shouldReceive('handle')
->andReturnUsing(function() use($order) { $order[] = 3; });
$run->pushHandler($handlerOne);
$run->pushHandler($handlerTwo);
$run->pushHandler($handlerThree);
// Get an exception to be handled, and verify that the handlers
// are given the handler, and in the inverse order they were
// registered.
$run->handleException($exception);
$this->assertEquals((array) $order, array(3, 2, 1));
}
/**
* @covers Whoops\Run::handleException
*/
public function testLastHandler()
{
$run = $this->getRunInstance();
$handlerOne = $this->getHandler();
$handlerTwo = $this->getHandler();
$run->pushHandler($handlerOne);
$run->pushHandler($handlerTwo);
$test = $this;
$handlerOne
->shouldReceive('handle')
->andReturnUsing(function () use($test) {
$test->fail('$handlerOne should not be called');
})
;
$handlerTwo
->shouldReceive('handle')
->andReturn(Handler::LAST_HANDLER)
;
$run->handleException($this->getException());
}
/**
* Test error suppression using @ operator.
*/
public function testErrorSuppression()
{
$run = $this->getRunInstance();
$run->register();
$handler = $this->getHandler();
$run->pushHandler($handler);
$test = $this;
$handler
->shouldReceive('handle')
->andReturnUsing(function () use($test) {
$test->fail('$handler should not be called, error not suppressed');
})
;
@trigger_error("Test error suppression");
}
/**
* Test to make sure that error_reporting is respected.
*/
public function testErrorReporting()
{
$run = $this->getRunInstance();
$run->register();
$handler = $this->getHandler();
$run->pushHandler($handler);
$test = $this;
$handler
->shouldReceive('handle')
->andReturnUsing(function () use($test) {
$test->fail('$handler should not be called, error_reporting not respected');
})
;
$oldLevel = error_reporting(E_ALL ^ E_USER_NOTICE);
trigger_error("Test error reporting", E_USER_NOTICE);
error_reporting($oldLevel);
}
/**
* @covers Whoops\Run::handleException
* @covers Whoops\Run::writeToOutput
*/
public function testOutputIsSent()
{
$run = $this->getRunInstance();
$run->pushHandler(function() {
echo "hello there";
});
ob_start();
$run->handleException(new RuntimeException);
$this->assertEquals("hello there", ob_get_clean());
}
/**
* @covers Whoops\Run::handleException
* @covers Whoops\Run::writeToOutput
*/
public function testOutputIsNotSent()
{
$run = $this->getRunInstance();
$run->writeToOutput(false);
$run->pushHandler(function() {
echo "hello there";
});
ob_start();
$this->assertEquals("hello there", $run->handleException(new RuntimeException));
$this->assertEquals("", ob_get_clean());
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops;
use Whoops\Run;
class TestCase extends \PHPUnit_Framework_TestCase
{
/**
* @return Whoops\Run
*/
protected function getRunInstance()
{
$run = new Run;
$run->allowQuit(false);
return $run;
}
}

11
vendor/filp/whoops/tests/bootstrap.php vendored Normal file
View File

@@ -0,0 +1,11 @@
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*
* Bootstraper for PHPUnit tests.
*/
error_reporting(E_ALL | E_STRICT);
$_ENV['whoops-test'] = true;
$loader = require_once __DIR__ . '/../vendor/autoload.php';
$loader->add('Whoops\\', __DIR__);

View File

@@ -0,0 +1,10 @@
<?php
// Line 2
// Line 3
// Line 4
// Line 5
// Line 6
// Line 7
// Line 8
// Line 9
// Line 10