PHPackages                             ekiwok/option - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. ekiwok/option

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

ekiwok/option
=============

PHP Option implementations for all scalars. Possible to register custom Option.

1.0.0(7y ago)21.0k↓100%MITPHP

Since Aug 26Pushed 7y agoCompare

[ Source](https://github.com/ekiwok/PHPOption)[ Packagist](https://packagist.org/packages/ekiwok/option)[ RSS](/packages/ekiwok-option/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (1)Versions (11)Used By (0)

PHP Option [![Build Status](https://camo.githubusercontent.com/5f6b079f5cbd8560517aa943672646205b123b824a5921e6f576678d43662e05/68747470733a2f2f7472617669732d63692e6f72672f656b69776f6b2f5048504f7074696f6e2e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/ekiwok/PHPOption)
==========================================================================================================================================================================================================================================================================================

[](#php-option-)

Option is a value that might or might not be present. In other words it's elegant alternative to throwing an exception or allowing method to return null. It allows for fluent chaining method calls.

Instead of either:

```
     /**
      * @return string|null
      */
     public function get(string $parameter)

     /**
      * @throws ParameterNotFoundException
      */
     public function get(string $parameter): string
```

Just have:

```
    public function get(string $parameter): OptionString
```

Allow null approach:

```
    $uuid = $request->get('id');

    if ($uuid === null) {
       return new NotFoundResponse();
    }

    $product = $this->products->findOneById($uuid);

    if ($product === null) {
       return new NotFoundResponse();
    }

    return new JsonResponse($product);
```

Option approach:

```
    return $request->get('id')
        ->map([$this->products, 'findOneById')
        ->map([JsonResponse::class, 'create'])
        ->orElse(new NotFoundResponse());
```

In contrary to other libraries this one implements separate Option for each scalar type and allows registering custom Options for objects. It's the closest to Java templates we can get and enforces strict type checking.

So if you prefer strict type checking over having a few opcodes less, you can enforce:

```
    return Optional::Some("I love strict types")->orElse(new \stdClass());
    // TypeError: Argument 1 passed to class@anonymous::orElse() must be of the type string, object given, called in ...
```

`Optional::Some` wraps each value to correct Option class:

```
   $maybeString   = Optional::Some("test");                          // OptionString
   $maybeInt      = Optional::Some(43);                              // OptionInteger
   $maybeDouble   = Optional::Some(0.0);                             // OptionDobule
   $maybeBool     = Optional::Some(false);                           // OptionBoolean
   $maybeArray    = Optional::Some([]);                              // OpionArray
   $maybeBlogPost = Optional::Some($blogPosts->findOneById($uuid));  // Optional

   OptionArray::of(null) instanceof None; // true
   OptionArray::of([])   instanceof Some; // true
```

Installation
------------

[](#installation)

With composer: `composer require ekiwok/option`

Interface
---------

[](#interface)

```
interface Option
{
    public function equals(Option $another): bool;

    public function isPresent(): bool;

    public function map(callable $mapper, string $typeToWrap = null): Option;

    public function get(): T;

    public function orElse(T $value): T;

    public function orElseGet(callable $supplier): T;

    public function orElseThrow(callable $supplier): T;

    static public function of(T $value): Option

    static public function Some($value): Some

    static public function None(): None
}

```

All scalar options enforce that methods `get`, `orElse`, `orElseGet`, `orElseThrow` returns the same scalar.

So it's not possible to `orElse` float from OptionString:

```
   return OptionString::of("test")
       ->orElse(34.5);
   // Fatal error: Uncaught TypeError: Argument 1 passed to class@anonymous::orElse() must be of the type string, float given
```

The only exception is `Optional` which does not enforce types.

```
   return Optional::of("test")
        ->orElse(34.5);
```

**Important thing to notice** is that when you map Option which is None it will return Optional.

```
    $maybeIsPalindrome = OptionString::of(null)
        ->map('isPalindrome');

    $maybeIsPalindrome instanceof OptionBoolean; // false
    $maybeIsPalindrome instanceof Optional;      // true
```

This is because there is no reasonable way to guess what should be the type of the value returned by a $supplier. In Some we are able to wrap accordingly to the type of the returned value.

On top of that you probably do not care about mapping when you're dealing with None because all further mappings will also return None.

But if you really want to ensure that, for example, `orElseGet` $supplier returns value of correct type you might provide expected type as another map parameter:

```
   $maybeIsPalindrome = OptionString::of(null)
       ->map('isPalindrome', 'boolean');

   $maybeIsPalindrome instanceof OptionBoolean; // true
   $maybeIsPalindrome instanceof Optional;      // false

   $maybeIsPalindrome->orElseGet(function () {
      return null;
   });
   // Fatal error: Uncaught TypeError: Return value of class@anonymous::orElseGet() must be of the type boolean
```

### Custom mappings

[](#custom-mappings)

Simply register custom mappings:

```
   Optional::registerMappings([
       Foo::class => OptionFoo::class,
       Bar::class => OptionBar::clsss,
   ]);

   Optional::Some(new Foo()) instanceof OptionFoo; // true
   Optional::Some(new Bar()) instanceof OptionBar; // true
```

### Any

[](#any)

If you don't want to get specific type like, for example, OptionString you can wrap $value into Any

```
    return $products->findOneById($id)
        ->map(function (Product $product) {
            return new Any($product->getPrice());
        })
        ->orElse(3);
    // Not throwing exception because after map result is Optional instead of OptionProduct
```

### Autogenerating options

[](#autogenerating-options)

There is **experimental** `bin/generator.php` for autogenerating custom Option classes.

It accepts full class name (including namespace) as first argument and optionally desired namespace as second argument (if not provided default namespace is class namespace prefixed with autoload).

So to generate OptionDateTime one could do:

`vendor/ekiwok/opion/bin/generator.php \DateTime > ./autogenerated/OptionDateTime.php`

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity18

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity68

Established project with proven stability

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

10

Last Release

2791d ago

Major Versions

0.12.1 → 1.0.02018-09-16

### Community

Maintainers

![](https://www.gravatar.com/avatar/d7c66b78cd1619af58e44c485e96a03bcca66b3b18536734e44dfe87c9ba3b98?d=identicon)[ekiwok](/maintainers/ekiwok)

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ekiwok-option/health.svg)

```
[![Health](https://phpackages.com/badges/ekiwok-option/health.svg)](https://phpackages.com/packages/ekiwok-option)
```

###  Alternatives

[symfony/polyfill-php72

Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions

4.8k674.7M31](/packages/symfony-polyfill-php72)[symfony/polyfill-intl-icu

Symfony polyfill for intl's ICU-related data and classes

2.6k251.4M96](/packages/symfony-polyfill-intl-icu)[nette/php-generator

🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 8.5 features.

2.2k64.2M574](/packages/nette-php-generator)[consolidation/site-process

A thin wrapper around the Symfony Process Component that allows applications to use the Site Alias library to specify the target for a remote call.

5345.3M8](/packages/consolidation-site-process)[sycho/flarum-profile-cover

Adds the ability to add a cover image to a profile.

1836.6k](/packages/sycho-flarum-profile-cover)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
