Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.x] Introduce before hook #111

Merged
merged 2 commits into from
Jul 8, 2024
Merged

[1.x] Introduce before hook #111

merged 2 commits into from
Jul 8, 2024

Conversation

timacdonald
Copy link
Member

@timacdonald timacdonald commented Jul 8, 2024

replaces #110

This PR introduces a "before" hook to class based features that allows you to intercept feature resolution before hitting the storage driver.

When a non-null value is returned from the before hook, it is used in place of retrieving the value from storage. Values returned from the before hook are never persisted to storage and are only ever used in-memory.

This allows you to:

  1. Start resolving / storing a feature after a given date.
  2. Schedule the release of a feature to everyone without changing the database values.
  3. Create a non-destructive temporary "kill switch" for a feature without having the purge the feature in the database. This is good if you want to turn a feature off and back on and maintain the existing resolved values for users in the database.

Example: Schedule the initial feature rollout after a given date

<?php

class MyFeature
{
    public function resolve($scope)
    {
        // After '2024-07-01 00:00:00', the feature will start resolving and
        // storing based on the lottery.
        return Lottery::odds(1, 100);
    }

    public function before($scope)
    {
        // Until '2024-07-01 00:00:00', everyone will not have the feature
        // available...
        if (Carbon::parse('2024-07-01 00:00:00')->isFuture()) {
            return false;
        }
    }
}

Example: Roll the feature out to everyone after a given date.

<?php

class MyFeature
{
    public function resolve($scope)
    {
        // Until '2024-07-01 00:00:00', the feature will be resolved and
        // storing based on the lottery.
        return Lottery::odds(1, 100);
    }

    public function before($scope)
    {
        // After '2024-07-01 00:00:00', everyone will have the feature activated.
        // The previously resolved values will persist in the database, allowing you
        // to revert to the previous set of users and resolved values if something
        // goes wrong after rollout.
        if (Carbon::parse('2024-07-01 00:00:00')->isPast()) {
            return true;
        }
    }
}

Example: provide a non-destructive killswitch

<?php

class MyFeature
{
    public function resolve($scope)
    {
        return Arr::random([
            'blue-sapphire',
            'seafoam-green',
            'tart-orange',
        ]);
    }

    public function before($scope)
    {
        // Changing the config value via an ENV flag allows you to quickly
        // disable a feature without changing the values stored in the
        // database.  When the feature is restored, the previously resolved
        // values will persist.
        if (Config::get('features.my-feature.disabled') === true) {
            return false;
        }
    }
}

Example: composing different usecases

<?php

class MyFeature
{
    public function resolve($scope)
    {
        return Arr::random([
            'blue-sapphire',
            'seafoam-green',
            'tart-orange',
        ]);
    }

    public function before($scope)
    {
        // Global kill switch...
        if (Config::get('features.my-feature.disabled') === true) {
            return false;
        }

         // Rollout to everyone after the given date...
         if (Carbon::parse('2024-07-01 00:00:00')->isPast()) {
            return true;
        }
    }
}

$features = Collection::make($features)
$resolved = Collection::make($features)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed a potential bug with the reassignment of this argument. Renaming to avoid issues.

phpunit.xml.dist Outdated
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migrated to the latest config here. running the testsuite was yelling at me that the config was out of date.

@alexanderkroneis
Copy link

This feels like a better and more laravely solution than #110. I love it.

@taylorotwell taylorotwell merged commit e031b6f into laravel:1.x Jul 8, 2024
6 checks passed
@timacdonald timacdonald deleted the before branch July 8, 2024 22:44
@alexanderkroneis
Copy link

Just a quick hint: The link of this repository does not point to the current docs (it's linking to 10.x).

@timacdonald
Copy link
Member Author

Updated that. Thanks for the heads up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants