-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
455 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PetrKnap\Optional; | ||
|
||
use InvalidArgumentException; | ||
use Throwable; | ||
|
||
/** | ||
* @template T of mixed type of non-null value | ||
* | ||
* @see https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html | ||
*/ | ||
abstract class AbstractOptional | ||
{ | ||
private bool|null $wasPresent = null; | ||
|
||
/** | ||
* @deprecated will be changed to protected - use {@see self::ofNullable()}/{@see self::of()}/{@see self::empty()} | ||
* | ||
* @param T|null $value | ||
*/ | ||
final public function __construct( | ||
protected readonly mixed $value, | ||
) { | ||
if ($this->value !== null && !static::isSupported($this->value)) { | ||
throw new InvalidArgumentException('Value is not supported.'); | ||
} | ||
} | ||
|
||
public static function empty(): static | ||
{ | ||
return new static(null); | ||
} | ||
|
||
/** | ||
* @param T $value | ||
*/ | ||
public static function of(mixed $value): static | ||
{ | ||
return $value !== null ? new static($value) : throw new InvalidArgumentException('Value must not be null.'); | ||
} | ||
|
||
/** | ||
* @param T|null $value | ||
*/ | ||
public static function ofNullable(mixed $value): static | ||
{ | ||
return new static($value); | ||
} | ||
|
||
public function equals(mixed $obj): bool | ||
{ | ||
if ($obj instanceof static) { | ||
$obj = $obj->isPresent() ? $obj->get() : null; | ||
} | ||
return ($obj === null || static::isSupported($obj)) && $this->value == $obj; | ||
} | ||
|
||
/** | ||
* @return T | ||
* | ||
* @throws Exception\NoSuchElement | ||
*/ | ||
public function get(): mixed | ||
{ | ||
if ($this->wasPresent === null) { | ||
trigger_error( | ||
'Call `isPresent()` before accessing the value.', | ||
error_level: E_USER_NOTICE, | ||
); | ||
} | ||
return $this->orElseThrow(static fn (): Exception\NoSuchElement => new Exception\NoSuchElement()); | ||
} | ||
|
||
/** | ||
* @param callable(T): void $consumer | ||
*/ | ||
public function ifPresent(callable $consumer): void | ||
{ | ||
if ($this->value !== null) { | ||
$consumer($this->value); | ||
} | ||
} | ||
|
||
public function isPresent(): bool | ||
{ | ||
return $this->wasPresent = $this->value !== null; | ||
} | ||
|
||
/** | ||
* @param T $other | ||
* | ||
* @return T | ||
*/ | ||
public function orElse(mixed $other): mixed | ||
{ | ||
return $this->orElseGet(static fn (): mixed => $other); | ||
} | ||
|
||
/** | ||
* @param callable(): T $otherSupplier | ||
* | ||
* @return T | ||
*/ | ||
public function orElseGet(callable $otherSupplier): mixed | ||
{ | ||
if ($this->value !== null) { | ||
return $this->value; | ||
} | ||
$other = $otherSupplier(); | ||
return static::isSupported($other) ? $other : throw new InvalidArgumentException('Other supplier must return supported other.'); | ||
} | ||
|
||
/** | ||
* @template E of Throwable | ||
* | ||
* @param callable(): E $exceptionSupplier | ||
* | ||
* @return T | ||
* | ||
* @throws E | ||
*/ | ||
public function orElseThrow(callable $exceptionSupplier): mixed | ||
{ | ||
return $this->orElseGet(static function () use ($exceptionSupplier): never { | ||
/** @var Throwable|mixed $exception */ | ||
$exception = $exceptionSupplier(); | ||
if ($exception instanceof Throwable) { | ||
throw $exception; | ||
} | ||
throw new InvalidArgumentException('Exception supplier must return ' . Throwable::class . '.'); | ||
}); | ||
} | ||
|
||
/** | ||
* @param T|mixed $value not null | ||
*/ | ||
abstract protected static function isSupported(mixed $value): bool; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PetrKnap\Optional; | ||
|
||
/** | ||
* @template T of object | ||
* | ||
* @template-extends AbstractOptional<T> | ||
*/ | ||
abstract class AbstractOptionalObject extends AbstractOptional | ||
{ | ||
protected static function isSupported(mixed $value): bool | ||
{ | ||
$expectedObjectClassName = static::getObjectClassName(); | ||
return $value instanceof $expectedObjectClassName; | ||
} | ||
|
||
/** | ||
* @return class-string | ||
*/ | ||
abstract protected static function getObjectClassName(): string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PetrKnap\Optional; | ||
|
||
/** | ||
* @template-extends AbstractOptional<resource> | ||
*/ | ||
abstract class AbstractOptionalResource extends AbstractOptional | ||
{ | ||
protected static function isSupported(mixed $value): bool | ||
{ | ||
$expectedResourceType = static::getResourceType(); | ||
return is_resource($value) && get_resource_type($value) === $expectedResourceType; | ||
} | ||
|
||
/** | ||
* @see get_resource_type() | ||
* | ||
* @return non-empty-string | ||
*/ | ||
abstract protected static function getResourceType(): string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PetrKnap\Optional; | ||
|
||
/** | ||
* @template K of array-key | ||
* @template V of mixed | ||
* | ||
* @template-extends AbstractOptional<array<K, V>> | ||
*/ | ||
final class OptionalArray extends AbstractOptional | ||
{ | ||
protected static function isSupported(mixed $value): bool | ||
{ | ||
return is_array($value); | ||
} | ||
} |
Oops, something went wrong.