From 9247df8dd7f3e3468d704731f81eb8eec4a8c1fd Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Thu, 23 Jan 2020 17:24:41 +1300 Subject: [PATCH 01/21] Initial fix-ups for the readme, update mentions of v1 to v2, add link to v1 readme. --- README.md | 84 +++++++++++++++++++------------------------------------ 1 file changed, 29 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 8d242a3..8dab98b 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ Raygun4PHP [Raygun.com](http://raygun.com) provider for PHP 7.1+ +_See [v1.8 documentation](https://github.com/MindscapeHQ/raygun4php/blob/1.8/README.md) for support for legacy versions of PHP_ + [![Build Status](https://secure.travis-ci.org/MindscapeHQ/raygun4php.png?branch=master)](http://travis-ci.org/MindscapeHQ/raygun4php) @@ -19,12 +21,12 @@ Composer is a package management tool for PHP which automatically fetches depend 2. Inside your project's root directory create a composer.json file, containing: ```json { - "require": { - "mindscape/raygun4php": "^1.0" - } + "require": { + "mindscape/raygun4php": "^2.0" + } } ``` -3. From your shell run `php composer.phar install` (*nix) or `composer install` (Windows). This will download Raygun4Php and create the appropriate autoload data. +3. From your shell run `php composer.phar install` (*nix) or `composer install` (Windows). This will download Raygun4PHP and create the appropriate autoload data. 4. Then in a PHP file just add: ```php @@ -41,19 +43,19 @@ Then, create handlers that look something like this: ```php namespace { - // paste your 'requires' statement + // paste your 'requires' statement - $client = new \Raygun4php\RaygunClient("apikey for your application"); + $client = new \Raygun4php\RaygunClient("APIKEY_FOR_YOUR_APPLICATION"); - function error_handler($errno, $errstr, $errfile, $errline ) { - global $client; - $client->SendError($errno, $errstr, $errfile, $errline); - } + function error_handler($errno, $errstr, $errfile, $errline ) { + global $client; + $client->SendError($errno, $errstr, $errfile, $errline); + } - function exception_handler($exception) - { - global $client; - $client->SendException($exception); + function exception_handler($exception) + { + global $client; + $client->SendException($exception); } function fatal_error() @@ -62,11 +64,11 @@ namespace $last_error = error_get_last(); if (!is_null($last_error)) { - $errno = $last_error['type']; - $errstr = $last_error['message']; - $errfile = $last_error['file']; - $errline = $last_error['line']; - $client->SendError($errno, $errstr, $errfile, $errline); + $errno = $last_error['type']; + $errstr = $last_error['message']; + $errfile = $last_error['file']; + $errline = $last_error['line']; + $client->SendError($errno, $errstr, $errfile, $errline); } } @@ -78,43 +80,15 @@ namespace Note that if you are placing in inside a file with a namespace of your choosing, the above code should be declared to be within the global namespace (thus the `namespace { }` is required). You will also need whichever `requires` statement as above (autoload or manual) before the `$client` instantiation. -Copy your application's API key from the Raygun.io dashboard, and place it in the constructor call as above (do not include the curly brackets). - -If the handlers reside in their own file, just import it in every file where you'd like exceptions and errors to be sent, and they will be delivered to Raygun.io. - -## Configuration - -### Sending method - async/sync - -Raygun4PHP has two algorithms which it can use to send your errors: - -* **Asynchronous**: POSTs the message and returns to your script immediately without waiting for the response from the Raygun API. - -* **Synchronous**: POSTs the message, blocks and receives the HTTP response from the Raygun API. This uses a socket connection which is still reasonably fast. This also allows the use of the debug mode to receive the HTTP response code; see below. - - -This can be set by passing in a boolean as the 2nd parameter to the constructor: - -```php -$client = new \Raygun4php\RaygunClient("apiKey", $useAsyncSending); -``` -#### $useAsyncSending options - -Type: *boolean* - -Linux/OS X default: *true* - -Windows default: *false* - -* If **$useAsyncSending** is *true*, and the script is running on a *nix platform, the message will be delivered asynchronously. SendError() and SendException() will return 0 if all went well. +Copy your application's API key from the Raygun dashboard, and place it in the constructor call as above (do not include the curly brackets). -* If **$useAsyncSending** is *false*, the script will block and receive the HTTP response. +If the handlers reside in their own file, just import it in every file where you'd like exceptions and errors to be sent, and they will be delivered to Raygun.com. -*false* is the only effective option on Windows due to platform and library limitations within the supported versions. +## Configuration (v1) ### Proxies -A HTTP proxy can be set if your environment can't connect out through PHP or the `curl` binrary natively: +A HTTP proxy can be set if your environment can't connect out through PHP or the `curl` binary natively: ```php $client = new \Raygun4php\RaygunClient("apiKey"); @@ -201,7 +175,7 @@ This feature can be used in CLI mode by calling SetUser() at the start of your s ### Custom error grouping -Control of how error instances are grouped together can achieved by passing a callback to the `SetGroupingKey` method on the client. If the callback returns a string, ideally 100 characters or less, errors matching that key will grouped together. Overriding the default automatic grouping. If the callback returns a non-string value then that error will be grouped automatically. +Control of how error instances are grouped together can achieved by passing a callback to the `SetGroupingKey` method on the client. If the callback returns a string, ideally 100 characters or less, errors matching that key will grouped together. Overriding the default automatic grouping. If the callback returns a non-string value then that error will be grouped automatically. ```php $client = new \Raygun4php\RaygunClient("apiKey"); @@ -254,7 +228,7 @@ Note that when any filters are defined, the Raygun error will no longer contain ### Updating Cookie options -Cookies are used for the user tracking functionality of the Raygun4Php provider. In version 1.8 of the provider, the options passed to the `setcookie` method can now be customized to your needs. +Cookies are used for the user tracking functionality of the Raygun4PHP provider. In version 1.8 of the provider, the options passed to the `setcookie` method can now be customized to your needs. ```php $client = new \Raygun4php\RaygunClient("apiKey"); @@ -296,10 +270,10 @@ function error_handler($errno, $errstr, $errfile, $errline ) { } ``` -See the [Error Control Operators section on PHP.net](http://php.net/manual/en/language.operators.errorcontrol.php) for more information +See the [Error Control Operators section on PHP.net](http://php.net/manual/en/language.operators.errorcontrol.php) for more information ## Changelog -- 2.0.0 (unreleased): New major version +- 2.0.0: New major version - Increased minimum PHP version to 7.1 - Added PSR-4 autoloader - Removes `toJsonRemoveUnicodeSequences()` and `removeNullBytes()` methods from the RaygunClient class - use `toJson()` instead From a3493a28f87ace6ee28c960c0fa6ab80892b9053 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Thu, 23 Jan 2020 17:26:37 +1300 Subject: [PATCH 02/21] Add a sample app which uses the v2 API, sending errors asynchronously, setup logging and user tracking --- sample-apps/async-example/.gitignore | 1 + sample-apps/async-example/composer.json | 6 + sample-apps/async-example/index.php | 46 ++++++++ sample-apps/async-example/partials/head.php | 15 +++ sample-apps/async-example/raygunSetup.php | 124 ++++++++++++++++++++ sample-apps/async-example/viewData.php | 75 ++++++++++++ 6 files changed, 267 insertions(+) create mode 100644 sample-apps/async-example/.gitignore create mode 100644 sample-apps/async-example/composer.json create mode 100644 sample-apps/async-example/index.php create mode 100644 sample-apps/async-example/partials/head.php create mode 100644 sample-apps/async-example/raygunSetup.php create mode 100644 sample-apps/async-example/viewData.php diff --git a/sample-apps/async-example/.gitignore b/sample-apps/async-example/.gitignore new file mode 100644 index 0000000..b14c548 --- /dev/null +++ b/sample-apps/async-example/.gitignore @@ -0,0 +1 @@ +debug.log diff --git a/sample-apps/async-example/composer.json b/sample-apps/async-example/composer.json new file mode 100644 index 0000000..141b0cf --- /dev/null +++ b/sample-apps/async-example/composer.json @@ -0,0 +1,6 @@ +{ + "require": { + "mindscape/raygun4php": "2.x-dev", + "monolog/monolog": "2.0.2" + } +} diff --git a/sample-apps/async-example/index.php b/sample-apps/async-example/index.php new file mode 100644 index 0000000..48ff036 --- /dev/null +++ b/sample-apps/async-example/index.php @@ -0,0 +1,46 @@ + + + + + + + + + +
+
+

Runner's pace calculator

+
+
+
+
+ + +
+
+ + +
+
+ + +
+ + hasSentData()) : ?> +
+

Average pace: getAveragePace(); ?>mins/km

+

Average speed: getAverageSpeed(); ?>km/hr

