Skip to content

Commit

Permalink
Customize overwriting file and line in ValidationException
Browse files Browse the repository at this point in the history
I've already changed the `ValidationException` so as not to let the file
and line from the Validator.php [1]. However, one could go even further
when creating more customizations on top of this library, and allowing
to customize the line could be very useful.

What motivated me making this change because it will be handy when I get
back to work on [Assertion][].

[1]: 75a9b8e
[Assertion]: https://github.com/Respect/Assertion
  • Loading branch information
henriquemoody committed Dec 26, 2024
1 parent 1d1da7f commit 9e79a0e
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 10 deletions.
32 changes: 27 additions & 5 deletions library/Exceptions/ValidationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,23 @@

use InvalidArgumentException;

use function array_shift;
use function in_array;
use function realpath;

final class ValidationException extends InvalidArgumentException implements Exception
{
/** @param array<string, mixed> $messages */
/**
* @param array<string, mixed> $messages
* @param array<string> $ignoredBacktracePaths
*/
public function __construct(
string $message,
private readonly string $fullMessage,
private readonly array $messages,
array $ignoredBacktracePaths = [],
) {
if (realpath($this->file) === realpath(__DIR__ . '/../Validator.php')) {
$this->file = $this->getTrace()[0]['file'] ?? $this->file;
$this->line = $this->getTrace()[0]['line'] ?? $this->line;
}
$this->overwriteFileAndLine($ignoredBacktracePaths);
parent::__construct($message);
}

Expand All @@ -38,4 +41,23 @@ public function getMessages(): array
{
return $this->messages;
}

/** @param array<string> $ignoredBacktracePaths */
private function overwriteFileAndLine(array $ignoredBacktracePaths = []): void
{
if ($ignoredBacktracePaths === []) {
return;
}

$trace = $this->getTrace();
while (in_array(realpath($this->file), $ignoredBacktracePaths, true)) {
$top = array_shift($trace);
if ($top === false || !isset($top['file']) || !isset($top['line'])) {
break;

Check warning on line 56 in library/Exceptions/ValidationException.php

View check run for this annotation

Codecov / codecov/patch

library/Exceptions/ValidationException.php#L56

Added line #L56 was not covered by tests
}

$this->file = $top['file'];
$this->line = $top['line'];
}
}
}
19 changes: 14 additions & 5 deletions library/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ final class Validator implements Rule

private ?string $template = null;

/** @param array<string> $ignoredBacktracePaths */
public function __construct(
private readonly Factory $factory,
private readonly Formatter $formatter,
private readonly Translator $translator,
private readonly array $ignoredBacktracePaths = ValidatorDefaults::DEFAULT_BACKTRACE_IGNORED_PATHS,
) {
}

Expand All @@ -48,7 +50,8 @@ public static function create(Rule ...$rules): self
$validator = new self(
ValidatorDefaults::getFactory(),
ValidatorDefaults::getFormatter(),
ValidatorDefaults::getTranslator()
ValidatorDefaults::getTranslator(),
ValidatorDefaults::getIgnoredBacktracePaths()
);
$validator->rules = $rules;

Expand Down Expand Up @@ -89,7 +92,8 @@ public function assert(mixed $input, array|string|Throwable|callable|null $templ
$exception = new ValidationException(
$this->formatter->main($result, $templates, $this->translator),
$this->formatter->full($result, $templates, $this->translator),
$this->formatter->array($result, $templates, $this->translator)
$this->formatter->array($result, $templates, $this->translator),
$this->ignoredBacktracePaths
);

if (!is_callable($template)) {
Expand All @@ -107,6 +111,13 @@ public function setTemplates(array $templates): self
return $this;
}

public function addRule(Rule $rule): self
{
$this->rules[] = $rule;

return $this;
}

/** @return array<Rule> */
public function getRules(): array
{
Expand Down Expand Up @@ -166,8 +177,6 @@ public static function __callStatic(string $ruleName, array $arguments): self
*/
public function __call(string $ruleName, array $arguments): self
{
$this->rules[] = $this->factory->rule($ruleName, $arguments);

return $this;
return $this->addRule($this->factory->rule($ruleName, $arguments));
}
}
16 changes: 16 additions & 0 deletions library/ValidatorDefaults.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@

final class ValidatorDefaults
{
public const DEFAULT_BACKTRACE_IGNORED_PATHS = [__DIR__ . '/Validator.php'];

private static ?Factory $factory = null;

private static ?Formatter $formatter = null;

private static ?Translator $translator = null;

/** @var array<string> */
private static array $ignoredBacktracePaths = self::DEFAULT_BACKTRACE_IGNORED_PATHS;

public static function setFactory(Factory $factory): void
{
self::$factory = $factory;
Expand Down Expand Up @@ -63,4 +68,15 @@ public static function getTranslator(): Translator

return self::$translator;
}

/** @return array<string>*/
public static function getIgnoredBacktracePaths(): array
{
return self::$ignoredBacktracePaths;
}

public static function setIgnoredBacktracePaths(string ...$ignoredBacktracePaths): void

Check warning on line 78 in library/ValidatorDefaults.php

View check run for this annotation

Codecov / codecov/patch

library/ValidatorDefaults.php#L78

Added line #L78 was not covered by tests
{
self::$ignoredBacktracePaths = $ignoredBacktracePaths;

Check warning on line 80 in library/ValidatorDefaults.php

View check run for this annotation

Codecov / codecov/patch

library/ValidatorDefaults.php#L80

Added line #L80 was not covered by tests
}
}
10 changes: 10 additions & 0 deletions tests/feature/ValidationExceptionStackTraceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
declare(strict_types=1);

use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Test\Stubs\MyValidator;

test('Should overwrite stack trace when in Validator', function (): void {
try {
Expand All @@ -18,6 +19,15 @@
}
});

test('Should overwrite in the constructor of Validator', function (): void {
try {
MyValidator::assertIntType('string');
} catch (ValidationException $e) {
expect($e->getFile())->toBe(__FILE__);
expect($e->getLine())->toBe(__LINE__ - 3);
}
});

test('Should not overwrite stack trace when created manually', function (): void {
try {
throw new ValidationException('message', 'fullMessage', ['id' => 'message']);
Expand Down
33 changes: 33 additions & 0 deletions tests/library/Stubs/MyValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* Copyright (c) Alexandre Gomes Gaigalas <[email protected]>
* SPDX-License-Identifier: MIT
*/

declare(strict_types=1);

namespace Respect\Validation\Test\Stubs;

use Respect\Validation\Rules\IntType;
use Respect\Validation\Validator;
use Respect\Validation\ValidatorDefaults;

use function array_merge;

final class MyValidator
{
public static function assertIntType(mixed $input): void
{
$validator = new Validator(
ValidatorDefaults::getFactory(),
ValidatorDefaults::getFormatter(),
ValidatorDefaults::getTranslator(),
array_merge(
ValidatorDefaults::DEFAULT_BACKTRACE_IGNORED_PATHS,
[__FILE__]
)
);
$validator->addRule(new IntType())->assert($input);
}
}

0 comments on commit 9e79a0e

Please sign in to comment.