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
53 changes: 14 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ It is an easy way to make sure that everyone has to check if they have (not) rec
```php
use PetrKnap\Optional\Optional;

/** @var Optional<string> $stringOption */
$stringOption = Optional::of('data');
if ($stringOption->isPresent()) {
echo $stringOption->get();
Expand All @@ -30,48 +29,24 @@ Optional::ofFalsable(tmpfile())->ifPresent(function ($tmpFile): void {
```


## Creating your own typed optional
## Creating your own optional

The library provides specialized traits to help you implement strictly typed wrappers with full static analysis support.
This library provides [`OptionalOverrides` trait](src/OptionalOverrides.php) to help you implement your own wrapper with full static analysis support (see [`Some\Optional` class](./tests/Some/Optional.php)):

### 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
use PetrKnap\Optional\TypedOptional;
use PetrKnap\Optional\Optional;

TypedOptional::register(OptionalBool::class);

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`.
use PetrKnap\Optional\OptionalOverrides;

/**
* @template-covariant T of mixed type of non-null value
*
* @extends Optional<T>
*/
final class MyOptional extends Optional
{
/** @use OptionalOverrides<T> */
use OptionalOverrides;
}
```

---
Expand Down
5 changes: 1 addition & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
"autoload-dev": {
"psr-4": {
"PetrKnap\\Optional\\": "tests"
},
"files": [
"tests/Some/typed_optionals.php"
]
}
},
"config": {
"allow-plugins": false,
Expand Down
249 changes: 113 additions & 136 deletions phpdoc_check.php

Large diffs are not rendered by default.

51 changes: 0 additions & 51 deletions src/AbstractOptional.php

This file was deleted.

85 changes: 0 additions & 85 deletions src/GenericOptional.php

This file was deleted.

49 changes: 0 additions & 49 deletions src/NonGenericOptional.php

This file was deleted.

32 changes: 4 additions & 28 deletions src/Optional.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ abstract class Optional implements JavaSe8\Optional
final protected function __construct(
protected readonly mixed $value,
) {
if ($this->value !== null && !@static::isSupported($this->value)) {
throw new InvalidArgumentException('Value is not supported.');
}
}

/**
Expand Down Expand Up @@ -84,19 +81,8 @@ public static function ofFalsable(mixed $value): self
public static function ofNullable(mixed $value): self
{
if (static::class === Optional::class) {
if ($value !== null) {
try {
/** @var self<U> */
return TypedOptional::of($value, Optional::class);
} catch (Exception\CouldNotFindTypedOptionalForValue) {
}
}
/** @var self<U> */
return new class ($value) extends Optional {
protected static function isSupported(mixed $value): bool
{
return true;
}
};
}
return new static($value);
Expand Down Expand Up @@ -221,11 +207,9 @@ public function isPresent(): bool
*
* @param callable(T): U $mapper
*
* @return Optional<U>
*
* @note do not use {@see self}/{@see static}, it maps itself to another {@see Optional}
* @return self<U>
*/
public function map(callable $mapper): Optional
public function map(callable $mapper): self
{
/** @var callable(T): Optional<U> $flatMapper */
$flatMapper = static function (mixed $value) use ($mapper): Optional {
Expand All @@ -246,11 +230,8 @@ public function orElseGet(callable $otherSupplier): mixed
if ($this->value !== null) {
return $this->value;
}
/** @var T|null $other */
$other = $otherSupplier();
return $other === null || static::isSupported($other)
? $other
: throw new InvalidArgumentException('Other supplier must return supported other.');
/** @var T|null */
return $otherSupplier();
}

/**
Expand Down Expand Up @@ -294,11 +275,6 @@ public function orElseThrow(
});
}

/**
* @param mixed $value not null
*/
abstract protected static function isSupported(mixed $value): bool;

private static function triggerNotice(string $message): void
{
trigger_error(
Expand Down
Loading
Loading