+
+ +
+
+
+ + + + diff --git a/sample-apps/async-example/partials/head.php b/sample-apps/async-example/partials/head.php new file mode 100644 index 0000000..c1d6c42 --- /dev/null +++ b/sample-apps/async-example/partials/head.php @@ -0,0 +1,15 @@ + +Runner's pace calculator + + + + + + diff --git a/sample-apps/async-example/raygunSetup.php b/sample-apps/async-example/raygunSetup.php new file mode 100644 index 0000000..85d62a7 --- /dev/null +++ b/sample-apps/async-example/raygunSetup.php @@ -0,0 +1,124 @@ +useAsync = $useAsync; + $this->logger = new Logger('example_logger'); + $this->logger->pushHandler(new StreamHandler(__DIR__ . '/debug.log')); + $this->logger->pushHandler(new FirePHPHandler()); + + $this->httpClient = new Client([ + 'base_uri' => self::RAYGUN_BASE_URI, + 'timeout' => self::HTTP_CLIENT_TIMEOUT, + 'headers' => [ + 'X-ApiKey' => self::API_KEY + ] + ]); + + if ($this->useAsync) { + $this->transport = new GuzzleAsync($this->httpClient); + } else { + $this->transport = new GuzzleSync($this->httpClient); + } + + $this->transport->setLogger($this->logger); + + $raygunClient = new RaygunClient($this->transport); + + // Get logged in user info to track affected user + $raygunClient->SetUser("test@example.com", "Test", "Test User", "test@example.com"); + + $this->raygunClient = $raygunClient; + } + + public function SendError($errno, + $errstr, + $errfile, + $errline) + { + $this->raygunClient->SendError($errno, + $errstr, + $errfile, + $errline, + ['useAsync' => $this->useAsync] + ); + } + + public function SendException($exception) + { + $this->raygunClient->SendException($exception); + } + + public function handleLastError() + { + $last_error = error_get_last(); + + if (!is_null($last_error)) { + $this->SendError($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); + } + } + + public function flushAsyncPromises() + { + if ($this->useAsync) { + $this->transport->wait(); + } + } + } + + $setup = new RaygunSetup(); + + set_error_handler([$setup, 'SendError']); + set_exception_handler([$setup, 'SendException']); + register_shutdown_function([$setup, 'handleLastError']); + register_shutdown_function([$setup, 'flushAsyncPromises']); +} diff --git a/sample-apps/async-example/viewData.php b/sample-apps/async-example/viewData.php new file mode 100644 index 0000000..ad5744c --- /dev/null +++ b/sample-apps/async-example/viewData.php @@ -0,0 +1,75 @@ +time = $_POST['time']; + $this->distance = $_POST['distance']; + } + + /** + * @return int + */ + public function getTime() + { + return $this->time; + } + + /** + * @return float + */ + public function getDistance() + { + return $this->distance; + } + + /** + * @return bool + */ + public function hasSentData() + { + return isset($this->time) && isset($this->distance); + } + + /** + * @return string + */ + public function getAveragePace() + { + return $this->decimalToMinuteString($this->time / $this->distance); + } + + /** + * @return string + */ + public function getAverageSpeed() + { + return round($this->distance / ($this->time / 60), self::SPEED_PRECISION); + } + + /** + * @param float $timeAsDecimal + * @return string + */ + private function decimalToMinuteString(float $timeAsDecimal) + { + $whole = floor($timeAsDecimal); + $decimal = $timeAsDecimal - $whole; + $roundedMinutes = round($decimal * 60, 0); + $minutes = str_pad($roundedMinutes, 2, "0"); + return "{$whole}:{$minutes}"; + } +} From 83bdad116a869f5d4e6dda85f579b8a93633d28b Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Thu, 23 Jan 2020 17:27:09 +1300 Subject: [PATCH 03/21] Bump the version in the RaygunClientMessage to v2.0.0 --- src/Raygun4php/RaygunClientMessage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Raygun4php/RaygunClientMessage.php b/src/Raygun4php/RaygunClientMessage.php index 4ccccf5..0078273 100644 --- a/src/Raygun4php/RaygunClientMessage.php +++ b/src/Raygun4php/RaygunClientMessage.php @@ -11,7 +11,7 @@ class RaygunClientMessage public function __construct() { $this->Name = "Raygun4php"; - $this->Version = "1.8.3"; + $this->Version = "2.0.0"; $this->ClientUrl = "https://github.com/MindscapeHQ/raygun4php"; } } From f93e9efaa2fc9e0bced641598d382de1da73c687 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 09:04:25 +1300 Subject: [PATCH 04/21] Exclude the sample-apps dir from releases --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitattributes b/.gitattributes index 412eeda..38ea924 100644 --- a/.gitattributes +++ b/.gitattributes @@ -20,3 +20,6 @@ *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain + +# Exclude from release +sample-apps export-ignore From e72bf7b9608be999366eae07adeb97b183a9e795 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 12:33:15 +1300 Subject: [PATCH 05/21] Tidy up RaygunSetup class --- sample-apps/async-example/raygunSetup.php | 45 ++++++++++++++--------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/sample-apps/async-example/raygunSetup.php b/sample-apps/async-example/raygunSetup.php index 85d62a7..56529c6 100644 --- a/sample-apps/async-example/raygunSetup.php +++ b/sample-apps/async-example/raygunSetup.php @@ -18,7 +18,9 @@ class RaygunSetup { const RAYGUN_BASE_URI = 'https://api.raygun.io'; const HTTP_CLIENT_TIMEOUT = 2.0; - const API_KEY = 'INSERT_API_KEY_HERE'; + const API_KEY = 'sgbs3RocyWEnR2ar5q6W6Q'; + const LOGGER_NAME = 'example_logger'; + const LOG_FILE_PATH = __DIR__ . '/debug.log'; /** * @var GuzzleSync @@ -52,8 +54,8 @@ class RaygunSetup public function __construct($useAsync = true) { $this->useAsync = $useAsync; - $this->logger = new Logger('example_logger'); - $this->logger->pushHandler(new StreamHandler(__DIR__ . '/debug.log')); + $this->logger = new Logger(self::LOGGER_NAME); + $this->logger->pushHandler(new StreamHandler(self::LOG_FILE_PATH)); $this->logger->pushHandler(new FirePHPHandler()); $this->httpClient = new Client([ @@ -80,22 +82,29 @@ public function __construct($useAsync = true) $this->raygunClient = $raygunClient; } - public function SendError($errno, - $errstr, - $errfile, - $errline) + /** + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $tags + * @param array $customData + * @param int $timestamp + */ + public function sendError($errno, $errstr, $errfile, $errline, $tags = null, $customData = null, $timestamp = null) { - $this->raygunClient->SendError($errno, - $errstr, - $errfile, - $errline, - ['useAsync' => $this->useAsync] - ); + $this->raygunClient->SendError($errno, $errstr, $errfile, $errline, $tags, $customData, $timestamp); } - public function SendException($exception) + /** + * @param Throwable $exception + * @param array $tags + * @param array $customData + * @param int $timestamp + */ + public function sendException($exception, $tags = null, $customData = null, $timestamp = null) { - $this->raygunClient->SendException($exception); + $this->raygunClient->SendException($exception, $tags, $customData, $timestamp); } public function handleLastError() @@ -103,7 +112,7 @@ public function handleLastError() $last_error = error_get_last(); if (!is_null($last_error)) { - $this->SendError($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); + $this->sendError($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); } } @@ -117,8 +126,8 @@ public function flushAsyncPromises() $setup = new RaygunSetup(); - set_error_handler([$setup, 'SendError']); - set_exception_handler([$setup, 'SendException']); + set_error_handler([$setup, 'sendError']); + set_exception_handler([$setup, 'sendException']); register_shutdown_function([$setup, 'handleLastError']); register_shutdown_function([$setup, 'flushAsyncPromises']); } From 68df8cf7c0622252b81b5b10d953dcb0ed76f22f Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 12:33:50 +1300 Subject: [PATCH 06/21] Fix padding error in sample app view data --- sample-apps/async-example/viewData.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sample-apps/async-example/viewData.php b/sample-apps/async-example/viewData.php index ad5744c..7cee7b8 100644 --- a/sample-apps/async-example/viewData.php +++ b/sample-apps/async-example/viewData.php @@ -69,7 +69,8 @@ private function decimalToMinuteString(float $timeAsDecimal) $whole = floor($timeAsDecimal); $decimal = $timeAsDecimal - $whole; $roundedMinutes = round($decimal * 60, 0); - $minutes = str_pad($roundedMinutes, 2, "0"); + $minutes = str_pad($roundedMinutes, 2, "0", STR_PAD_LEFT); + return "{$whole}:{$minutes}"; } } From 3efcfdc3aa8073499effa691e3a5349a5cb3fa3a Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 12:35:14 +1300 Subject: [PATCH 07/21] Replace raygun.io with raygun in RaygunClient class --- src/Raygun4php/RaygunClient.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Raygun4php/RaygunClient.php b/src/Raygun4php/RaygunClient.php index bb29db1..7c813b7 100755 --- a/src/Raygun4php/RaygunClient.php +++ b/src/Raygun4php/RaygunClient.php @@ -4,7 +4,6 @@ use Raygun4php\Rhumsaa\Uuid\Uuid; use Raygun4php\Interfaces\TransportInterface; -use Raygun4php\Interfaces\RaygunMessageInterface; class RaygunClient { @@ -74,7 +73,7 @@ public function getDisableUserTracking() } /** - * Transmits an error to the Raygun.io API + * Transmits an error to the Raygun API * * @param int $errno The error number * @param string $errstr The error string @@ -84,7 +83,7 @@ public function getDisableUserTracking() * @param array $userCustomData An optional associative array that can be used to place custom key-value * @param int $timestamp Current Unix timestamp in the local timezone, used to set when an error occurred. * data in the message payload - * @return int The HTTP status code of the result when transmitting the message to Raygun.io + * @return int The HTTP status code of the result when transmitting the message to Raygun */ public function SendError( $errno, @@ -111,7 +110,7 @@ public function SendError( } /** - * Transmits an exception to the Raygun.io API + * Transmits an exception to the Raygun API * * @param \Throwable $throwable An exception object to transmit * @param array $tags An optional array of string tags used to provide metadata for the message @@ -119,7 +118,7 @@ public function SendError( * data in the message payload * @param int $timestamp Current Unix timestamp in the local timezone, used to set when an exception * occurred. - * @return int The HTTP status code of the result when transmitting the message to Raygun.io + * @return int The HTTP status code of the result when transmitting the message to Raygun */ public function SendException($throwable, $tags = null, $userCustomData = null, $timestamp = null) { @@ -356,7 +355,7 @@ private function is_assoc($array) } /** - * Transmits a RaygunMessage to the Raygun.io API. + * Transmits a RaygunMessage to the Raygun API. * This is a lower level function used by SendException and SendError and one of those should be used preferably. * * @param \Raygun4php\RaygunMessage $message A populated message to be posted to the Raygun API From c480cb1f6d022a63a8181a560efa942247519dd2 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 12:57:10 +1300 Subject: [PATCH 08/21] Simplify the RaygunSetup class, add missing expected return types --- sample-apps/async-example/raygunSetup.php | 46 +++++++---------------- sample-apps/async-example/viewData.php | 12 +++--- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/sample-apps/async-example/raygunSetup.php b/sample-apps/async-example/raygunSetup.php index 56529c6..998afee 100644 --- a/sample-apps/async-example/raygunSetup.php +++ b/sample-apps/async-example/raygunSetup.php @@ -23,7 +23,7 @@ class RaygunSetup const LOG_FILE_PATH = __DIR__ . '/debug.log'; /** - * @var GuzzleSync + * @var GuzzleAsync|GuzzleSync */ private $transport; @@ -76,47 +76,26 @@ public function __construct($useAsync = true) $raygunClient = new RaygunClient($this->transport); - // Get logged in user info to track affected user + // Get the logged-in user info to track affected user $raygunClient->SetUser("test@example.com", "Test", "Test User", "test@example.com"); $this->raygunClient = $raygunClient; } - /** - * @param int $errno - * @param string $errstr - * @param string $errfile - * @param int $errline - * @param array $tags - * @param array $customData - * @param int $timestamp - */ - public function sendError($errno, $errstr, $errfile, $errline, $tags = null, $customData = null, $timestamp = null) - { - $this->raygunClient->SendError($errno, $errstr, $errfile, $errline, $tags, $customData, $timestamp); - } - - /** - * @param Throwable $exception - * @param array $tags - * @param array $customData - * @param int $timestamp - */ - public function sendException($exception, $tags = null, $customData = null, $timestamp = null) - { - $this->raygunClient->SendException($exception, $tags, $customData, $timestamp); + public function getRaygunClient(): RaygunClient { + return $this->raygunClient; } - public function handleLastError() + public function handleLastError(): void { $last_error = error_get_last(); if (!is_null($last_error)) { - $this->sendError($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); + $this->raygunClient->SendError($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); } } - public function flushAsyncPromises() + public function flushAsyncPromises(): void { if ($this->useAsync) { $this->transport->wait(); @@ -124,10 +103,11 @@ public function flushAsyncPromises() } } - $setup = new RaygunSetup(); + $raygunSetup = new RaygunSetup(); + $raygunClient = $raygunSetup->getRaygunClient(); - set_error_handler([$setup, 'sendError']); - set_exception_handler([$setup, 'sendException']); - register_shutdown_function([$setup, 'handleLastError']); - register_shutdown_function([$setup, 'flushAsyncPromises']); + set_error_handler([$raygunClient, 'SendError']); + set_exception_handler([$raygunClient, 'SendException']); + register_shutdown_function([$raygunSetup, 'handleLastError']); + register_shutdown_function([$raygunSetup, 'flushAsyncPromises']); } diff --git a/sample-apps/async-example/viewData.php b/sample-apps/async-example/viewData.php index 7cee7b8..2f7b143 100644 --- a/sample-apps/async-example/viewData.php +++ b/sample-apps/async-example/viewData.php @@ -23,7 +23,7 @@ public function __construct() /** * @return int */ - public function getTime() + public function getTime(): int { return $this->time; } @@ -31,7 +31,7 @@ public function getTime() /** * @return float */ - public function getDistance() + public function getDistance(): float { return $this->distance; } @@ -39,7 +39,7 @@ public function getDistance() /** * @return bool */ - public function hasSentData() + public function hasSentData(): bool { return isset($this->time) && isset($this->distance); } @@ -47,7 +47,7 @@ public function hasSentData() /** * @return string */ - public function getAveragePace() + public function getAveragePace(): string { return $this->decimalToMinuteString($this->time / $this->distance); } @@ -55,7 +55,7 @@ public function getAveragePace() /** * @return string */ - public function getAverageSpeed() + public function getAverageSpeed(): string { return round($this->distance / ($this->time / 60), self::SPEED_PRECISION); } @@ -64,7 +64,7 @@ public function getAverageSpeed() * @param float $timeAsDecimal * @return string */ - private function decimalToMinuteString(float $timeAsDecimal) + private function decimalToMinuteString(float $timeAsDecimal): string { $whole = floor($timeAsDecimal); $decimal = $timeAsDecimal - $whole; From ae6a72330476e7ce762db0944bde09fa83499b8a Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 17:32:26 +1300 Subject: [PATCH 09/21] Minor fix-ups for the async example app, add API key in an ignored config.php file to avoid commit issues --- sample-apps/.gitignore | 2 ++ sample-apps/async-example/.gitignore | 1 - sample-apps/async-example/config-sample.php | 2 ++ sample-apps/async-example/raygunSetup.php | 16 ++++++++-------- 4 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 sample-apps/.gitignore delete mode 100644 sample-apps/async-example/.gitignore create mode 100644 sample-apps/async-example/config-sample.php diff --git a/sample-apps/.gitignore b/sample-apps/.gitignore new file mode 100644 index 0000000..75122a5 --- /dev/null +++ b/sample-apps/.gitignore @@ -0,0 +1,2 @@ +debug.log +config.php diff --git a/sample-apps/async-example/.gitignore b/sample-apps/async-example/.gitignore deleted file mode 100644 index b14c548..0000000 --- a/sample-apps/async-example/.gitignore +++ /dev/null @@ -1 +0,0 @@ -debug.log diff --git a/sample-apps/async-example/config-sample.php b/sample-apps/async-example/config-sample.php new file mode 100644 index 0000000..c9e082a --- /dev/null +++ b/sample-apps/async-example/config-sample.php @@ -0,0 +1,2 @@ + self::RAYGUN_BASE_URI, 'timeout' => self::HTTP_CLIENT_TIMEOUT, 'headers' => [ - 'X-ApiKey' => self::API_KEY + 'X-ApiKey' => API_KEY ] ]); @@ -86,12 +86,12 @@ public function getRaygunClient(): RaygunClient { return $this->raygunClient; } - public function handleLastError(): void + public function handleFatalError(): void { - $last_error = error_get_last(); + $lastError = error_get_last(); - if (!is_null($last_error)) { - $this->raygunClient->SendError($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); + if (!is_null($lastError)) { + $this->raygunClient->SendError($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']); } } @@ -108,6 +108,6 @@ public function flushAsyncPromises(): void set_error_handler([$raygunClient, 'SendError']); set_exception_handler([$raygunClient, 'SendException']); - register_shutdown_function([$raygunSetup, 'handleLastError']); + register_shutdown_function([$raygunSetup, 'handleFatalError']); register_shutdown_function([$raygunSetup, 'flushAsyncPromises']); } From 3836b5eda27c0651308f53dba6118a77034c88d8 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 17:35:37 +1300 Subject: [PATCH 10/21] Add another sample app for synchronous transmission of errors --- sample-apps/sync-example/composer.json | 6 ++ sample-apps/sync-example/config-sample.php | 2 + sample-apps/sync-example/index.php | 27 +++++++++ sample-apps/sync-example/raygunSetup.php | 70 ++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 sample-apps/sync-example/composer.json create mode 100644 sample-apps/sync-example/config-sample.php create mode 100644 sample-apps/sync-example/index.php create mode 100644 sample-apps/sync-example/raygunSetup.php diff --git a/sample-apps/sync-example/composer.json b/sample-apps/sync-example/composer.json new file mode 100644 index 0000000..141b0cf --- /dev/null +++ b/sample-apps/sync-example/composer.json @@ -0,0 +1,6 @@ +{ + "require": { + "mindscape/raygun4php": "2.x-dev", + "monolog/monolog": "2.0.2" + } +} diff --git a/sample-apps/sync-example/config-sample.php b/sample-apps/sync-example/config-sample.php new file mode 100644 index 0000000..c9e082a --- /dev/null +++ b/sample-apps/sync-example/config-sample.php @@ -0,0 +1,2 @@ + + + + + + + Synchronous example + + + +

