charm/options

A generic class for storing options while enforcing option types.

1.1.5 2024-06-25 09:26 UTC

This package is auto-updated.

Last update: 2024-10-25 10:21:58 UTC


README

A better way to allow configuration of your library via an options class.

TLDR

Document your library's options like this. If you're targetting PHP 8.1, you should declare the options 'readonly'.

<?php
namespace Some\Namespace;

class MyOptions extends \Charm\AbstractOptions {

    const CACHE_OFF = 0;
    const CACHE_AUTO = 1;
    const CACHE_ON = 2;

    /**
     * Example option which is required.
     */
    public string $apiKey;

    /**
     * Example option which is not required.
     */
    public string $apiSecret = "";

    /**
     * Example option which must be among the MyOptions::CACHE_* constants.
     */
    public int $cache = self::CACHE_AUTO;

    /**
     * An integer value is required.
     */
    public int $ttl = 86400;
}

Constructing via new keyword

<?php
$options = new Some\Namespace\MyOptions([
    'apiKey' => "some-value",
    'apiSecret' => "some-secret",
    'ttl' => 'short',
]);

Constructing via create() method

<?php
$options = YourOptions::create([
    'apiKey' => "some-value",
    'apiSecret' => "some-secret",
    'ttl' => 'short',
]);

Mutability

Options are mutable if they are declared public. They are read-only if they are declare protected or private.

In PHP 8.1 you can use the readonly keyword on public properties to achieve the same with a very small performance improvement.

Declare an options class

<?php
class HttpClientOptions extends Charm\AbstractOptions {
    /**
     * Most permissive option. It is not required, and any scalar value is accepted.
     */
    public $anything_goes;

    
    /**
     * Class constants with a prefix which is an uppercase of a property name
     * is validated and allows only values among those constants.
     */
    const CACHE_OFF = 0;
    const CACHE_AUTO = 1;
    const CACHE_ON = 2;

    /**
     * Must be set to one of the above constants
     */
    public int $cache;
    


    /**
     * An strictly typed option which is required since it does not have a default.
     */
    public string $base_url;

    /**
     * Union types are OK if you are targetting PHP 8
     */
    public int|bool $either_int_or_boolean;

    /**
     * Allow nested options by declaring those as well:
     */
    public HttpClientCookieOptions $cookie_options;

    /**
     * Declare this function if you wish to automatically set some options or
     * perform custom validation
     */
    protected function finalize() {
        assert(str_starts_with($this->base_url, 'http', 'URL must start with "http"');
    }
}

Setting Options

Options are set in the constructor.

<?php
$httpClient = new HttpClient(new HttpClientOptions([
    'anything_goes_not_required' => "Hello World",
    'base_url' => 123,                                  // triggers a validation error (not string)
    'either_int_or_boolean' => false,
    'cookie_options' => [
        // configuration options for a HttpClientCookieOptions
    ],
]));

Reading Options
---------------

Options can be accessed directly as properties.

<?php echo $options->cookie_options->cookie_jar_file;


Alternatives for options that are private:

<?php // Get a stdClass options map $accessibleOptions = $options->asObject(); // you'll receive a new stdClass with all the options

// Read via ArrayAccess interface $theOption = $options['a_private_option']; ?>

Mutating Options

It is okay to create a new instance of the options object if you wish to "inherit" from another instance.

<?php
$heir = new HttpClientOptions(["some_overridden_options" => 123] + $ancestor);

Using an option class

Example provided for completeness.

<?php
class HttpClient {

    protected HttpClientOptions $options;

    public function __construct(HttpClientOptions $options) {
        $this->options = $options;
    }

}