Installation

Raygun4PHP provider

A library you can easily add to your PHP-based website, which then allows you to transmit all errors and exceptions to your Raygun Crash Reporting dashboard. Installation is painless, and configuring your site to start real time error monitoring and crash reporting takes only 5 minutes.

Raygun4PHP is designed to send both classical PHP errors and Exception objects by providing Send() functions. You can add a set of tags (as an array of strings) to identify certain errors, add custom user data, and customize how errors are grouped.


Installation

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.

  • If you use a *nix environment, follow the instructions to install Composer. Windows users can run this installer to automatically add it to the path etc.
  • Inside your project's root directory create a composer.json file, containing:
{
    "require": {
        "mindscape/raygun4php": "^2.0"
    }
}
  • From your shell run php composer.phar install (*nix) or composer install (Windows). This will download Raygun4PHP and create the appropriate autoload data.
  • Then in a PHP file just add this, and the library will be ready for use:
require_once 'vendor/autoload.php'

Clone the Raygun4PHP repository and copy the folder src/Raygun4php into an appropriate subdirectory in your project, such as /vendor/Raygun4php. Then add the following require definitions for RaygunClient.php inside the file you want to make a call to Send().


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. Then, create handlers that look like this:

<?php
namespace {
    require_once 'vendor/autoload.php';

    use GuzzleHttp\Client;
    use Raygun4php\RaygunClient;
    use Raygun4php\Transports\GuzzleAsync;

    $httpClient = new Client([
        'base_uri' => 'https://api.raygun.com',
        'headers' => [
            'X-ApiKey' => 'paste_your_api_key_here'
        ]
    ]);

    $transport = new GuzzleAsync($httpClient);

    $raygunClient = new RaygunClient($transport);

    set_error_handler(function($errno, $errstr, $errfile, $errline) use ($raygunClient) {
        $raygunClient->SendError($errno, $errstr, $errfile, $errline);
    }, E_ALL ^ (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)); // Fatal errors will be sent before shutdown

    set_exception_handler(function($exception) use ($raygunClient) {
        $raygunClient->SendException($exception);
    });