Synchronous example app

+ +
+
+ + +
+ + + +
Result:
+
+ + + diff --git a/sample-apps/sync-example/raygunSetup.php b/sample-apps/sync-example/raygunSetup.php new file mode 100644 index 0000000..480ec1b --- /dev/null +++ b/sample-apps/sync-example/raygunSetup.php @@ -0,0 +1,70 @@ +pushHandler(new StreamHandler(LOG_FILE_PATH)); + $logger->pushHandler(new FirePHPHandler()); + + $httpClient = new Client([ + 'base_uri' => RAYGUN_BASE_URI, + 'timeout' => HTTP_CLIENT_TIMEOUT, + 'headers' => [ + 'X-ApiKey' => API_KEY + ] + ]); + + $transport = new GuzzleSync($httpClient); + $transport->setLogger($logger); + + $raygunClient = new RaygunClient($transport, false, $logger); + + class HandlerUtilities { + private $raygunClient; + private $tags; + + public function __construct($raygunClient, $tags) + { + $this->raygunClient = $raygunClient; + $this->tags = $tags; + } + + public function errorHandler($errno, $errstr, $errfile, $errline) { + $this->raygunClient->SendError($errno, $errstr, $errfile, $errline, $this->tags); + } + + public function exceptionHandler($exception) { + $this->raygunClient->SendException($exception, $this->tags); + } + + public function fatalErrorHandler() { + $lastError = error_get_last(); + + if (!is_null($lastError)) { + $this->raygunClient->SendError($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']); + } + } + } + + $tags = ['staging-environment', 'machine-4']; + $handlers = new HandlerUtilities($raygunClient, $tags); + + set_error_handler([$handlers, 'errorHandler']); + set_exception_handler([$handlers, 'exceptionHandler']); + register_shutdown_function([$handlers, 'fatalErrorHandler']); +} + From 5df62443f4c456b4b15545b63392c1c50b8e25ba Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 17:36:34 +1300 Subject: [PATCH 11/21] Update the README.md with updated instructions relevant to the v2 API --- README.md | 229 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 129 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index 8dab98b..2c79e08 100644 --- a/README.md +++ b/README.md @@ -36,152 +36,185 @@ and the library will be imported ready for use. ## Usage -You can send both PHP errors and object-oriented exceptions to Raygun. An easy way to accomplish this is to create a file containing exception and error handlers which make calls to the appropriate Raygun4PHP functions. As above, import Raygun4PHP - if you're using Composer, just add `require_once 'vendor/autoload.php'`, or if not manually import RaygunClient.php. +You can automatically send both PHP errors and object-oriented exceptions to Raygun. -Then, create handlers that look something like this: +This is an example of basic usage for asynchronous sending of errors and exceptions: ```php -namespace -{ - // paste your 'requires' statement - - $client = new \Raygun4php\RaygunClient("APIKEY_FOR_YOUR_APPLICATION"); - - function error_handler($errno, $errstr, $errfile, $errline ) { - global $client; - $client->SendError($errno, $errstr, $errfile, $errline); - } - - function exception_handler($exception) - { - global $client; - $client->SendException($exception); - } - - function fatal_error() - { - global $client; - $last_error = error_get_last(); - - if (!is_null($last_error)) { - $errno = $last_error['type']; - $errstr = $last_error['message']; - $errfile = $last_error['file']; - $errline = $last_error['line']; - $client->SendError($errno, $errstr, $errfile, $errline); - } - } - - set_exception_handler('exception_handler'); - set_error_handler("error_handler"); - register_shutdown_function("fatal_error"); + RAYGUN_BASE_URI, + 'timeout' => HTTP_CLIENT_TIMEOUT, + 'headers' => [ + 'X-ApiKey' => API_KEY + ] + ]); + + $transport = new GuzzleAsync($httpClient); + // For synchronous transmission of data, change this to: + // `$transport = new GuzzleSync($httpClient);` + + $raygunClient = new RaygunClient($transport); + + set_error_handler([$raygunClient, 'SendError']); + set_exception_handler([$raygunClient, 'SendException']); + register_shutdown_function([$transport, 'wait']); // Note: You'll need to omit this line if using synchronous transport } ``` Note that if you are placing in inside a file with a namespace of your choosing, the above code should be declared to be within the global namespace (thus the `namespace { }` is required). You will also need whichever `requires` statement as above (autoload or manual) before the `$client` instantiation. -Copy your application's API key from the Raygun dashboard, and place it in the constructor call as above (do not include the curly brackets). +Copy your application's API key from the Raygun setup instructions, and place it in the constructor call as above (do not include the curly brackets). -If the handlers reside in their own file, just import it in every file where you'd like exceptions and errors to be sent, and they will be delivered to Raygun.com. +If the handlers reside in their own file, just import it in every file where you'd like exceptions and errors to be sent, and they will be delivered to Raygun. -## Configuration (v1) +## Configuration ### Proxies -A HTTP proxy can be set if your environment can't connect out through PHP or the `curl` binary natively: +An HTTP proxy can be set as the `base_uri` property on the HTTP Client: ```php -$client = new \Raygun4php\RaygunClient("apiKey"); -$client->setProxy('http://someproxy:8080'); + 'http://someproxy:8080', + 'headers' => [ + 'X-ApiKey' => 'INSERT_API_KEY_HERE' + ] +]); ``` -### Debug mode +### Debugging with a logger + +We recommend using a logger which is compatible with the PSR-3 LoggerInterface (e.g. Monolog). -The client offers a debug mode in which the HTTP response code can be returned after a POST attempt. This can be useful when adding Raygun to your site. This is accessed by passing in *true* as the third parameter in the client constructor: +Expanding on the example above, you can set a logger to be used by the transport like so: ```php -$client = new \Raygun4php\RaygunClient("apiKey", $useAsyncSending, $debugMode); -``` +// ... + +use Monolog\Handler\FirePHPHandler; +use Monolog\Handler\StreamHandler; +use Monolog\Logger; -#### $debugMode options +// ... -*Default: false* +$logger = new Logger('my_logger'); +$logger->pushHandler(new StreamHandler(__DIR__ . '/debug.log')); +$logger->pushHandler(new FirePHPHandler()); -If true is passed in, and **$useAsyncSending** is set to *false*, client->SendException() or SendError() will return the HTTP status code of the POST attempt. +// Create $httpClient ... -**Note:** If $useAsyncSending is *true*, $debugMode is not available. +$transport = new GuzzleAsync($httpClient); +$transport->setLogger($logger); + +$raygunClient = new RaygunClient($transport, false, $logger); +``` #### Response codes * **202**: Message received by Raygun API correctly -* **403**: Invalid API key. Copy it from your Raygun Application Settings, it should be of the form `new RaygunClient("A+nUc2dLh27vbh8abls7==")` +* **403**: Invalid API key. Copy it from your Raygun Setup Instructions or Application Settings ### Version numbers You can transmit the version number of your PHP project along with the message by calling `SetVersion()` on your RaygunClient after it is instantiated - this is optional but recommended as the version number is considered to be first-class data for a message. +```php +$raygunClient = new RaygunClient($transport); +$raygunClient->SetVersion('1.0.0.0'); +``` + ### Adding Tags Tags can be added to error data to provide extra information and to help filtering errors within Raygun. They are provided as an array of strings or numbers passed as the `5th argument to the SendError function` and as the `2nd argument to the SendException function`. -The declaration of the exception and error handlers using tags could look something like this: +The declaration of the exception and error handlers adding tags to each payload could look something like this: ```php -$tags = array("testing-enviroment", "machine-4"); +SendError($errno, $errstr, $errfile, $errline, $tags); -} + public function __construct($raygunClient, $tags) + { + $this->raygunClient = $raygunClient; + $this->tags = $tags; + } + + public function errorHandler($errno, $errstr, $errfile, $errline) { + $this->raygunClient->SendError($errno, $errstr, $errfile, $errline, $this->tags); + } -function exception_handler($exception) { - global $client, $tags; - $client->SendException($exception, $tags); + public function exceptionHandler($exception) { + $this->raygunClient->SendException($exception, $this->tags); + } + + public function fatalErrorHandler() { + $lastError = error_get_last(); + + if (!is_null($lastError)) { + $this->raygunClient->SendError($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']); + } + } } -function fatal_error() -{ - global $client; - $last_error = error_get_last(); - - if (!is_null($last_error)) { - $errno = $last_error['type']; - $errstr = $last_error['message']; - $errfile = $last_error['file']; - $errline = $last_error['line']; - $client->SendError($errno, $errstr, $errfile, $errline, $tags); - } +// ... Setup Raygun client, etc. ... + +$tags = ['staging-environment', 'machine-4']; +$handlers = new HandlerUtilities($raygunClient, $tags); + +set_error_handler([$handlers, 'errorHandler']); +set_exception_handler([$handlers, 'exceptionHandler']); +register_shutdown_function([$handlers, 'fatalErrorHandler']); +``` + +You can add tags when sending a handled exception like so: + +```php +try { + // Do something questionable... +} catch(Exception $exception) { + $raygunClient->SendException($exception, ['handled-exception']); } ``` ### Affected user tracking -**New in 1.5: additional data support** - -You can call $client->SetUser, passing in some or all of the following data, which will be used to provide an affected user count and reports: +You can call `$raygunClient->SetUser`, passing in some or all of the following data, which will be used to provide an affected user count and reports: ```php -SetUser($user = null, $firstName = null, $fullName = null, $email = null, $isAnonymous = null, $uuid = null) +$raygunClient->SetUser($user = 'email_or_unique_identifier', $firstName = 'Example', $fullName = 'Example User', $emailAddress = 'test@example.com', $isAnonymous = false, $uuid = 'abc123'); ``` `$user` should be a unique identifier which is used to identify your users. If you set this to their email address, be sure to also set the $email parameter too. -This feature and values are optional if you wish to disable it for privacy concerns. To do so, pass `true` in as the third parameter to the RaygunClient constructor. +This feature and values are optional if you wish to disable it for privacy concerns. To do so, pass `true` in as the second parameter to the RaygunClient constructor. Note that this data is stored as cookies. If you do not call SetUser the default is to store a random UUID to represent the user. -This feature can be used in CLI mode by calling SetUser() at the start of your session. +This feature can be used in CLI mode by calling `SetUser()` at the start of your session. ### Custom error grouping -Control of how error instances are grouped together can achieved by passing a callback to the `SetGroupingKey` method on the client. If the callback returns a string, ideally 100 characters or less, errors matching that key will grouped together. Overriding the default automatic grouping. If the callback returns a non-string value then that error will be grouped automatically. +Control of how error instances are grouped together can be achieved by passing a callback to the `SetGroupingKey` method on the client. If the callback returns a string, ideally 100 characters or less, errors matching that key will grouped together. Overriding the default automatic grouping. If the callback returns a non-string value then that error will be grouped automatically. ```php -$client = new \Raygun4php\RaygunClient("apiKey"); -$client->SetGroupingKey(function($payload, $stackTrace) { - // Inspect the above parameters and return a hash from the properties - +$raygunClient->SetGroupingKey(function($payload, $stackTrace) { + // Inspect the above parameters and return a hash from the properties ... return $payload->Details->Error->Message; // Naive message-based grouping only }); ``` @@ -191,8 +224,7 @@ $client->SetGroupingKey(function($payload, $stackTrace) { Some error data will be too sensitive to transmit to an external service, such as credit card details or passwords. Since this data is very application specific, Raygun doesn't filter out anything by default. You can configure to either replace or otherwise transform specific values based on their keys. These transformations apply to form data (`$_POST`), custom user data, HTTP headers, and environment data (`$_SERVER`). It does not filter the URL or its `$_GET` parameters, or custom message strings. Since Raygun doesn't log method arguments in stack traces, those don't need filtering. All key comparisons are case insensitive. ```php -$client = new \Raygun4php\RaygunClient("apiKey"); -$client->setFilterParams(array( +$raygunClient->setFilterParams(array( 'password' => true, 'creditcardnumber' => true, 'ccv' => true, @@ -205,8 +237,7 @@ $client->setFilterParams(array( You can also define keys as regular expressions: ```php -$client = new \Raygun4php\RaygunClient("apiKey"); -$client->setFilterParams(array( +$raygunClient->setFilterParams(array( '/^credit/i' => true, )); // Example input: array('CreditCardNumber' => '4111111111111111','CreditCardCcv' => '123') @@ -216,8 +247,7 @@ $client->setFilterParams(array( In case you want to retain some hints on the data rather than removing it completely, you can also apply custom transformations through PHP's anonymous functions. The following example truncates all keys starting with "address". ```php -$client = new \Raygun4php\RaygunClient("apiKey"); -$client->setFilterParams(array( +$raygunClient->setFilterParams(array( 'Email' => function($key, $val) {return substr($val, 0, 5) . '...';} )); // Example input: array('Email' => 'test@test.com') @@ -231,8 +261,7 @@ Note that when any filters are defined, the Raygun error will no longer contain Cookies are used for the user tracking functionality of the Raygun4PHP provider. In version 1.8 of the provider, the options passed to the `setcookie` method can now be customized to your needs. ```php -$client = new \Raygun4php\RaygunClient("apiKey"); -$client->SetCookieOptions(array( +$raygunClient->SetCookieOptions(array( 'expire' => 2592000, // 30 * 24 * 60 * 60 'path' => '/', 'domain' => '', @@ -243,34 +272,34 @@ $client->SetCookieOptions(array( ## Troubleshooting -As above, enable debug mode by instantiating the client with +As above, you can specify a logger to the HTTP transport: ```php -$client = new \Raygun4php\RaygunClient("apiKey", FALSE, TRUE); +$transport->setLogger($logger); ``` -This will echo the HTTP response code. Check the list above, and create an issue or contact us if you continue to have problems. +This will log out exceptions occurring while transmitting data to Raygun. Create an issue or contact us if you continue to have problems. ### 400 from command-line Posix environments -If, when running a PHP script from the command line on *nix operating systems, you receive a '400 Bad Request' error (when debug mode is enabled), check to see if you have any LESS_TERMCAP environment variables set. These are not compatible with the current version of Raygun4PHP. As a workaround, unset these variables before your script runs, then reset them afterwards. +If, when running a PHP script from the command line on *nix operating systems, you receive a '400 Bad Request' error (in your debug log), check to see if you have any LESS_TERMCAP environment variables set. These are not compatible with the current version of Raygun4PHP. As a workaround, unset these variables before your script runs, then reset them afterwards. ### Error Control Operators (@) -If you are using the setup as described above errors will be send to Raygun regardless of any lines prepended with an error control operator (the @ symbol). To stop these errors from being sent to Raygun you can call PHP's [error_reporting](http://php.net/manual/en/function.error-reporting.php) method which return 0 if the triggered error was preceded by an @ symbol. +If you are using the setup as described above errors will be sent to Raygun regardless of any lines prepended with an error control operator (the @ symbol). To stop these errors from being sent to Raygun you can call PHP's [error_reporting](http://php.net/manual/en/function.error-reporting.php) method which return 0 if the triggered error was preceded by an @ symbol. _Error handler example:_ ```php -function error_handler($errno, $errstr, $errfile, $errline ) { - global $client; +function errorHandler($errno, $errstr, $errfile, $errline ) { + global $raygunClient; if(error_reporting() !== 0) { - $client->SendError($errno, $errstr, $errfile, $errline); + $raygunClient->SendError($errno, $errstr, $errfile, $errline); } } ``` -See the [Error Control Operators section on PHP.net](http://php.net/manual/en/language.operators.errorcontrol.php) for more information +See the [Error Control Operators section on PHP.net](http://php.net/manual/en/language.operators.errorcontrol.php) for more information. ## Changelog - 2.0.0: New major version From 5e99be3982275131620ef73fea8710d8082ed82f Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Fri, 24 Jan 2020 17:41:57 +1300 Subject: [PATCH 12/21] Add readme files for the sample apps --- sample-apps/async-example/README.md | 6 ++++++ sample-apps/sync-example/README.md | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 sample-apps/async-example/README.md create mode 100644 sample-apps/sync-example/README.md diff --git a/sample-apps/async-example/README.md b/sample-apps/async-example/README.md new file mode 100644 index 0000000..da5ad61 --- /dev/null +++ b/sample-apps/async-example/README.md @@ -0,0 +1,6 @@ +## Setup instructions + +1. Install composer dependencies +2. Copy `config-sample.php` and rename to `config.php` +3. Insert your API key from Raygun into the `API_KEY` field +4. Submit the form with empty/zero values to trigger errors diff --git a/sample-apps/sync-example/README.md b/sample-apps/sync-example/README.md new file mode 100644 index 0000000..ccb2e5f --- /dev/null +++ b/sample-apps/sync-example/README.md @@ -0,0 +1,6 @@ +## Setup instructions + +1. Install composer dependencies +2. Copy `config-sample.php` and rename to `config.php` +3. Insert your API key from Raygun into the `API_KEY` field +4. Submit the form with a zero value to trigger an error From 0d619346edb237103a3abf8d5f7225f460bb7f87 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 11:55:22 +1300 Subject: [PATCH 13/21] More tidy-up of the README, improve the code snippet readability --- README.md | 154 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 2c79e08..09d58f3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Raygun4PHP [Raygun.com](http://raygun.com) provider for PHP 7.1+ -_See [v1.8 documentation](https://github.com/MindscapeHQ/raygun4php/blob/1.8/README.md) for support for legacy versions of PHP_ +_See [v1.8 documentation](https://github.com/MindscapeHQ/raygun4php/blob/1.8/README.md) for PHP v5.3+ support_ [![Build Status](https://secure.travis-ci.org/MindscapeHQ/raygun4php.png?branch=master)](http://travis-ci.org/MindscapeHQ/raygun4php) @@ -16,9 +16,10 @@ Firstly, ensure that **curl** is installed and enabled in your server's php.ini Composer is a package management tool for PHP which automatically fetches dependencies and also supports autoloading - this is a low-impact way to get Raygun4PHP into your site. -1. If you use a *nix environment, [follow the instructions](http://getcomposer.org/doc/01-basic-usage.md#installation) to install Composer. Windows users can run [this installer](https://github.com/johnstevenson/composer-setup) to automatically add it to the Path. +1\. If you use a *nix environment, [follow the instructions](http://getcomposer.org/doc/01-basic-usage.md#installation) to install Composer. Windows users can run [this installer](https://github.com/johnstevenson/composer-setup) to automatically add it to the Path. + +2\. Inside your project's root directory create a composer.json file, containing: -2. Inside your project's root directory create a composer.json file, containing: ```json { "require": { @@ -26,9 +27,10 @@ Composer is a package management tool for PHP which automatically fetches depend } } ``` -3. From your shell run `php composer.phar install` (*nix) or `composer install` (Windows). This will download Raygun4PHP and create the appropriate autoload data. -4. Then in a PHP file just add: +3\. From your shell run `php composer.phar install` (*nix) or `composer install` (Windows). This will download Raygun4PHP and create the appropriate autoload data. + +4\. Then in a PHP file just add: ```php require_once 'vendor/autoload.php'; ``` @@ -36,7 +38,11 @@ and the library will be imported ready for use. ## Usage -You can automatically send both PHP errors and object-oriented exceptions to Raygun. +You can automatically send both PHP errors and object-oriented exceptions to Raygun. The RaygunClient requires an HTTP transport (e.g. [Guzzle](http://docs.guzzlephp.org/), [UniRest](http://unirest.io/php.html), etc.). + +There are Guzzle-based asynchronous and synchronous transport classes in the provider, or you can use your own. + +### Asynchronous transport This is an example of basic usage for asynchronous sending of errors and exceptions: @@ -49,33 +55,62 @@ namespace { use Raygun4php\RaygunClient; use Raygun4php\Transports\GuzzleAsync; - const RAYGUN_BASE_URI = 'http://api.raygun.com'; - const HTTP_CLIENT_TIMEOUT = 2.0; - const API_KEY = 'INSERT_API_KEY_HERE'; - $httpClient = new Client([ - 'base_uri' => RAYGUN_BASE_URI, - 'timeout' => HTTP_CLIENT_TIMEOUT, + 'base_uri' => 'https://api.raygun.com', + 'timeout' => 2.0, 'headers' => [ - 'X-ApiKey' => API_KEY + 'X-ApiKey' => 'INSERT_API_KEY_HERE' ] ]); $transport = new GuzzleAsync($httpClient); - // For synchronous transmission of data, change this to: - // `$transport = new GuzzleSync($httpClient);` $raygunClient = new RaygunClient($transport); - set_error_handler([$raygunClient, 'SendError']); - set_exception_handler([$raygunClient, 'SendException']); - register_shutdown_function([$transport, 'wait']); // Note: You'll need to omit this line if using synchronous transport + set_error_handler(function($errno, $errstr, $errfile, $errline) use ($raygunClient) { + $raygunClient->SendError($errno, $errstr, $errfile, $errline); + }); + + set_exception_handler(function($exception) use ($raygunClient) { + $raygunClient->SendException($exception); + }); + + register_shutdown_function(function() use ($raygunClient) { + $lastError = error_get_last(); + + if (!is_null($lastError)) { + [$type, $message, $file, $line] = $lastError; + $raygunClient->SendError($type, $message, $file, $line); + } + }); + + register_shutdown_function([$transport, 'wait']); } ``` -Note that if you are placing in inside a file with a namespace of your choosing, the above code should be declared to be within the global namespace (thus the `namespace { }` is required). You will also need whichever `requires` statement as above (autoload or manual) before the `$client` instantiation. +### Synchronous transport + +For synchronous transport, use the snippet above but replace `use Raygun4php\Transports\GuzzleAsync;` with: + +```php +use Raygun4php\Transports\GuzzleSync; +``` + +And replace `$transport = new GuzzleAsync($httpClient);` with: + +```php +$transport = new GuzzleSync($httpClient); +``` + +Remove this line: + +```php +register_shutdown_function([$transport, 'wait']); +``` + +Note that if you are placing it inside a file with a namespace of your choosing, the above code should be declared to be within the global namespace (thus the `namespace { }` is required). You will also need whichever `requires` statement as above (autoload or manual) before the `$raygunClient` instantiation. -Copy your application's API key from the Raygun setup instructions, and place it in the constructor call as above (do not include the curly brackets). +Copy your application's API key from the [Raygun app](https://app.raygun.com), and paste it into the `X-ApiKey` header field of the HTTP client. If the handlers reside in their own file, just import it in every file where you'd like exceptions and errors to be sent, and they will be delivered to Raygun. @@ -97,7 +132,7 @@ $httpClient = new Client([ ### Debugging with a logger -We recommend using a logger which is compatible with the PSR-3 LoggerInterface (e.g. Monolog). +We recommend using a logger which is compatible with the [PSR-3 LoggerInterface](https://www.php-fig.org/psr/psr-3/) (e.g. [Monolog](https://github.com/Seldaek/monolog)). Expanding on the example above, you can set a logger to be used by the transport like so: @@ -120,12 +155,14 @@ $transport = new GuzzleAsync($httpClient); $transport->setLogger($logger); $raygunClient = new RaygunClient($transport, false, $logger); + +// ... ``` #### Response codes * **202**: Message received by Raygun API correctly -* **403**: Invalid API key. Copy it from your Raygun Setup Instructions or Application Settings +* **403**: Invalid API key. Copy the API key from the [Raygun app](https://app.raygun.com) setup instructions or application settings ### Version numbers @@ -141,54 +178,43 @@ $raygunClient->SetVersion('1.0.0.0'); Tags can be added to error data to provide extra information and to help filtering errors within Raygun. They are provided as an array of strings or numbers passed as the `5th argument to the SendError function` and as the `2nd argument to the SendException function`. -The declaration of the exception and error handlers adding tags to each payload could look something like this: +The declaration of the exception and error handlers for adding tags to each payload could look something like this: ```php raygunClient = $raygunClient; - $this->tags = $tags; - } +// ... - public function errorHandler($errno, $errstr, $errfile, $errline) { - $this->raygunClient->SendError($errno, $errstr, $errfile, $errline, $this->tags); - } +$raygunClient = new RaygunClient($transport); - public function exceptionHandler($exception) { - $this->raygunClient->SendException($exception, $this->tags); - } +$tags = ['staging-environment', 'machine-4']; - public function fatalErrorHandler() { - $lastError = error_get_last(); +set_error_handler(function($errno, $errstr, $errfile, $errline) use ($raygunClient, $tags) { + $raygunClient->SendError($errno, $errstr, $errfile, $errline, $tags); +}); - if (!is_null($lastError)) { - $this->raygunClient->SendError($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']); - } - } -} +set_exception_handler(function($exception) use ($raygunClient, $tags) { + $raygunClient->SendException($exception, $tags); +}); -// ... Setup Raygun client, etc. ... +register_shutdown_function(function() use ($raygunClient, $tags) { + $lastError = error_get_last(); -$tags = ['staging-environment', 'machine-4']; -$handlers = new HandlerUtilities($raygunClient, $tags); + if (!is_null($lastError)) { + [$type, $message, $file, $line] = $lastError; + $raygunClient->SendError($type, $message, $file, $line, $tags); + } +}); -set_error_handler([$handlers, 'errorHandler']); -set_exception_handler([$handlers, 'exceptionHandler']); -register_shutdown_function([$handlers, 'fatalErrorHandler']); +// ... ``` -You can add tags when sending a handled exception like so: +You can add tags when manually sending a handled exception like so: ```php try { - // Do something questionable... + // Do something questionable... } catch(Exception $exception) { - $raygunClient->SendException($exception, ['handled-exception']); + $raygunClient->SendException($exception, ['handled-exception']); } ``` @@ -200,17 +226,22 @@ You can call `$raygunClient->SetUser`, passing in some or all of the following d $raygunClient->SetUser($user = 'email_or_unique_identifier', $firstName = 'Example', $fullName = 'Example User', $emailAddress = 'test@example.com', $isAnonymous = false, $uuid = 'abc123'); ``` -`$user` should be a unique identifier which is used to identify your users. If you set this to their email address, be sure to also set the $email parameter too. +`$user` should be a unique identifier which is used to identify your users. If you set this to their email address, be sure to also set the `$email` parameter too. This feature and values are optional if you wish to disable it for privacy concerns. To do so, pass `true` in as the second parameter to the RaygunClient constructor. +```php +// Disable user tracking: +$raygunClient = new RaygunClient($transport, true); +``` + Note that this data is stored as cookies. If you do not call SetUser the default is to store a random UUID to represent the user. This feature can be used in CLI mode by calling `SetUser()` at the start of your session. ### Custom error grouping -Control of how error instances are grouped together can be achieved by passing a callback to the `SetGroupingKey` method on the client. If the callback returns a string, ideally 100 characters or less, errors matching that key will grouped together. Overriding the default automatic grouping. If the callback returns a non-string value then that error will be grouped automatically. +Control of how error instances are grouped together can be achieved by passing a callback to the `SetGroupingKey` method on the client. If the callback returns a string, ideally 100 characters or less, errors matching that key will be grouped together, overriding the default automatic grouping. If the callback returns a non-string value then that error will be grouped automatically. ```php $raygunClient->SetGroupingKey(function($payload, $stackTrace) { @@ -272,17 +303,17 @@ $raygunClient->SetCookieOptions(array( ## Troubleshooting -As above, you can specify a logger to the HTTP transport: +As mentioned above, you can specify a logger to the HTTP transport: ```php $transport->setLogger($logger); ``` -This will log out exceptions occurring while transmitting data to Raygun. Create an issue or contact us if you continue to have problems. +This will log out exceptions occurring while transmitting data to Raygun. [Create an issue](https://raygun.com/forums/forum/3) or [contact us](https://raygun.com/about/contact) if you continue to have problems. ### 400 from command-line Posix environments -If, when running a PHP script from the command line on *nix operating systems, you receive a '400 Bad Request' error (in your debug log), check to see if you have any LESS_TERMCAP environment variables set. These are not compatible with the current version of Raygun4PHP. As a workaround, unset these variables before your script runs, then reset them afterwards. +If, when running a PHP script from the command line on *nix operating systems, you receive a '400 Bad Request' error (in your debug log), check to see if you have any `LESS_TERMCAP` environment variables set. These are not compatible with the current version of Raygun4PHP. As a workaround, unset these variables before your script runs, then reset them afterwards. ### Error Control Operators (@) @@ -291,9 +322,8 @@ If you are using the setup as described above errors will be sent to Raygun rega _Error handler example:_ ```php -function errorHandler($errno, $errstr, $errfile, $errline ) { - global $raygunClient; - if(error_reporting() !== 0) { +function ($errno, $errstr, $errfile, $errline) use ($raygunClient) { + if (error_reporting() !== 0) { $raygunClient->SendError($errno, $errstr, $errfile, $errline); } } From cb75d04ec60d9e53409dea5efb8dc4dade7dfd92 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 11:56:01 +1300 Subject: [PATCH 14/21] Change time and distance getter return types to be nullable --- sample-apps/async-example/viewData.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample-apps/async-example/viewData.php b/sample-apps/async-example/viewData.php index 2f7b143..70c8eda 100644 --- a/sample-apps/async-example/viewData.php +++ b/sample-apps/async-example/viewData.php @@ -23,7 +23,7 @@ public function __construct() /** * @return int */ - public function getTime(): int + public function getTime(): ?int { return $this->time; } @@ -31,7 +31,7 @@ public function getTime(): int /** * @return float */ - public function getDistance(): float + public function getDistance(): ?float { return $this->distance; } From 3c1329a22ddc239ddab5edbe829aee109643e477 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 11:57:29 +1300 Subject: [PATCH 15/21] Add tags to the async example raygun setup, add wrapper functions for sending errors and exceptions --- sample-apps/async-example/raygunSetup.php | 33 ++++++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/sample-apps/async-example/raygunSetup.php b/sample-apps/async-example/raygunSetup.php index 5e525d3..62da951 100644 --- a/sample-apps/async-example/raygunSetup.php +++ b/sample-apps/async-example/raygunSetup.php @@ -47,11 +47,17 @@ class RaygunSetup */ private $raygunClient; + /** + * @var array + */ + private $tags; + /** * RaygunSetup constructor. + * @param array $tags * @param bool $useAsync */ - public function __construct($useAsync = true) + public function __construct($tags = [], $useAsync = true) { $this->useAsync = $useAsync; $this->logger = new Logger(self::LOGGER_NAME); @@ -79,19 +85,33 @@ public function __construct($useAsync = true) // Get the logged-in user info to track affected user $raygunClient->SetUser("test@example.com", "Test", "Test User", "test@example.com"); + $this->tags = $tags; + $this->raygunClient = $raygunClient; } - public function getRaygunClient(): RaygunClient { + public function getRaygunClient(): RaygunClient + { return $this->raygunClient; } + public function sendError($errno, $errstr, $errfile, $errline): void { + $this->raygunClient->SendError($errno, $errstr, $errfile, $errline, $this->tags); + } + + public function sendException($exception): void { + $this->raygunClient->SendException($exception, $this->tags); + } + public function handleFatalError(): void { $lastError = error_get_last(); if (!is_null($lastError)) { - $this->raygunClient->SendError($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']); + [$type, $message, $file, $line] = $lastError; + + $tags = array_merge($this->tags, ['fatal-error']); + $this->raygunClient->SendError($type, $message, $file, $line, $tags); } } @@ -103,11 +123,10 @@ public function flushAsyncPromises(): void } } - $raygunSetup = new RaygunSetup(); - $raygunClient = $raygunSetup->getRaygunClient(); + $raygunSetup = new RaygunSetup(['local']); - set_error_handler([$raygunClient, 'SendError']); - set_exception_handler([$raygunClient, 'SendException']); + set_error_handler([$raygunSetup, 'sendError']); + set_exception_handler([$raygunSetup, 'sendException']); register_shutdown_function([$raygunSetup, 'handleFatalError']); register_shutdown_function([$raygunSetup, 'flushAsyncPromises']); } From ba1a4e4c31c9114086428c08eb54857b380ae4d2 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 12:00:15 +1300 Subject: [PATCH 16/21] Add tags to the sync example setup, replace utils with closures --- sample-apps/sync-example/raygunSetup.php | 43 ++++++++---------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/sample-apps/sync-example/raygunSetup.php b/sample-apps/sync-example/raygunSetup.php index 480ec1b..7b57378 100644 --- a/sample-apps/sync-example/raygunSetup.php +++ b/sample-apps/sync-example/raygunSetup.php @@ -32,39 +32,24 @@ $transport->setLogger($logger); $raygunClient = new RaygunClient($transport, false, $logger); + $tags = ['local-environment', 'machine-4']; - class HandlerUtilities { - private $raygunClient; - private $tags; + set_error_handler(function ($errno, $errstr, $errfile, $errline) use ($raygunClient, $tags) { + $raygunClient->SendError($errno, $errstr, $errfile, $errline, $tags); + }); - public function __construct($raygunClient, $tags) - { - $this->raygunClient = $raygunClient; - $this->tags = $tags; - } + set_exception_handler(function ($exception) use ($raygunClient, $tags) { + $raygunClient->SendException($exception, $tags); + }); - public function errorHandler($errno, $errstr, $errfile, $errline) { - $this->raygunClient->SendError($errno, $errstr, $errfile, $errline, $this->tags); - } + register_shutdown_function(function () use ($raygunClient, $tags) { + $lastError = error_get_last(); - public function exceptionHandler($exception) { - $this->raygunClient->SendException($exception, $this->tags); + if (!is_null($lastError)) { + [$type, $message, $file, $line] = $lastError; + $_tags = array_merge($tags, ['fatal-error']); + $raygunClient->SendError($type, $message, $file, $line, $_tags); } - - public function fatalErrorHandler() { - $lastError = error_get_last(); - - if (!is_null($lastError)) { - $this->raygunClient->SendError($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line']); - } - } - } - - $tags = ['staging-environment', 'machine-4']; - $handlers = new HandlerUtilities($raygunClient, $tags); - - set_error_handler([$handlers, 'errorHandler']); - set_exception_handler([$handlers, 'exceptionHandler']); - register_shutdown_function([$handlers, 'fatalErrorHandler']); + }); } From 67db11efdb9773992aa3e3251e5bce20ce0d9862 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 12:14:42 +1300 Subject: [PATCH 17/21] Replace UniRest with PSR-7 compatible interface --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09d58f3..4ce3911 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ and the library will be imported ready for use. ## Usage -You can automatically send both PHP errors and object-oriented exceptions to Raygun. The RaygunClient requires an HTTP transport (e.g. [Guzzle](http://docs.guzzlephp.org/), [UniRest](http://unirest.io/php.html), etc.). +You can automatically send both PHP errors and object-oriented exceptions to Raygun. The RaygunClient requires an HTTP transport (e.g. [Guzzle](http://docs.guzzlephp.org/) or other [PSR-7](https://www.php-fig.org/psr/psr-7/) compatible interface). There are Guzzle-based asynchronous and synchronous transport classes in the provider, or you can use your own. From 5cb7d3e4b707a28ac87e126738fb59b28e613b2d Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 12:32:35 +1300 Subject: [PATCH 18/21] Fix typo, improve readability and replace array notation with short syntax --- README.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 4ce3911..1e14b56 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Remove this line: register_shutdown_function([$transport, 'wait']); ``` -Note that if you are placing it inside a file with a namespace of your choosing, the above code should be declared to be within the global namespace (thus the `namespace { }` is required). You will also need whichever `requires` statement as above (autoload or manual) before the `$raygunClient` instantiation. +Note that if you are placing it inside a file with a namespace of your choosing, the above code should be declared to be within the global namespace (thus the `namespace { }` is required). You will also need whichever `require` statement as above (autoload or manual) before the `$raygunClient` instantiation. Copy your application's API key from the [Raygun app](https://app.raygun.com), and paste it into the `X-ApiKey` header field of the HTTP client. @@ -121,7 +121,8 @@ If the handlers reside in their own file, just import it in every file where you An HTTP proxy can be set as the `base_uri` property on the HTTP Client: ```php - 'http://someproxy:8080', 'headers' => [ @@ -134,7 +135,7 @@ $httpClient = new Client([ We recommend using a logger which is compatible with the [PSR-3 LoggerInterface](https://www.php-fig.org/psr/psr-3/) (e.g. [Monolog](https://github.com/Seldaek/monolog)). -Expanding on the example above, you can set a logger to be used by the transport like so: +Expanding on the _Asynchronous transport_ example above, you can set a logger to be used by the transport like so: ```php // ... @@ -255,34 +256,34 @@ $raygunClient->SetGroupingKey(function($payload, $stackTrace) { Some error data will be too sensitive to transmit to an external service, such as credit card details or passwords. Since this data is very application specific, Raygun doesn't filter out anything by default. You can configure to either replace or otherwise transform specific values based on their keys. These transformations apply to form data (`$_POST`), custom user data, HTTP headers, and environment data (`$_SERVER`). It does not filter the URL or its `$_GET` parameters, or custom message strings. Since Raygun doesn't log method arguments in stack traces, those don't need filtering. All key comparisons are case insensitive. ```php -$raygunClient->setFilterParams(array( +$raygunClient->setFilterParams([ 'password' => true, 'creditcardnumber' => true, 'ccv' => true, 'php_auth_pw' => true, // filters basic auth from $_SERVER -)); -// Example input: array('Username' => 'myuser','Password' => 'secret') -// Example output: array('Username' => 'myuser','Password' => '[filtered]') +]); +// Example input: ['Username' => 'myuser','Password' => 'secret'] +// Example output: ['Username' => 'myuser','Password' => '[filtered]'] ``` You can also define keys as regular expressions: ```php -$raygunClient->setFilterParams(array( +$raygunClient->setFilterParams([ '/^credit/i' => true, -)); -// Example input: array('CreditCardNumber' => '4111111111111111','CreditCardCcv' => '123') -// Example output: array('CreditCardNumber' => '[filtered]','CreditCardCcv' => '[filtered]') +]); +// Example input: ['CreditCardNumber' => '4111111111111111','CreditCardCcv' => '123'] +// Example output: ['CreditCardNumber' => '[filtered]','CreditCardCcv' => '[filtered]'] ``` In case you want to retain some hints on the data rather than removing it completely, you can also apply custom transformations through PHP's anonymous functions. The following example truncates all keys starting with "address". ```php -$raygunClient->setFilterParams(array( +$raygunClient->setFilterParams([ 'Email' => function($key, $val) {return substr($val, 0, 5) . '...';} -)); -// Example input: array('Email' => 'test@test.com') -// Example output: array('Email' => 'test@...') +]); +// Example input: ['Email' => 'test@test.com'] +// Example output: ['Email' => 'test@...'] ``` Note that when any filters are defined, the Raygun error will no longer contain the raw HTTP data, since there's no effective way to filter it. @@ -292,13 +293,13 @@ Note that when any filters are defined, the Raygun error will no longer contain Cookies are used for the user tracking functionality of the Raygun4PHP provider. In version 1.8 of the provider, the options passed to the `setcookie` method can now be customized to your needs. ```php -$raygunClient->SetCookieOptions(array( +$raygunClient->SetCookieOptions([ 'expire' => 2592000, // 30 * 24 * 60 * 60 'path' => '/', 'domain' => '', 'secure' => false, 'httponly' => false -)); +]); ``` ## Troubleshooting From 5e2b9c90f7f08171c395cb08f7ab38b20268d113 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 14:23:04 +1300 Subject: [PATCH 19/21] Amend the proxy instructions --- README.md | 7 +++++-- composer.json | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1e14b56..65d8d31 100644 --- a/README.md +++ b/README.md @@ -118,19 +118,22 @@ If the handlers reside in their own file, just import it in every file where you ### Proxies -An HTTP proxy can be set as the `base_uri` property on the HTTP Client: +A URL can be set as the `proxy` property on the HTTP Client: ```php // ... $httpClient = new Client([ - 'base_uri' => 'http://someproxy:8080', + 'base_uri' => 'https://api.raygun.com', + 'proxy' => 'http://someproxy:8080', 'headers' => [ 'X-ApiKey' => 'INSERT_API_KEY_HERE' ] ]); ``` +See Guzzle's [proxy documentation](http://docs.guzzlephp.org/en/stable/request-options.html#proxy) for more options. + ### Debugging with a logger We recommend using a logger which is compatible with the [PSR-3 LoggerInterface](https://www.php-fig.org/psr/psr-3/) (e.g. [Monolog](https://github.com/Seldaek/monolog)). diff --git a/composer.json b/composer.json index e5b649f..d6c0f60 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "exceptions", "logging" ], - "homepage": "http://raygun.io", + "homepage": "http://raygun.com", "license": "MIT", "authors": [ { From b6ae361282ac6f8d3768f19ee93b8ebfbfabef72 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 14:24:37 +1300 Subject: [PATCH 20/21] Minor adjustments to existing v2 usage doc --- UsageExampleV2.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UsageExampleV2.md b/UsageExampleV2.md index 75eea07..91b9c01 100644 --- a/UsageExampleV2.md +++ b/UsageExampleV2.md @@ -1,12 +1,12 @@ ## Usage ```php - + $apiUrl, From 1f320ebcb372d2156d885645c6df0d1d8a3c0921 Mon Sep 17 00:00:00 2001 From: samuel-holt Date: Mon, 27 Jan 2020 17:16:51 +1300 Subject: [PATCH 21/21] Improve consistency of indentation in readme --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 65d8d31..3e89e51 100644 --- a/README.md +++ b/README.md @@ -249,8 +249,8 @@ Control of how error instances are grouped together can be achieved by passing a ```php $raygunClient->SetGroupingKey(function($payload, $stackTrace) { - // Inspect the above parameters and return a hash from the properties ... - return $payload->Details->Error->Message; // Naive message-based grouping only + // Inspect the above parameters and return a hash from the properties ... + return $payload->Details->Error->Message; // Naive message-based grouping only }); ``` @@ -260,10 +260,10 @@ Some error data will be too sensitive to transmit to an external service, such a ```php $raygunClient->setFilterParams([ - 'password' => true, - 'creditcardnumber' => true, - 'ccv' => true, - 'php_auth_pw' => true, // filters basic auth from $_SERVER + 'password' => true, + 'creditcardnumber' => true, + 'ccv' => true, + 'php_auth_pw' => true, // filters basic auth from $_SERVER ]); // Example input: ['Username' => 'myuser','Password' => 'secret'] // Example output: ['Username' => 'myuser','Password' => '[filtered]'] @@ -273,7 +273,7 @@ You can also define keys as regular expressions: ```php $raygunClient->setFilterParams([ - '/^credit/i' => true, + '/^credit/i' => true, ]); // Example input: ['CreditCardNumber' => '4111111111111111','CreditCardCcv' => '123'] // Example output: ['CreditCardNumber' => '[filtered]','CreditCardCcv' => '[filtered]'] @@ -283,7 +283,7 @@ In case you want to retain some hints on the data rather than removing it comple ```php $raygunClient->setFilterParams([ - 'Email' => function($key, $val) {return substr($val, 0, 5) . '...';} + 'Email' => function($key, $val) {return substr($val, 0, 5) . '...';} ]); // Example input: ['Email' => 'test@test.com'] // Example output: ['Email' => 'test@...']