Skip to content

Commit

Permalink
Merge pull request #66 from codex-team/feature/add-should-handle-erro…
Browse files Browse the repository at this point in the history
…r-method-in-handler

Enhance error handling logic by adding `shouldHandleError` method and updating `Options` class
  • Loading branch information
pavelzotikov authored Dec 5, 2024
2 parents 1e12f85 + 60b1d64 commit 0d83b51
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 52 deletions.
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

0 comments on commit 0d83b51

Please sign in to comment.