Skip to content

Commit 17e2e0b

Browse files
committed
PR Review
1 parent 640bbdd commit 17e2e0b

2 files changed

Lines changed: 65 additions & 12 deletions

File tree

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,9 +455,15 @@ $nothing = $fruits->get("a-missing-key", "DEFAULT VALUE"); // PHP warning
455455

456456
```php
457457
$fruits = Block::make($fruitsArray)
458-
->throwOnMissingKey(\OutOfBoundsException::class);
458+
->throwOnMissingKey();
459459

460460
$nothing = $fruits->get("a-missing-key"); // throws exception
461+
462+
463+
$fruits->throwOnMissingKey(
464+
MyMissingKeyException::class,
465+
"The key in the configuration JSON file doens't exist?"
466+
);
461467
```
462468

463469
You can also pass your own exception class (must extend `\Throwable`).

src/Block.php

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,49 +38,91 @@ final class Block implements Iterator, ArrayAccess, Countable
3838
/** @var array<int|string, mixed> */
3939
private array $data;
4040

41-
private const MISSING_KEY_SILENT = 0;
42-
private const MISSING_KEY_WARNING = 1;
41+
/**
42+
* Missing key handling mode: return default value silently.
43+
*/
44+
private const MISSING_KEY_SILENT = 0;
45+
46+
/**
47+
* Missing key handling mode: emit a warning and continue execution.
48+
*/
49+
private const MISSING_KEY_WARNING = 1;
50+
51+
/**
52+
* Missing key handling mode: throw an exception.
53+
*/
4354
private const MISSING_KEY_EXCEPTION = 2;
4455

56+
/**
57+
* Current missing key handling mode.
58+
*
59+
* One of:
60+
* - self::MISSING_KEY_SILENT
61+
* - self::MISSING_KEY_WARNING
62+
* - self::MISSING_KEY_EXCEPTION
63+
*/
4564
private int $missingKeyMode = self::MISSING_KEY_SILENT;
4665

47-
/** @var class-string<\Throwable>|null */
66+
67+
/** @var class-string<\Throwable>|null Exception class to throw on missing key */
4868
private ?string $missingKeyExceptionClass = null;
69+
/** @var string|null Optional hint appended to the exception message */
70+
private ?string $missingKeyExceptionHint = null;
4971

5072
/** @param array<int|string, mixed> $data */
5173
public function __construct(array $data = [], private bool $iteratorReturnsBlock = true)
5274
{
5375
$this->data = $data;
5476
}
5577

78+
/**
79+
* Use silent mode when accessing missing keys (return default value).
80+
*/
5681
public function silentOnMissingKey(): self
5782
{
5883
$this->missingKeyMode = self::MISSING_KEY_SILENT;
84+
$this->missingKeyExceptionClass = null;
85+
$this->missingKeyExceptionHint = null;
5986
return $this;
6087
}
6188

62-
public function warnOnMissingKey(bool $enabled = true): self
89+
/**
90+
* Emit a warning when accessing a missing key.
91+
*/
92+
public function warnOnMissingKey(): self
6393
{
64-
$this->missingKeyMode = $enabled
65-
? self::MISSING_KEY_WARNING
66-
: self::MISSING_KEY_SILENT;
94+
$this->missingKeyMode = self::MISSING_KEY_WARNING;
6795

6896
return $this;
6997
}
7098

7199
/**
100+
* Configure the Block object to throw an exception when accessing a missing key.
101+
*
102+
* When enabled, any access to a non-existing key will throw an exception of the
103+
* given class instead of returning the default value.
104+
*
105+
* An optional hint can be provided to add extra context to the exception message.
72106
*
73-
* @param class-string<\Throwable> $exceptionClass
107+
* @param class-string<\Throwable> $exceptionClass Exception class to throw (must extend Throwable)
108+
* @param string|null $hint Optional additional context appended to the exception message
109+
*
110+
* @return self Returns the current instance for method chaining.
111+
*
112+
* @throws \InvalidArgumentException If the given class does not extend Throwable
74113
*/
75-
public function throwOnMissingKey(string $exceptionClass = \OutOfBoundsException::class): self
76-
{
114+
public function throwOnMissingKey(
115+
string $exceptionClass = \OutOfBoundsException::class,
116+
?string $hint = null,
117+
): self {
77118
/** @phpstan-ignore function.alreadyNarrowedType, booleanAnd.alwaysFalse */
78119
if (!is_subclass_of($exceptionClass, \Throwable::class) && $exceptionClass !== \Throwable::class) {
79120
throw new \InvalidArgumentException("Exception class must extend Throwable");
80121
}
81122

82123
$this->missingKeyMode = self::MISSING_KEY_EXCEPTION;
83124
$this->missingKeyExceptionClass = $exceptionClass;
125+
$this->missingKeyExceptionHint = $hint;
84126

85127
return $this;
86128
}
@@ -94,7 +136,12 @@ private function handleMissingKey(int|string $key, mixed $defaultValue): mixed
94136

95137
case self::MISSING_KEY_EXCEPTION:
96138
$class = $this->missingKeyExceptionClass ?? \OutOfBoundsException::class;
97-
throw new $class("Undefined array key: " . $key);
139+
$message = "Undefined array key: " . $key;
140+
if ($this->missingKeyExceptionHint) {
141+
$message = $message . " (" . $this->missingKeyExceptionHint . ")";
142+
}
143+
144+
throw new $class($message);
98145

99146
case self::MISSING_KEY_SILENT:
100147
default:

0 commit comments

Comments
 (0)