Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance error handling logic by adding shouldHandleError method and updating Options class #66

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"description": "PHP errors Catcher module for Hawk.so",
"keywords": ["hawk", "php", "error", "catcher"],
"type": "library",
"version": "2.2.6",
"version": "2.2.7",
"license": "MIT",
"require": {
"ext-curl": "*",
Expand Down
20 changes: 16 additions & 4 deletions src/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,12 @@ public function handleError(int $level, string $message, string $file, int $line
$eventPayload = $this->eventPayloadBuilder->create($data);
$event = $this->buildEvent($eventPayload);

if ($event !== null) {
if ($event !== null && $this->shouldHandleError($level, $isSilencedError)) {
$this->send($event);
}

if (null !== $this->previousErrorHandler) {
return false !== ($this->previousErrorHandler)($level, $message, $file, $line);
}
if (null !== $this->previousErrorHandler) {
return false !== ($this->previousErrorHandler)($level, $message, $file, $line);
}

return false;
Expand Down Expand Up @@ -335,6 +335,18 @@ public function buildEvent(EventPayload $eventPayload): ?Event
);
}

/**
* Determines if the error should be handled, considering its level and if it was silenced using (@).
*/
private function shouldHandleError(int $level, bool $silenced): bool
{
if ($silenced) {
return $this->options->shouldCaptureSilencedErrors();
}

return ($this->options->getErrorTypes() & $level) !== 0;
}

/**
* Send the event to the remote server.
*/
Expand Down
172 changes: 126 additions & 46 deletions src/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,88 +5,168 @@
namespace Hawk;

/**
* Class Options is responsible for configuring catcher
*
* @package Hawk
* Class Options is responsible for configuring the Hawk catcher.
*/
class Options
{
/**
* Default available options
*
* @var array
* @var string
*/
private $integrationToken = '';

/**
* @var string
*/
private $options = [
'integrationToken' => '',
'url' => 'https://k1.hawk.so/',
'release' => '',
'error_types' => \E_ALL,
'beforeSend' => null,
'timeout' => 10,
private $url = 'https://k1.hawk.so/';

/**
* @var string
*/
private $release = '';

/**
* @var int|null
*/
private $errorTypes = null;

/**
* @var bool
*/
private $captureSilencedErrors = false;

/**
* @var callable|null
*/
private $beforeSend = null;

/**
* @var int
*/
private $timeout = 10;

/**
* Map of accepted option keys to class properties.
*/
private const OPTION_KEYS = [
'integrationToken' => 'integrationToken',
'integration_token' => 'integrationToken',
'url' => 'url',
'release' => 'release',
'errorTypes' => 'errorTypes',
'error_types' => 'errorTypes',
'captureSilencedErrors' => 'captureSilencedErrors',
'capture_silenced_errors' => 'captureSilencedErrors',
'beforeSend' => 'beforeSend',
'before_send' => 'beforeSend',
'timeout' => 'timeout',
];

/**
* Options constructor.
*
* @param array $options
* @param array $options Associative array of options to initialize.
*/
public function __construct(array $options = [])
{
$this->options = array_merge($this->options, $options);
foreach ($options as $key => $value) {
$normalizedKey = self::OPTION_KEYS[$key] ?? null;

if ($normalizedKey === null) {
throw new \InvalidArgumentException("Unknown option: $key");
}

$this->setOption($normalizedKey, $value);
}
}

/**
* Returns access token. It is available on projects settings
* Set a class property based on the normalized option key.
*
* @return string
* @param string $key
* @param mixed $value
*/
public function getIntegrationToken(): string
private function setOption(string $key, $value): void
{
return $this->options['integrationToken'];
switch ($key) {
case 'integrationToken':
case 'release':
case 'url':
if (!is_string($value)) {
throw new \InvalidArgumentException("Option '$key' must be a string.");
}
$this->$key = $value;

break;

case 'errorTypes':
if (!is_int($value) && $value !== null) {
throw new \InvalidArgumentException("Option 'errorTypes' must be an integer or null.");
}
$this->errorTypes = $value;

break;

case 'captureSilencedErrors':
if (!is_bool($value)) {
throw new \InvalidArgumentException("Option 'captureSilencedErrors' must be a boolean.");
}
$this->captureSilencedErrors = $value;

break;

case 'beforeSend':
if (!is_callable($value) && $value !== null) {
throw new \InvalidArgumentException("Option 'beforeSend' must be callable or null.");
}
$this->beforeSend = $value;

break;

case 'timeout':
if (!is_int($value)) {
throw new \InvalidArgumentException("Option 'timeout' must be an integer.");
}
$this->timeout = $value;

break;

default:
throw new \InvalidArgumentException("Unknown option '$key'.");
}
}

/**
* Returns URL that should be send
*
* @return string
*/
public function getUrl(): string
public function getIntegrationToken(): string
{
return $this->options['url'];
return $this->integrationToken;
}

public function getTimeout(): int
public function getUrl(): string
{
return $this->options['timeout'];
return $this->url;
}

/**
* Returns application release
*
* @return string
*/
public function getRelease(): string
{
return $this->options['release'];
return $this->release;
}

/**
* Returns error types
*
* @return int
*/
public function getErrorTypes(): int
{
return $this->options['error_types'];
return $this->errorTypes ?? error_reporting();
}

public function shouldCaptureSilencedErrors(): bool
{
return $this->captureSilencedErrors;
}

/**
* Returns before send callback
*
* @return callable|null
*/
public function getBeforeSend(): ?callable
{
return $this->options['beforeSend'];
return $this->beforeSend;
}

public function getTimeout(): int
{
return $this->timeout;
}
}
2 changes: 1 addition & 1 deletion tests/Unit/OptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function testDefaultOptions(): void
$this->assertEmpty($options->getIntegrationToken());
$this->assertEmpty($options->getRelease());
$this->assertEquals('https://k1.hawk.so/', $options->getUrl());
$this->assertEquals(\E_ALL, $options->getErrorTypes());
$this->assertEquals(error_reporting(), $options->getErrorTypes());
}

public function testCustomOptions(): void
Expand Down
Loading