From b22af293be1d14c45f19658c7fef0a2b6585590c Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Wed, 19 Feb 2014 22:55:57 +0000 Subject: [PATCH] Moved over docs from wiki --- CONTRIBUTING.md | 4 +- README.md | 10 +- docs/API Documentation.md | 393 +++++++++++++++++++++++++++++ docs/Framework Integration.md | 120 +++++++++ docs/Open Files In An Editor.md | 49 ++++ docs/Possible Upcoming Features.md | 16 ++ 6 files changed, 585 insertions(+), 7 deletions(-) create mode 100644 docs/API Documentation.md create mode 100644 docs/Framework Integration.md create mode 100644 docs/Open Files In An Editor.md create mode 100644 docs/Possible Upcoming Features.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4542942..3209266 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,10 @@ If you want to give me some feedback or make a suggestion, create an [issue on GitHub](https://github.com/filp/whoops/issues/new). If you want to get your hands dirty, great! Here's a couple of steps/guidelines: -- See [a list of possible features to add](https://github.com/filp/whoops/wiki/Possible-features-to-add) for ideas on what can be improved. +- See [a list of possible features to add](https://github.com/filp/whoops/blob/master/docs/Possible%20Upcoming%20Features.md) for ideas on what can be improved. - Add tests for your changes (in `tests/`). - Remember to stick to the existing code style as best as possible. When in doubt, follow `PSR-2`. - Before investing a lot of time coding, create an issue to get our opinion on your big changes. -- If you want to add an integration to a web framework, please [review our guidelines for that](https://github.com/filp/whoops/wiki/Contributing-an-integration-with-a-framework). +- If you want to add an integration to a web framework, please [review our guidelines for that](https://github.com/filp/whoops/blob/master/docs/Framework%20Integration.md#contributing-an-integration-with-a-framework). In `PrettyPageHandler` we are using a Zepto library, but if you are only familiar with jQuery, note that it is pretty much identical. diff --git a/README.md b/README.md index a08fac4..7bfb543 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ powerful stacked error handling system. - Stand-alone library with (currently) no required dependencies - Simple API for dealing with exceptions, trace frames & their data - Includes a pretty rad error page for your webapp projects -- Includes the ability to [open referenced files directly in your editor and IDE](https://github.com/filp/whoops/wiki/Open-files-in-editor) +- Includes the ability to [open referenced files directly in your editor and IDE](https://github.com/filp/whoops/blob/master/docs/Open%20Files%20In%20An%20Editor.md) - Includes handlers for different response formats (JSON, XML, SOAP) - 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/) @@ -37,15 +37,15 @@ Whoops can be easily integrated into many web frameworks. If you use Laravel 4, you already have Whoops. For other frameworks, see instructions on how to integrate Whoops into -[Silex](https://github.com/filp/whoops/wiki/Integrating-with-Silex), -[Phalcon](https://github.com/filp/whoops/wiki/Integrating-with-Phalcon), +[Silex](https://github.com/filp/whoops/blob/master/docs/Framework%20Integration.md#integrating-with-Silex), +[Phalcon](https://github.com/filp/whoops/blob/master/docs/Framework%20Integration.md#integrating-with-Phalcon), [Laravel 3](https://gist.github.com/hugomrdias/5169713#file-start-php) (thanks, [@hugomrdias](https://github.com/hugomrdias)), [CakePHP](https://github.com/oldskool/WhoopsCakephp) (thanks, [@oldskool](https://github.com/oldskool)), -[Zend Framework 2](https://github.com/filp/whoops/wiki/Integrating-with-Zend-Framework-2). +[Zend Framework 2](https://github.com/filp/whoops/blob/master/docs/Framework%20Integration.md#integrating-with-Zend-Framework-2). If you are not using any of these frameworks, have a look at the **example files** in `examples/` to get a feel for how things work. I promise it's really simple! -If you want to edit some more, take a look at the [API Documentation](https://github.com/filp/whoops/wiki/API-Documentation) and the list of available handers below. +If you want to edit some more, take a look at the [API Documentation](https://github.com/filp/whoops/blob/master/docs/Framework%20Integration.md#API%20Documentation) and the list of available handers below. ### Available Handlers diff --git a/docs/API Documentation.md b/docs/API Documentation.md new file mode 100644 index 0000000..93307c7 --- /dev/null +++ b/docs/API Documentation.md @@ -0,0 +1,393 @@ +# API Documentation + +### Core Classes: +- [`Whoops\Run`](#whoops-run) - The main `Whoops` class - represents the stack and current execution +- [`Whoops\Handler\Handler` and `Whoops\Handler\HandlerInterface`](#handler-abstract) - Abstract representation of a Handler, and utility methods +- [`Whoops\Exception\Inspector`](#inspector) - Exposes methods to inspect an exception +- [`Whoops\Exception\FrameCollection`](#frame-collection) - Exposes methods to work with a list of frames +- [`Whoops\Exception\Frame`](#frame) - Exposes methods to inspect a single stack trace frame from an exception + +### Core Handlers: +- [`Whoops\Handler\CallbackHandler`](#handler-callback) - Wraps regular closures as handlers +- [`Whoops\Handler\JsonResponseHandler`](#handler-json) - Formats errors and exceptions as a JSON payload +- [`Whoops\Handler\PrettyPageHandler`](#handler-pretty) - Outputs a detailed, fancy error page + +# Core Classes: + +## `Whoops\Run` + +The `Run` class models an instance of an execution, and integrates the methods to control whoops' execution in that context, and control the handlers stack. + +### Constants + +```php +string Run::EXCEPTION_HANDLER // (name for exception handler method) +string Run::ERROR_HANDLER // (name for error handler method) +string Run::SHUTDOWN_HANDLER // (name for shutdown handler method) +``` + +### Methods + +```php +// Pushes a new handler to the stack of handlers +Run::pushHandler( Whoops\HandlerInterface $handler ) + #=> Whoops\Run + +// Pops and returns the last handler from the stack +Run::popHandler() + #=> Whoops\HandlerInterface + +// Returns all handlers in the stack +Run::getHandlers() + #=> Whoops\HandlerInterface[] + +// Returns a Whoops\Inspector instance for a given Exception +Run::getInspector( Exception $exception ) + #=> Whoops\Exception\Inspector + +// Registers this Whoops\Run instance as an error/exception/shutdown +// handler with PHP +Run::register() + #=> Whoops\Run + +// I'll let you guess this one +Run::unregister() + #=> Whoops\Run + +// If true, allows Whoops to terminate script execution (default: true) +Run::allowQuit( $allowQuit = null ) + #=> bool + +// If true, allows Whoops to send output produced by handlers directly +// to the client. You'll want to set this to false if you want to +// package the handlers' response into your HTTP response abstraction +// or something (default: true) +Run::writeToOutput( $send = null) + #=> bool + +// ** HANDLERS ** +// These are semi-internal methods that receive input from +// PHP directly. If you know what you're doing, you can +// also call them directly + +// Handles an exception with the current stack. Returns the +// output produced by handlers. +Run::handleException( Exception $exception ) + #=> string + +// Handles an error with the current stack. Errors are +// converted into SPL ErrorException instances +Run::handleError( int $level, string $message, string $file = null, int $line = null) + #=> null + +// Hooked as a shutdown handler, captures fatal errors and handles them +// through the current stack: +Run::handleShutdown() + #=> null +``` + +## `Whoops\Handler\Handler` & `Whoops\Handler\HandlerInterface` + +This abstract class contains the base methods for concrete handler implementations. Custom handlers can extend it, or implement the `Whoops\Handler\HandlerInterface` interface. + +### Constants +```php +int Handler::DONE // If returned from HandlerInterface::handle, does absolutely nothing. +int Handler::LAST_HANDLER // ...tells whoops to not execute any more handlers after this one. +int Handler::QUIT // ...tells whoops to quit script execution immediately. +``` + +### Methods + +```php +// Custom handlers should expose this method, which will be called once an +// exception needs to be handled. The Handler::* constants can be used to +// signal the underlying logic as to what to do next. +HandlerInterface::handle() + #=> null | int + +// Sets the Run instance for this handler +HandlerInterface::setRun( Whoops\Run $run) + #=> null + +// Sets the Inspector instance for this handler +HandlerInterface::setInspector( Whoops\Exception\Inspector $inspector) + #=> null + +// Sets the Exception for this handler to handle +HandlerInterface::setException( Exception $exception ) + #=> null +``` + +## `Whoops\Exception\Inspector` + +The `Inspector` class provides methods to inspect an exception instance, with particular focus on its frames/stack-trace. + +### Methods + +```php +Inspector::__construct( Exception $exception ) + #=> null + +// Returns the Exception instance being inspected +Inspector::getException() + #=> Exception + +// Returns the string name of the Exception being inspected +// A faster way of doing get_class($inspector->getException()) +Inspector::getExceptionName() + #=> string + +// Returns the string message for the Exception being inspected +// A faster way of doing $inspector->getException()->getMessage() +Inspector::getExceptionMessage() + #=> string + +// Returns an iterator instance for all the frames in the stack +// trace for the Exception being inspected. +Inspector::getFrames() + #=> Whoops\Exception\FrameIterator +``` + +## `Whoops\Exception\FrameCollection` + +The `FrameCollection` class exposes a fluent interface to manipulate and examine a +collection of `Frame` instances. + +`FrameCollection` objects are **serializable**. + +### Methods + +```php +// Returns the number of frames in the collection +// May also be called as count($frameCollection) +FrameCollection::count() + #=> int + +// Filter the Frames in the collection with a callable. +// The callable must accept a Frame object, and return +// true to keep it in the collection, or false not to. +FrameCollection::filter( callable $callable ) + #=> FrameCollection + +// See: array_map +// The callable must accept a Frame object, and return +// a Frame object, doesn't matter if it's the same or not +// - will throw an UnexpectedValueException if something +// else is returned. +FrameCollection::map( callable $callable) + #=> FrameCollection +``` + +## `Whoops\Exception\Frame` + +The `Frame` class models a single frame in an exception's stack trace. You can use it to retrieve info about things such as frame context, file, line number. Additionally, you have available functionality to add comments to a frame, which is made available to other handlers. + +`Frame` objects are **serializable**. + +### Methods + +```php +// Returns the file path for the file where this frame occured. +// The optional $shortened argument allows you to retrieve a +// shorter, human-readable file path for display. +Frame::getFile( bool $shortened = false ) + #=> string | null (Some frames do not have a file path) + +// Returns the line number for this frame +Frame::getLine() + #=> int | null + +// Returns the class name for this frame, if it occured +// within a class/instance. +Frame::getClass() + #=> string | null + +// Returns the function name for this frame, if it occured +// within a function/method +Frame::getFunction() + #=> string | null + +// Returns an array of arguments for this frame. Empty if no +// arguments were provided. +Frame::getArgs() + #=> array + +// Returns the full file contents for the file where this frame +// occured. +Frame::getFileContents() + #=> string | null + +// Returns an array of lines for a file, optionally scoped to a +// given range of line numbers. i.e: Frame::getFileLines(0, 3) +// returns the first 3 lines after line 0 (1) +Frame::getFileLines( int $start = 0, int $length = null) + #=> array | null + +// Adds a comment to this Frame instance. Comments are shared +// with everything that can access the frame instance, obviously, +// so they can be used for a variety of inter-operability purposes. +// The context option can be used to improve comment filtering. +// Additionally, if frames contain URIs, the PrettyPageHandler +// will automagically convert them to clickable anchor elements. +Frame::addComment( string $comment, string $context = 'global' ) + #=> null + +// Returns all comments for this instance optionally filtered by +// a string context identifier. +Frame::getComments( string $filter = null ) + #=> array +``` + +# Core Handlers + +## `Whoops\Handler\CallbackHandler` + +The `CallbackHandler` handler wraps regular PHP closures as valid handlers. Useful for quick prototypes or simple handlers. When you pass a closure to `Run::pushHandler`, it's automatically converted to a `CallbackHandler` instance. + +```php +pushHandler(function($exception, $inspector, $run) { + var_dump($exception->getMessage()); + return Handler::DONE; +}); + +$run->popHandler() // #=> Whoops\Handler\CallbackHandler +``` + +### Methods + +```php +// Accepts any valid callable +// For example, a closure, a string function name, an array +// in the format array($class, $method) +CallbackHandler::__construct( $callable ) + #=> null + +CallbackHandler::handle() + #=> int | null +``` + +## `Whoops\Handler\JsonResponseHandler` + +The `JsonResponseHandler`, upon receiving an exception to handle, simply constructs a `JSON` payload, and outputs it. Methods are available to control the detail of the output, and if it should only execute for AJAX requests - paired with another handler under it, such as the `PrettyPageHandler`, it allows you to have meaningful output for both regular and AJAX requests. Neat! + +The `JSON` body has the following format: + +```json +{ + "error": { + "type": "RuntimeException", + "message": "Something broke!", + "file": "/var/project/foo/bar.php", + "line": 22, + + # if JsonResponseHandler::addTraceToOutput(true): + "trace": [ + { "file": "/var/project/foo/index.php", + "line": 157, + "function": "handleStuffs", + "class": "MyApplication\DoerOfThings", + "args": [ true, 10, "yay method arguments" ] }, + # ... more frames here ... + ] + } +} +``` + +### Methods + +```php + +// Should detailed stack trace output also be added to the +// JSON payload body? +JsonResponseHandler::addTraceToOutput( bool $yes = null ) + #=> bool + +// Should output only be sent if the current request is an +// AJAX request? +JsonResponseHandler::onlyForAjaxRequests( bool $yes = null ) + #=> bool + +JsonResponseHandler::handle() + #=> int | null +``` + +## `Whoops\Handler\PrettyPageHandler` + +The `PrettyPageHandler` generates a fancy, detailed error page which includes code views for all frames in the stack trace, environment details, etc. Super neat. It produces a bundled response string that does not require any further HTTP requests, so it's fit to work on pretty much any environment and framework that speaks back to a browser, without you having to explicitly hook it up to your framework/project's routing mechanisms. + +### Methods + +```php +// Adds a key=>value table of arbitrary data, labeled by $label, to +// the output. Useful where you want to display contextual data along +// with the error, about your application or project. +PrettyPageHandler::addDataTable( string $label, array $data ) + #=> null + +// Similar to PrettyPageHandler::addDataTable, but accepts a callable +// that will be called only when rendering an exception. This allows +// you to gather additional data that may not be available very early +// in the process. +PrettyPageHandler::addDataTableCallback( string $label, callable $callback ) + #=> null + +// Returns all data tables registered with this handler. Optionally +// accepts a string label, and will only return the data under that +// label. +PrettyPageHandler::getDataTables( string $label = null ) + #=> array | array[] + +// Sets the title for the error page +PrettyPageHandler::setPageTitle( string $title ) + #=> null + +// Returns the title for the error page +PrettyPageHandler::getPageTitle() + #=> string + +// Returns a string path to the location where resources +// used by this handler are stored - the template and CSS +// files. +PrettyPageHandler::getResourcesPath() + #=> string + +// Sets a string path to the location of resources for the +// handler. Useful if you want to roll your own template +// file (pretty-template.php and pretty-page.css) while +// still using the logic this handler provides +PrettyPageHandler::setResourcesPath( string $resourcesPath ) + #=> null + +// Sets an editor to use to open referenced files, either by +// a string identifier, or as an arbitrary callable that returns +// a string that can be used as an href attribute. +// Available built-in editors are: +// - sublime +// - emacs +// - textmate +// - macvim +PrettyPageHandler::setEditor( string $editor ) +PrettyPageHandler::setEditor( function($file, $line) { return string } ) + #=> null + +// Similar to PrettyPageHandler::setEditor, but allows you +// to name your custom editor, thus sharing it with the +// rest of the application. Useful if, for example, you integrate +// Whoops into your framework or library, and want to share +// support for extra editors with the end-user. +// +// $resolver may be a callable, like with ::setEditor, or a string +// with placeholders %file and %line. +// For example: +// $handler->addEditor('whatevs', 'whatevs://open?file=file://%file&line=%line') +PrettyPageHandler::addEditor( string $editor, $resolver ) + #=> null + +PrettyPageHandler::handle() + #=> int | null +``` diff --git a/docs/Framework Integration.md b/docs/Framework Integration.md new file mode 100644 index 0000000..5b4a69d --- /dev/null +++ b/docs/Framework Integration.md @@ -0,0 +1,120 @@ +# 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 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) + + +# Contributing an integration with a framework + +Lately we're prefering to keep integration libraries out of the Whoops core. +If possible, consider managing an official Whoops-SomeFramework integration. + +The procedure is not hard at all. + +1. Keep your integration classes and instructions in a repository of your own; +2. Create a `composer.json` file in your repository with contents similar to the following: + + ``` + { + "name": "username/whoops-someframework", + "description": "Integrates the Whoops library into SomeFramework", + "version": "1.0.0", + "require": { + "filp/whoops": "1.*" + } + } + ``` + +3. [Register it with Packagist](https://packagist.org/packages/submit). + +Once that is done, please create an issue and we will add a link to it in our README. + +SomeFramework users then would write this in their `composer.json`: + + "require": { + "username/whoops-someframework": "*" + } + +This would also install Whoops and you'd be able to release updates to your package as quickly as you wish them to. diff --git a/docs/Open Files In An Editor.md b/docs/Open Files In An Editor.md new file mode 100644 index 0000000..c789aca --- /dev/null +++ b/docs/Open Files In An Editor.md @@ -0,0 +1,49 @@ +# Open Files In An Editor + +When using the pretty error page feature, whoops comes with the ability to +open referenced files directly in your IDE or editor. + +```php +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"; +}); + +``` + +You can add PhpStorm support with [PhpStormOpener](https://github.com/pinepain/PhpStormOpener#phpstormopener) (Mac OS X only): +```php + +$handler->setEditor( + function ($file, $line) { + // if your development server is not local it's good to map remote files to local + $translations = array('^' . __DIR__ => '~/Development/PhpStormOpener'); // change to your path + + foreach ($translations as $from => $to) { + $file = preg_replace('#' . $from . '#', $to, $file, 1); + } + + return "pstorm://$file:$line"; + } +); + +``` diff --git a/docs/Possible Upcoming Features.md b/docs/Possible Upcoming Features.md new file mode 100644 index 0000000..00fa025 --- /dev/null +++ b/docs/Possible Upcoming Features.md @@ -0,0 +1,16 @@ +# Possible Upcoming Features + +### General +* [#100](https://github.com/filp/whoops/issues/100) Handle deprecated errors +* Add a method to check server settings, say `checkProductionSettings`, that would check `display_errors`, and other settings. +* [#133](https://github.com/filp/whoops/issues/133) Send a pretty email report. (EmailHandler) +* Remove providers from the core + +### PrettyPageHandler +* [#34](https://github.com/filp/whoops/issues/34) Integration with Xdebug tools. +* [#106](https://github.com/filp/whoops/issues/106) Add information on common problems on PrettyPageHandler. +* [#113](https://github.com/filp/whoops/issues/113) Simple stack trace in comments in the PrettyPageHandler head, and only send if Accept: html. +* [#119](https://github.com/filp/whoops/issues/119#issuecomment-34502643) Filter functions in PrettyPageHandler to remove sensitive data from tracebacks. +* Mobile layout support +* Share/export feature to easily export exception message to a copy-paste-friendly format +* Whoops JS API