Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 42 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@

It is an easy way to make sure that everyone has to check if they have (not) received a `null`.

## Examples

## Basic usage

```php
namespace PetrKnap\Optional;
use PetrKnap\Optional\Optional;

/** @var Optional<string> $stringOption */
$stringOption = Optional::of('data');
Expand All @@ -28,34 +29,49 @@ Optional::ofFalsable(tmpfile())->ifPresent(function ($tmpFile): void {
}, else: fn () => print('tmpfile() failed'));
```

### Create and use your own typed optional

## Creating your own typed optional

The library provides specialized traits to help you implement strictly typed wrappers with full static analysis support.

### Quick Overview

1. **[`NonGenericOptional` trait](./src/NonGenericOptional.php)** for fixed specific types like `bool` or `string` (see [`OptionalString` class](./tests/Some/OptionalString.php))
```php
use PetrKnap\Optional\Optional;
use PetrKnap\Optional\NonGenericOptional;

/** @extends Optional<bool> */
final class OptionalBool extends Optional
{
use NonGenericOptional;

protected static function isSupported(mixed $value): bool
{
return is_bool($value);
}
}
```
2. **[`GenericOptional` trait](./src/GenericOptional.php)** for generic structures like `array` (see [`OptionalArray` class](./tests/Some/OptionalArray.php))
3. **[`AbstractOptional` trait](./src/AbstractOptional.php)** for extendable factory optionals (use this when you need a base class for other typed optionals, see [`OptionalObject` class](./tests/Some/OptionalObject.php))

### Type registration

You can register your custom typed optionals into [`TypedOptional` helper](./src/TypedOptional.php).
Once registered, calling the base [`Optional` class](./src/Optional.php) will automatically look up and return the correct specific subclass if the value matches its criteria.
```php
namespace PetrKnap\Optional;

/**
* @extends Optional<Some\DataObject>
*/
class YourOptional extends Optional {
use NonGenericOptional;
protected static function isSupported(mixed $value): bool {
return $value instanceof Some\DataObject;
}
}
TypedOptional::register(YourOptional::class); // optional recommended step
use PetrKnap\Optional\TypedOptional;
use PetrKnap\Optional\Optional;

function your_strong_typed_function(YourOptional $input): YourOptional {
return YourOptional::empty();
}
TypedOptional::register(OptionalBool::class);

/**
* @param Optional<Some\DataObject> $input
*
* @return Optional<Some\DataObject>
*/
function your_weak_typed_function(Optional $input): Optional {
return YourOptional::empty();
}
printf(
"Class name of optional which holds `true` is `%s`.\n",
get_class(Optional::of(true)),
);
```
```
Class name of optional which holds `true` is `OptionalBool`.
```

---
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
},
"require-dev": {
"nunomaduro/phpinsights": "^2.11",
"petrknap/shorts": "^3.0",
"petrknap/shorts": "^3.3",
"phpstan/phpstan": "^1.12",
"phpunit/phpunit": "^10.5",
"squizlabs/php_codesniffer": "^3.7"
Expand Down
5 changes: 3 additions & 2 deletions tests/ReadmeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ public static function getPathToMarkdownFile(): string
public static function getExpectedOutputsOfPhpExamples(): iterable
{
return [
'examples' => 'data',
'create-and-use-your-own-typed-optional' => '',
'Basic usage' => 'data',
'Creating your own typed optional / Quick Overview / NonGenericOptional' => '',
'Creating your own typed optional / Type registration' => self::OUTPUT_IN_MARKDOWN,
];
}
}
Loading