    register_shutdown_function(function() use ($raygunClient) {
        $lastError = error_get_last();
        if (!is_null($lastError) && $lastError['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
            [$type, $message, $file, $line] = $lastError;
            $raygunClient->SendError($type, $message, $file, $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. Copy your application's API key from the Raygun Crash Reporting 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 Crash Reporting.


Deploy Raygun into your production environment for best results, or raise a test exception. Once we detect your first error event, the Raygun app will automatically update.


You can manually send exceptions at any time with the stack trace like this:

<?php
require_once "vendor/autoload.php"; // if using Composer

// ...

try {
    throw new Exception("Your message");
} catch (Exception $e) {
    $raygunClient->SendException($e);
}

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:

<?php
// ...
$raygunClient = new RaygunClient($transport);

$tags = ['staging-environment', 'machine-4'];

set_error_handler(function ($errno, $errstr, $errfile, $errline) use ($raygunClient, $tags) {
    $raygunClient->SendError($errno, $errstr, $errfile, $errline, $tags);
}, E_ALL ^ (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)); // Fatal errors will be sent before shutdown

set_exception_handler(function ($exception) use ($raygunClient, $tags) {
    $_tags = array_merge($tags, ['uncaught-exception', 'fatal']);
    $raygunClient->SendException($exception, $_tags);
});

register_shutdown_function(function () use ($raygunClient, $tags) {
    $lastError = error_get_last();
    if (!is_null($lastError) && $lastError['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
        [$type, $message, $file, $line] = $lastError;
        $_tags = array_merge($tags, ['fatal']);
        $raygunClient->SendError($type, $message, $file, $line, $_tags);
    }
});
// ...

If you would prefer more specific tagging, error constants can be mapped to strings. This approach does not require the reflection API, though be wary of changes to the error constants. A mapping such as:

<?php
const E_CONST_TAG = [
    E_ERROR => "error",
    E_WARNING => "warning",
    E_PARSE => "parse-error",
    E_NOTICE => "notice",
    E_CORE_ERROR => "core-error",
    E_CORE_WARNING => "core-warning",
    E_COMPILE_ERROR => "compile-error",
    E_COMPILE_WARNING => "compile-warning",
    E_USER_ERROR => "user-error",
    E_USER_WARNING => "user-warning",
    E_USER_NOTICE => "user-notice",
    E_STRICT => "strict",
    E_RECOVERABLE_ERROR => "recoverable-error",
    E_DEPRECATED => "deprecated",
    E_USER_DEPRECATED => "user-deprecated",
];

Could be incorporated into your handlers like so:

set_error_handler(function($errno, $errstr, $errfile, $errline) use ($raygunClient) {
    $raygunClient->SendError($errno, $errstr, $errfile, $errline, [E_CONST_TAG[$errno]]);
}, E_ALL ^ (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)); // Fatal errors will be sent before shutdown

set_exception_handler(function($exception) use ($raygunClient) {
     $raygunClient->SendException($exception, ['uncaught-exception', 'fatal']);
});

register_shutdown_function(function() use ($raygunClient) {
    $lastError = error_get_last();
    if (!is_null($lastError) && $lastError['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
        [$type, $message, $file, $line] = $lastError;
        $raygunClient->SendError($type, $message, $file, $line, [E_CONST_TAG[$type], 'fatal']);
    }
});

The provider includes two Guzzle interfaces for transporting data; asynchronous and synchronous. We recommend that you have cURL installed on your server. If it is not installed, Guzzle will use a PHP stream wrapper.

The asynchronous usage above is recommended, but you can use the GuzzleSync transport instead.

The transport POSTs the message, blocks and receives the HTTP response from the Raygun API.

<?php
namespace {
    require_once 'vendor/autoload.php';

    use GuzzleHttp\Client;
    use Raygun4php\RaygunClient;
    use Raygun4php\Transports\GuzzleSync;

    $httpClient = new Client([
        'base_uri' => 'https://api.raygun.com',
        'headers' => [
            'X-ApiKey' => 'paste_your_api_key_here'
        ]
    ]);

    $transport = new GuzzleSync($httpClient);

    $raygunClient = new RaygunClient($transport);
    $tags = ['synchronous-transport'];

    set_error_handler(function ($errno, $errstr, $errfile, $errline) use ($raygunClient, $tags) {
        $raygunClient->SendError($errno, $errstr, $errfile, $errline, $tags);
    }, E_ALL ^ (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)); // Fatal errors will be sent before shutdown

    set_exception_handler(function ($exception) use ($raygunClient, $tags) {
        $_tags = array_merge($tags, ['uncaught-exception', 'fatal']);
        $raygunClient->SendException($exception, $_tags);
    });

    register_shutdown_function(function () use ($raygunClient, $tags) {
        $lastError = error_get_last();
        if (!is_null($lastError) && $lastError['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
            [$type, $message, $file, $line] = $lastError;
            $_tags = array_merge($tags, ['fatal']);
            $raygunClient->SendError($type, $message, $file, $line, $_tags);
        }
    });
}

You can make Raygun4PHP post through an HTTP proxy of your choice like this:

<?php
// ...
$httpClient = new Client([
    'base_uri' => 'https://api.raygun.com',
    'proxy' => 'https://someproxy:8080',
    'headers' => [
        'X-ApiKey' => 'paste_your_api_key_here'
    ]
]);

See Guzzle's proxy documentation for more options.

We recommend using a logger which is compatible with the PSR-3 LoggerInterface (e.g. Monolog).

Expanding on the asynchronous usage example above, you can set a logger to be used by the transport like so:

<?php
namespace {
    require_once 'vendor/autoload.php';

    use GuzzleHttp\Client;
    use Raygun4php\RaygunClient;
    use Raygun4php\Transports\GuzzleAsync;
    use Monolog\Handler\FirePHPHandler;
    use Monolog\Handler\StreamHandler;
    use Monolog\Logger;

    $httpClient = new Client([
        'base_uri' => 'https://api.raygun.com',
        'headers' => [
            'X-ApiKey' => 'paste_your_api_key_here'
        ]
    ]);

    $transport = new GuzzleAsync($httpClient);

    // Create logger
    $logger = new Logger('my_logger');
    $logger->pushHandler(new StreamHandler(__DIR__ . '/debug.log'));
    $logger->pushHandler(new FirePHPHandler());

    // Attach logger to transport
    $transport->setLogger($logger);

    $raygunClient = new RaygunClient($transport);

    set_error_handler(function($errno, $errstr, $errfile, $errline) use ($raygunClient) {
        $raygunClient->SendError($errno, $errstr, $errfile, $errline);
    }, E_ALL ^ (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)); // Fatal errors will be sent before shutdown

    set_exception_handler(function($exception) use ($raygunClient) {
        $raygunClient->SendException($exception, ['uncaught-exception', 'fatal']);
    });

    register_shutdown_function(function() use ($raygunClient) {
        $lastError = error_get_last();
        if (!is_null($lastError) && $lastError['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
            [$type, $message, $file, $line] = $lastError;
            $raygunClient->SendError($type, $message, $file, $line, ['fatal']);
        }
    });

    register_shutdown_function([$transport, 'wait']);
}
  • 202: Message received by Raygun API correctly
  • 400: Bad Request. This may indicate an invalid payload - please contact us if you continue to see this.
  • 403: Invalid API key. API keys can be found in your Raygun Application Settings

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');

Custom data can be added as an associative array to a particular error report, or attached to all error reports.

<?php

try {
    $dataFromClient = json_decode($jsonString);
} catch(Exception $e) {
    $customData = [
        "clientId" => "abc123"
    ];
    
    $raygunClient->SendException($e, null, $customData);
}

In your Raygun set-up script:

<?php

// ...
$raygunClient = new RaygunClient($transport);

$tags = [];
$customData = [
    "myKey" => "some value"
];

set_error_handler(function($errno, $errstr, $errfile, $errline) use ($raygunClient, $tags, $customData) {
    $raygunClient->SendError($errno, $errstr, $errfile, $errline, $tags, $customData);
}, E_ALL ^ (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)); // Fatal errors will be sent before shutdown

set_exception_handler(function($exception) use ($raygunClient, $tags, $customData) {
    $_tags = array_merge($tags, ['uncaught-exception', 'fatal']);
    $raygunClient->SendException($exception, $_tags, $customData);
});
// ...

Error instances are grouped automatically using the Raygun4PHP hasher logic.

Grouping is, by default, based (in part) on the message (i.e., $errstr). So it is best to not include identifiers in $errstr (when using $raygunClient->SendError()).

e.g., "User 12345 does not have permissions to launch rocket" would get put in a different group for every user You can use the $userCustomData parameter, to include per instance unique values. Alternatively:

You can override this by passing a callback and manually group instances together by passing the same key for the errors/exceptions. The callback's signature can take in the error payload, stack trace and then return a string which functions as the key before mentioned.

<?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
});

If the callback doesn't return a string, the error will be grouped automatically.


Users are tracked automatically by setting a random UUID, stored as a cookie. You can provide your own details about the user by passing a string (either username or email address) when calling the SetUser($user) method. That information will then be visible in the dashboard.

If the user logs in or out, be sure to call it again passing in the new user (or just call $raygunClient->SetUser() to assign a new random identifier).

<?php
$raygunClient->SetUser($user);

This feature can be used in CLI mode by calling SetUser(string) at the start of your session.

The string properties on a User have a maximum length of 255 characters. Users who have fields that exceed this amount will not be processed.

Extra information can be passed, used to provide an affected user count and reports.

<?php
$raygunClient->SetUser($user, $firstName, $fullName, $email, $isAnonymous, $uuid);

In this case $user should be a unique identifier used to identify your users. If you set this to their email address, be sure to also set the $email parameter too.


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(
    '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]')

You can also define keys as regular expressions:

<?php
$raygunClient->setFilterParams(array(
    '/^credit/i' => true,
));
// Example input: array('CreditCardNumber' => '4111111111111111','CreditCardCcv' => '123')
// Example output: array('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 "Email".

<?php
$raygunClient->setFilterParams(array(
    'Email' => function($key, $val) {return substr($val, 0, 5) . '...';}
));
// Example input: array('Email' => 'test@test.com')
// Example output: array('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.


For more detailed examples, and to see how Raygun works before integrating it into your code, download the sample apps available in the Raygun4PHP repository.

The provider is open source and available at the Raygun4PHP repository.