Options as object

In version 3.7, Polylang transitions from using a simple array to store options in PHP to using an array-like object structure. While this change is transparent to end users, developers working with Polylang or building third-party integrations should be aware of this architectural update.

Object Interface

For backward compatibility, the object implements the interfaces ArrayAccess and IteratorAggregate.

ArrayAccess

ArrayAccess allows to access the data (almost) like we would do with an array:

// Make sure an option exists.
if ( isset( PLL()->options['redirect_lang'] ) ) {
    // Get a value.
    $old_redirect_lang = PLL()->options['redirect_lang'];

    // Reset a value.
    unset( PLL()->options['redirect_lang'] );

    // Set a value.
    PLL()->options['redirect_lang'] = true;
}

Options cannot be unset. Instead they are reset to their default value when unset() is used.

IteratorAggregate

IteratorAggregate allows to iterate the options like we would do with an array:

foreach ( PLL()->options as $key => $value ) {
    printf(
        "\n%s => %s",
        esc_html( $key ),
        esc_html( print_r( $value, true ) )
    );
}

Benefits

Automatic validation and sanitization

As soon as an option value is set, it is validated and sanitized automatically, so we don’t have to care about it. This has the huge benefit to let us always use valid and strongly typed data.

Automatic save into the database

No need to use update_option(), all options are saved automatically into database on the shutdown hook if they have been modified.

This means get_option() and update_option() shouldn’t be used at all.

Multisite

In a multisite environment, when using switch_to_blog() to change the current site, Polylang will automatically load the options specific to that site, but only if Polylang is activated on it.

New methods

Instead of using the capabilities of ArrayAccess, new methods are available and should be used if backward compatibility is not an issue.

Method Return type Description
has() bool Tells if an option exists.
get() mixed Returns the value of the specified option.
set() WP_Error Assigns a value to the specified option.
reset() mixed Resets an option to its default value. Returns the new value.
get_all() mixed[] Returns all options as an array.
merge() WP_Error Merges a subset of options into the current blog ones.

Note that the set() and merge() methods return a WP_Error object that may, or may not, contain validation/sanitization errors. Some errors prevent the new value from being set, some don’t. For example, passing a wrong type won’t set the value.

For instance, the first example can be written like this:

// Make sure an option exists.
if ( PLL()->options->has( 'redirect_lang' ) ) {
    // Get a value.
    $old_redirect_lang = PLL()->options->get( 'redirect_lang' );

    // Reset a value.
    $def_redirect_lang = PLL()->options->reset( 'redirect_lang' );

    // Set a value.
    $errors = PLL()->options->set( 'redirect_lang', true );
}

Limitations

Array functions

Since the options are now an object, that means array functions cannot be used. For example this will trigger a fatal error:

// Fatal error, `PLL()->options` is not an array.
PLL()->options = array_merge(
    PLL()->options,
    array( 'redirect_lang' => true )
)

To make up for this, a merge() method is available:

$error = PLL()->options->merge(
    array( 'redirect_lang' => true )
)

ArrayAccess

The ArrayAccess interface has a limitation: sub-arrays don’t trigger the offsetSet() method, so the validation/sanitization process isn’t fired. Consider this:

PLL()->options['domains']['en'] = 'https://example.com';

This won’t work. So we made sure that the value is not modified if this happens (not to store unsafe data), and instead this can be done with:

$domains = PLL()->options['domains'];
$domains['en'] = 'https://example.com';
PLL()->options['domains'] = $domains;

Custom option

If you have previously added custom keys to the Polylang options array, they will be removed immediately as they are not supported.

 

Picture illustrating the article by Lorenzo Cafaro and licensed under the Pixabay license.