Browse Source

More documentation improvements

2.1.0
Dustin Wilson 1 year ago
parent
commit
fe8c7770a4
  1. 190
      README.md
  2. 3
      composer.json
  3. 136
      composer.lock

190
README.md

@ -1,23 +1,27 @@
[a]: https://php.net/manual/en/book.dom.php
[b]: https://github.com/Seldaek/monolog
[c]: https://github.com/symfony/yaml
# Catcher
Catcher is a Throwable catcher and error handling library for PHP. Error handling is accomplished using a stack-based approach.
Catcher uses classes called _handlers_ to handle throwables sent its way. PHP is currently in a state of flux when it comes to errors. There are traditional PHP errors which are triggered in userland by using `trigger_error()` which can't be caught using `try`/`catch` and are generally a pain to work with. PHP has begun to remedy this problem by introducing the `\Error` class and its various child classes. However, a lot of functions and core aspects of the language itself continue to use legacy errors. This class does away with this pain point in PHP by turning all errors into throwables. Non user-level fatal errors are picked up by Catcher using its shutdown handler. This means that simply by invoking Catcher one may now catch (almost) any error PHP then handles.
Catcher uses classes called _handlers_ to handle throwables sent its way. PHP is currently in a state of flux when it comes to errors. There are traditional PHP errors which are triggered in userland by using `trigger_error()` which can't be caught using `try`/`catch` and are generally a pain to work with. PHP has begun to remedy this problem by introducing the `\Error` class and its various child classes. However, a lot of functions and core aspects of the language itself continue to use legacy errors. This class does away with this pain point in PHP by turning all errors into throwables. Non user-level fatal errors are picked up by Catcher using its shutdown handler. This means that simply by invoking Catcher one may now... catch (almost) any error PHP then handles.
## Requirements
* PHP 8.1 or newer with the following _optional_ extensions:
* [dom][a] extension (for HTMLHandler)
## Installation
```shell
composer require mensbeam/catcher
```
## Usage
For most use cases this library requires no configuration and little effort to integrate into non-complex environments:
@ -52,11 +56,16 @@ The example above uses [Monolog][b] for its logger, but any PSR-3-compatible log
Catcher comes built-in with the following handlers:
* `HTMLHandler` – Outputs errors in a clean HTML document; uses DOM to assemble the document.
* `JSONHandler` – Outputs errors in a JSON format mostly representative of how errors are stored internally by Catcher handlers; it is provided as an example. The decision to make it like this was made because errors often need to be represented according to a particular specification, and we cannot possibly support them all. `JSONHandler`, however, can be easily extended to suit individual project needs.
* `PlainTextHandler` – Outputs errors in plain text and provides for logging
* `JSONHandler` – Outputs errors in a JSON format mostly representative of how errors are stored internally by Catcher handlers; it is provided as an example. The decision to make it like this was made because errors often need to be represented according to particular requirements or even a specification, and we cannot possibly support them all. `JSONHandler`, however, can be easily extended to suit individual project needs.
* `PlainTextHandler` – Outputs errors cleanly in plain text meant mostly for command line use and also provides for logging
## Documentation
### MensBeam\Foundation\Catcher
This is the main class in the library. Unless you have a need to configure a handler or use multiple handlers there usually isn't a need to interact with the rest of the library at all.
```php
namespace MensBeam\Foundation;
use Mensbeam\Foundation\Catcher\Handler;
@ -82,7 +91,7 @@ class Catcher {
#### Properties
_preventExit_: When set to true Catcher won't exit at all even after fatal errors or exceptions
_preventExit_: When set to true Catcher won't exit at all even after fatal errors or exceptions
_throwErrors_: When set to true Catcher will convert errors to throwables
#### MensBeam\Foundation\Catcher::getHandlers
@ -125,9 +134,10 @@ Unregisters the Catcher instance as an error, exception and shutdown handler.
Unshifts the specified handler(s) onto the beginning of the stack
### MensBeam\Foundation\Catcher\Handler
All handlers inherit from this abstract class. Since it is an abstract class meant for constructing handlers protected methods and properties will be documented as well.
All handlers inherit from this abstract class. Since it is an abstract class meant for constructing handlers protected methods and properties will be documented here as well.
```php
namespace MensBeam\Foundation\Catcher;
@ -147,7 +157,8 @@ abstract class Handler {
protected array $outputBuffer;
protected int $_backtraceArgFrameLimit;
// Options
protected int $_backtraceArgFrameLimit = 5;
protected string $_charset = 'UTF-8';
protected bool $_forceBreak = false;
protected bool $_forceExit = false;
@ -179,31 +190,36 @@ abstract class Handler {
#### Constants
_CONTENT\_TYPE_: The mime type of the content that is output by the handler
_CONTINUE_: The stack loop continues onto the next handler after handling; this is the default behavior
_BREAK_: The stack loop breaks after the handler finishes, causing any further down in the stack to not run
_EXIT_: The stack loop exits after running all handlers
_OUTPUT_: The handler will output
_SILENT_: The handler will be silent
_NOW_: Outputs immediately after handling
_CONTENT\_TYPE_: The mime type of the content that is output by the handler
_CONTINUE_: When returned within the control code bitmask in the handler's output array, it causes the stack loop to continue onto the next handler after handling; this is the default behavior.
_BREAK_: When returned within the control code bitmask in the handler's output array, it causes the stack loop to break after the handler finishes, causing any further down in the stack to not run.
_EXIT_: When returned within the control code bitmask in the handler's output array, it causes Catcher to exit after running all handlers.
_OUTPUT_: When returned within the output code bitmask in the handler's output array, it causes the handler to output the throwable when dispatching.
_SILENT_: When returned within the output code bitmask in the handler's output array, it causes the handler to be silent.
_NOW_: When returned within the output code bitmask in the handler's output array, it causes Catcher to have the handler immediately dispatch.
#### Properties (Protected)
Properties which begin with an underscore all are options. They can be set either through the constructor or via `setHandler` by name, removing the underscore at the beginning. All handlers inherit these options.
_outputBuffer_: This is where the output arrays representing the handled throwables are stored until they are dispatched.
_\_backtraceArgFrameLimit_: The number of frames by which there can be arguments output with them. Defaults to _5_.
_\_charset_: The character set of the output; only used if headers weren't sent before an error occurred. Defaults to _"UTF\_8"_.
_\_forceBreak_: When set this will force the stack loop to break after the handler has run. Defaults to _false_.
_\_forceExit_: When set this will force an exit after all handlers have been run. Defaults to _false_.
_\_forceOutputNow_: When set this will force output of the handler immediately. Defaults to _false_.
_\_httpCode_: The HTTP code to be sent; possible values are 200, 400-599. Defaults to _500_.
_\_outputBacktrace_: When true will output a stack trace. Defaults to _false_.
_\_outputPrevious_: When true will output previous throwables. Defaults to _true_.
_\_outputTime_: When true will output times to the output. Defaults to _true_.
_\_outputToStderr_: When the SAPI is cli output errors to stderr. Defaults to _true_.
_\_silent_: When true the handler won't output anything. Defaults to _false_.
_\_timeFormat_: The PHP-standard date format which to use for times in output. Defaults to _"Y-m-d\\TH:i:s.vO"_.
_outputBuffer_: This is where the output arrays representing the handled throwables are stored until they are dispatched.
#### Options
Properties which begin with an underscore all are options. They can be set either through the constructor or via `setHandler` by name, removing the underscore at the beginning. All handlers inherit these options. Options in inherited classes should also begin with an underscore (_\__). How to extend `Handler` will be explained further down in the document.
_backtraceArgFrameLimit_: The number of frames by which there can be arguments output with them. Defaults to _5_.
_charset_: The character set of the output; only used if headers weren't sent before an error occurred. No conversion is done. Defaults to _"UTF\_8"_.
_forceBreak_: When set this will force the stack loop to break after the handler has run. Defaults to _false_.
_forceExit_: When set this will force an exit after all handlers have been run. Defaults to _false_.
_forceOutputNow_: When set this will force output of the handler immediately. Defaults to _false_.
_httpCode_: The HTTP code to be sent; possible values are 200, 400-599. Defaults to _500_.
_outputBacktrace_: When true will output a stack trace. Defaults to _false_.
_outputPrevious_: When true will output previous throwables. Defaults to _true_.
_outputTime_: When true will output times to the output. Defaults to _true_.
_outputToStderr_: When the SAPI is cli output errors to stderr. Defaults to _true_.
_silent_: When true the handler won't output anything. Defaults to _false_.
_timeFormat_: The PHP-standard date format which to use for times in output. Defaults to _"Y-m-d\\TH:i:s.vO"_.
#### MensBeam\Foundation\Catcher\Handler::dispatch
@ -243,7 +259,7 @@ Prints the provided string to stderr or stdout depending on how the handler is c
### MensBeam\Foundation\Catcher\ThrowableController
All throwables cannot be extended, so this class exists to control throwables for use with Catcher.
We cannot require all throwables to be converted to our own classes, so this class exists as a controller to add new features to throwables for use with Catcher.
```php
namespace MensBeam\Foundation\Catcher;
@ -260,11 +276,11 @@ class ThrowableController {
#### MensBeam\Foundation\Catcher\ThrowableController::getErrorType
Returns the error type of a `MensBeam\Foundation\Catcher\Error`, meaning a human-friendly representation of the error code or null if the throwable isn't an `Error`.
Returns the error type of a `MensBeam\Foundation\Catcher\Error`, meaning a human-friendly representation of the error code (eg: _"Fatal Error"_, _"Warning"_, _"Notice"_) or null if the throwable isn't an `Error`.
#### MensBeam\Foundation\Catcher\ThrowableController::getFrames
Returns the frames for the throwable as an array with deduplication and fixes all taken care of by the method
Returns the frames for the throwable as an array with deduplication and fixes all taken care of
#### MensBeam\Foundation\Catcher\ThrowableController::getPrevious
@ -272,4 +288,114 @@ Returns the previous `ThrowableController` if there is one
#### MensBeam\Foundation\Catcher\ThrowableController::getThrowable
Returns the throwable controlled by this class instance
Returns the throwable controlled by this class instance
### Mensbeam\Foundation\Catcher\HTMLHandler
```php
namespace MensBeam\Foundation\Catcher;
class HTMLHandler extends Handler {
public const CONTENT_TYPE = 'text/html';
// Options
protected ?\DOMDocument $_document = null;
protected string $_errorPath = '/html/body';
protected string $_timeFormat = 'H:i:s';
}
```
#### Options
_document_: The `\DOMDocument` errors should be inserted into. If one isn't provided a document will be created for this purpose.
_errorPath_: An XPath path to the element where the errors should be inserted. Defaults to _"/html/body"_.
_timeFormat_: Same as in `Handler`, but the default changes to _"H:i:s"_.
### Mensbeam\Foundation\Catcher\JSONHandler
```php
namespace MensBeam\Foundation\Catcher;
class JSONHandler extends Handler {
public const CONTENT_TYPE = 'application/json';
}
```
### Mensbeam\Foundation\Catcher\PlainTextHandler
```php
namespace MensBeam\Foundation\Catcher;
use Psr\Log\LoggerInterface;
class PlainTextHandler extends Handler {
public const CONTENT_TYPE = 'text/plain';
// Options
protected ?LoggerInterface $_logger = null;
protected string $_timeFormat = '[H:i:s]';
}
```
#### Options
_logger_: The PSR-3 compatible logger in which to log to. Defaults to _null_ (no logging).
_timeFormat_: Same as in `Handler`, but the default changes to _"[H:i:s]"_.
## Creating Handlers
The default handlers, especially `PlainTextHandler`, is set up to handle most tasks, but obviously more is possible with a bit of work. Thankfully, creating handlers is as simple as extending the Handler class. Here is an example of a theoretical `YamlHandler`:
```php
namespace Your\Namespace\Goes\Here;
use Mensbeam\Foundation\Catcher\Handler,
Symfony\Component\Yaml\Yaml;
class YamlHandler extends Handler {
public const CONTENT_TYPE = 'application/yaml';
// Options
protected bool _outputObjectAsMap = true;
protected bool _outputMultiLineLiteralBlock = true;
protected bool _outputEmptyArrayAsSequence = true;
protected bool _outputNullAsTilde = false;
protected function dispatchCallback(): void {
foreach ($this->outputBuffer as $key => $value) {
if ($value['outputCode'] & self::SILENT) {
unset($this->outputBuffer[$key]);
continue;
}
$this->outputBuffer[$key] = $this->cleanOutputThrowable($this->outputBuffer[$key]);
}
$flags = 0;
$flags |= ($this->_outputObjectAsMap) ? Yaml::DUMP_OBJECT_AS_MAP : 0;
$flags |= ($this->_outputMultiLineLiteralBlock) ? Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK : 0;
$flags |= ($this->_outputEmptyArrayAsSequence) ? Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE : 0;
$flags |= ($this->_outputNullAsTilde) ? Yaml::DUMP_NULL_AS_TILDE : 0;
if (count($this->outputBuffer) > 0) {
$this->print(Yaml::dump([
'errors' => $this->outputBuffer
], $flags));
}
}
}
```
This theoretical class uses the [`symfony/yaml`][c] package in Composer and exposes a few of its options as options for the Handler. Using this class would be very similar to the examples provided at the beginning of this document:
```php
use Mensbeam\Foundation\Catcher,
Symfony\Component\Yaml\Yaml,
Your\Namespace\Goes\Here\YamlHandler;
$catcher = new Catcher(new YamlHandler([
'outputNullAsTilde' => true
]);
```
More complex handlers are possible by extending the various methods documented above. Examples can be seen by looking at the code for both `HTMLHandler` and `PlainTextHandler`.

3
composer.json

@ -26,6 +26,7 @@
"mensbeam/html-dom": "^1.0",
"phpunit/phpunit": "^9.5",
"nikic/php-parser": "^4.15",
"eloquent/phony-phpunit": "^7.1"
"eloquent/phony-phpunit": "^7.1",
"symfony/yaml": "^6.2"
}
}

136
composer.lock

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a72b4696660d27f574ba5142eb76d7dd",
"content-hash": "dd7f0dc703d14f61f5c0d12d9106338f",
"packages": [
{
"name": "psr/log",
@ -2285,6 +2285,88 @@
],
"time": "2022-06-27T16:58:25+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.26.0",
@ -2369,29 +2451,39 @@
"time": "2022-05-10T07:21:04+00:00"
},
{
"name": "symfony/var-exporter",
"version": "v6.2.1",
"name": "symfony/yaml",
"version": "v6.2.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "8a3f442d48567a5447e984ce9e86875ed768304a"
"url": "https://github.com/symfony/yaml.git",
"reference": "6ed8243aa5f2cb5a57009f826b5e7fb3c4200cf3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/8a3f442d48567a5447e984ce9e86875ed768304a",
"reference": "8a3f442d48567a5447e984ce9e86875ed768304a",
"url": "https://api.github.com/repos/symfony/yaml/zipball/6ed8243aa5f2cb5a57009f826b5e7fb3c4200cf3",
"reference": "6ed8243aa5f2cb5a57009f826b5e7fb3c4200cf3",
"shasum": ""
},
"require": {
"php": ">=8.1"
"php": ">=8.1",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<5.4"
},
"require-dev": {
"symfony/var-dumper": "^5.4|^6.0"
"symfony/console": "^5.4|^6.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
},
"bin": [
"Resources/bin/yaml-lint"
],
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\VarExporter\\": ""
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
@ -2403,28 +2495,18 @@
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Allows exporting any serializable PHP data structure to plain PHP code",
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"keywords": [
"clone",
"construct",
"export",
"hydrate",
"instantiate",
"lazy loading",
"proxy",
"serialize"
],
"support": {
"source": "https://github.com/symfony/var-exporter/tree/v6.2.1"
"source": "https://github.com/symfony/yaml/tree/v6.2.2"
},
"funding": [
{
@ -2440,7 +2522,7 @@
"type": "tidelift"
}
],
"time": "2022-12-03T22:32:58+00:00"
"time": "2022-12-14T16:11:27+00:00"
},
{
"name": "theseer/tokenizer",
@ -2501,6 +2583,8 @@
"platform": {
"php": ">=8.1"
},
"platform-dev": [],
"platform-dev": {
"ext-dom": "*"
},
"plugin-api-version": "2.3.0"
}

Loading…
Cancel
Save