|
| 1 | +# Results |
| 2 | + |
| 3 | +Results is a PHP library that provides a set of helper functions and classes for handling optional values and results of operations. It is inspired by the [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) and [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) types in Rust. |
| 4 | + |
| 5 | +## Installation |
| 6 | + |
| 7 | +You can install the library via Composer: |
| 8 | + |
| 9 | +```bash |
| 10 | +composer require bosunski/results |
| 11 | +``` |
| 12 | + |
| 13 | +## Usage |
| 14 | + |
| 15 | +### Option |
| 16 | + |
| 17 | +The `Option` type represents an optional value: every `Option` is either `Some` and contains a value, or `None`, and does not. |
| 18 | + |
| 19 | +```php |
| 20 | +use function Bosunski\Results\Option; |
| 21 | + |
| 22 | +$some = Option('value'); // Some |
| 23 | +$none = Option(null); // None |
| 24 | +``` |
| 25 | + |
| 26 | +### Result |
| 27 | + |
| 28 | +The `Result` type is a type that represents either success (`Ok`) or failure (`Err`). |
| 29 | + |
| 30 | +```php |
| 31 | +use function Bosunski\Results\Result; |
| 32 | + |
| 33 | +$ok = Result('value'); // Ok |
| 34 | +$err = Result(new Exception('error')); // Err |
| 35 | +``` |
| 36 | + |
| 37 | +### Helper Functions |
| 38 | + |
| 39 | +The library provides a set of helper functions for creating `Option` and `Result` instances: |
| 40 | + |
| 41 | +- `Some(mixed $value): Some` |
| 42 | +- `None(): None` |
| 43 | +- `Option(mixed $value): Option` |
| 44 | +- `Ok(mixed $value): Ok` |
| 45 | +- `Err(Throwable $e): Err` |
| 46 | +- `Result(mixed $value): Ok|Err` |
| 47 | +- `wrap(callable $fn): Result` |
| 48 | + |
| 49 | +### Examples |
| 50 | + |
| 51 | +Optional values and results in PHP can be represented using the `Option` and `Result` types provided by the Results library. Here are some examples: |
| 52 | + |
| 53 | +```php |
| 54 | +use function Bosunski\Results\Option; |
| 55 | +use function Bosunski\Results\Result; |
| 56 | + |
| 57 | +// Options |
| 58 | +$some = Option('value'); // Some |
| 59 | +$none = Option(null); // None |
| 60 | + |
| 61 | +// Results |
| 62 | +$ok = Result('value'); // Ok |
| 63 | +$err = Result(new Exception('error')); // Err |
| 64 | +``` |
| 65 | + |
| 66 | +In the above examples: |
| 67 | + |
| 68 | +- `Option('value')` creates an `Option` that contains a value (referred to as `Some`). |
| 69 | +- `Option(null)` creates an `Option` that does not contain a value (referred to as `None`). |
| 70 | +- `Result('value')` creates a `Result` that represents a successful operation (`Ok`). |
| 71 | +- `Result(new Exception('error'))` creates a `Result` that represents a failed operation (`Err`). |
| 72 | + |
| 73 | + |
| 74 | +Let's dive into more complex examples of using the `Option` and `Result` types in PHP. |
| 75 | + |
| 76 | +Consider a scenario where we have a function that may or may not return a value. We can use the `Option` type to handle this uncertainty. |
| 77 | + |
| 78 | +```php |
| 79 | +use Bosunski\Results\Option as OptionInterface; |
| 80 | +use function Bosunski\Results\Option; |
| 81 | + |
| 82 | +function findUserById($id): OptionInterface { |
| 83 | + // Assume getUserFromDatabase is a function that returns a User object if found, null otherwise |
| 84 | + $user = getUserFromDatabase($id); |
| 85 | + |
| 86 | + return Option($user); |
| 87 | +} |
| 88 | + |
| 89 | +$userOption = findUserById(123); |
| 90 | + |
| 91 | +// We can then handle the optional value using the methods provided by the Option type |
| 92 | +if ($userOption->isSome()) { |
| 93 | + $user = $userOption->unwrap(); |
| 94 | + // Do something with the user |
| 95 | +} else { |
| 96 | + // Handle the case where no user was found |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +Now, let's consider a scenario where we have a function that can either succeed or fail. We can use the `Result` type to handle this. |
| 101 | + |
| 102 | +```php |
| 103 | +use Bosunski\Results\Result\Result as ResultInterface; |
| 104 | +use function Bosunski\Results\Result; |
| 105 | + |
| 106 | +function divide(int $numerator, int $denominator): ResultInterface { |
| 107 | + if ($denominator == 0) { |
| 108 | + return Err(new Exception("Cannot divide by zero")); |
| 109 | + } else { |
| 110 | + return Ok($numerator / $denominator); |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +$result = divide(10, 0); |
| 115 | + |
| 116 | +// We can then handle the result using the methods provided by the Result type |
| 117 | +if ($result->isOk()) { |
| 118 | + $value = $result->unwrap(); |
| 119 | + // Do something with the value |
| 120 | +} else { |
| 121 | + $error = $result->unwrapErr(); |
| 122 | + // Handle the error |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +In these examples, the `Option` and `Result` types provide a way to handle optional values and the results of operations in a safe and expressive manner. |
| 127 | + |
| 128 | +### The wrap Function |
| 129 | + |
| 130 | +The `wrap` function is a utility function provided by the library. It is designed to handle operations that can throw an error. The `wrap` function executes a `callable` and returns its result wrapped in a `Result` object. If the callable function throws an exception, the wrap function catches it and returns an `Err` object containing the exception. |
| 131 | + |
| 132 | +> [!IMPORTANT] |
| 133 | +> The error that `wrap` captures depends on your error configuration. Also, `wrap` will only capture instances of `Throwable`. If your function throws something that is not an instance of `Throwable`, it will not be captured by `wrap`. |
| 134 | +
|
| 135 | +Here's an example of how you might use the wrap function: |
| 136 | + |
| 137 | +```php |
| 138 | +use function Bosunski\Results\wrap; |
| 139 | + |
| 140 | +function mightThrowException(): int { |
| 141 | + if (rand(0, 1) === 1) { |
| 142 | + throw new Exception('An error occurred'); |
| 143 | + } |
| 144 | + |
| 145 | + return 42; |
| 146 | +} |
| 147 | + |
| 148 | +$result = wrap('mightThrowException'); |
| 149 | + |
| 150 | +if ($result->isOk()) { |
| 151 | + echo "Success: " . $result->unwrap(); |
| 152 | +} else { |
| 153 | + echo "Error: " . $result->unwrapErr()->getMessage(); |
| 154 | +} |
| 155 | +``` |
| 156 | + |
| 157 | +In this example, `mightThrowException` is a function that might throw an exception. We pass this function to `wrap`, which executes it, catch *any* error and wraps the result in a `Result` object. We then check if the result is an instance of `Ok` or `Err` and handle it accordingly. |
| 158 | + |
| 159 | +The `wrap` function provides a safe and expressive way to handle operations that can throw errors, allowing you to focus on your application logic rather than error handling when you don't need to. |
| 160 | + |
| 161 | + |
| 162 | +## Testing |
| 163 | + |
| 164 | +You can run `composer run test` to run the unit tests for this library. |
| 165 | + |
| 166 | +## License |
| 167 | + |
| 168 | +This project is licensed under the MIT License. |
0 commit comments