-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBox.php
136 lines (118 loc) · 3.11 KB
/
Box.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<?php
declare(strict_types=1);
namespace Epic64\PhpBox;
use LogicException;
/**
* A container that allows chaining transformations and assertions on a value.
*
* @template T of mixed
*/
readonly class Box
{
/**
* @template U
* @param U $value
* @return Box<U>
*/
public static function of($value): Box
{
return new self($value);
}
/**
* @param T $value
*/
public function __construct(private mixed $value)
{
}
/**
* Apply a transformation function to the value.
*
* Caution: This method will reuse values passed by reference (e.g. objects) to minimize performance overhead.
* For a side effect free version, use pure() instead.
*
* @template U
* @param callable(T): U $callback
* @return Box<U>
*/
public function map(callable $callback): Box
{
return new self($callback($this->value));
}
/**
* Modify the box itself via a callback.
*
* Useful grouping multiple calls on a box into one call (e.g. for common validation rules).
*
* @template U
* @param callable(self<T>): Box<U> $callback
* @return Box<U>
*/
public function flatMap(callable $callback): Box
{
return $callback($this);
}
/**
* Unwrap the value and apply a final transformation on it.
* Can be used instead of `unbox` to terminate the sequence.
*
* @template U
* @param callable(T): U $callback
* @return U
*/
public function get(callable $callback)
{
return $callback($this->value());
}
/**
* Unwrap the final value.
*
* @return T
*/
public function value()
{
return $this->value;
}
/**
* Run an assertion against the value.
* Example of a simple strict equality check: ->assert(5)
* Example of a callback check: ->assert(fn($x) => $x > 5)
*
* @template U
* @param U|callable(T):bool $check
* @return Box<T>
*/
public function assert(mixed $check, string $message = ''): Box
{
$isClosure = is_callable($check);
$pass = $isClosure
? $check($this->value)
: $this->value === $check;
if (! $pass) {
$report = $isClosure
? 'Value did not pass the callback check.'
: sprintf(
'Failed asserting that two values are the same. Expected %s, got %s.',
var_export($check, true),
var_export($this->value, true)
);
if ($message !== '') {
$report = $message . ' | ' . $report;
}
throw new LogicException($report);
}
return $this;
}
/**
* Dump the value to the console.
*
* @return Box<T>
*/
public function dump(?string $message = null): Box
{
if ($message !== null) {
echo $message . ': '; // @phpstan-ignore ekinoBannedCode.expression
}
var_dump($this->value); // @phpstan-ignore ekinoBannedCode.function
return $this;
}
}