Skip to content

Commit

Permalink
require php 8.3
Browse files Browse the repository at this point in the history
  • Loading branch information
PabloJoan committed Nov 28, 2023
1 parent f27a295 commit a25342f
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 42 deletions.
39 changes: 19 additions & 20 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
name: PHP Composer

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
push:
branches: ["master"]
pull_request:
branches: ["master"]

permissions:
contents: read
contents: read

jobs:
build:
build:
runs-on: ubuntu-latest

runs-on: ubuntu-latest
steps:
- name: "Checkout"
uses: actions/checkout@v3

steps:
- name: "Checkout"
uses: actions/checkout@v3
- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
tools: composer

- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
tools: composer
- name: Install dependencies
run: composer install --no-interaction

- name: Install dependencies
run: composer install --no-interaction

- name: Run test suite
run: composer test
- name: Run test suite
run: composer test
48 changes: 38 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![GitHub license](https://img.shields.io/github/license/PabloJoan/feature.svg)](https://github.com/PabloJoan/feature/blob/master/LICENSE)

Requires PHP 8.2 and above.
Requires PHP 8.3 and above.

# Installation

Expand Down Expand Up @@ -33,7 +33,7 @@ $featureConfigs = [

$features = new Features($featureConfigs);

$features->isEnabled(featureName: 'foo'); // true
$features->isEnabled(featureName: 'foo'); // true
$features->getEnabledVariant(featureName: 'foo'); // 'variant1'
```

Expand All @@ -50,27 +50,34 @@ A feature can be completely enabled, completely disabled, or something in
between and can comprise a number of related variants.

The two main API entry points are:

```php
$features->isEnabled(featureName: 'my_feature')
```

which returns true when `my_feature` is enabled and, for multi-variant features:

```php
$features->getEnabledVariant(featureName: 'my_feature')
```

which returns the name of the particular variant which should be used.

The single argument to each of these methods is the name of the
feature to test.

A typical use of `$features->isEnabled` for a single-variant feature
would look something like this:

```php
if ($features->isEnabled(featureName: 'my_feature')) {
// do stuff
}
```

For a multi-variant feature, we can determine the appropriate code to run for
each variant with something like this:

```php
switch ($features->getEnabledVariant(featureName: 'my_feature')) {
case 'foo':
Expand All @@ -81,8 +88,10 @@ each variant with something like this:
break;
}
```

If a feature is bucketed by id, then we pass the id string to
`$features->isEnabled` and `$features->getEnabledVariant` as a second parameter

```php
$isMyFeatureEnabled = $features->isEnabled(
featureName: 'my_feature',
Expand All @@ -95,30 +104,38 @@ If a feature is bucketed by id, then we pass the id string to
);
```


## Configuration cookbook

There are a number of common configurations so before I explain the complete
syntax of the feature configuration stanzas, here are some of the more common
cases along with the most concise way to write the configuration.

### A totally enabled feature:

```php
$server_config['foo'] = ['variants' => ['enabled' => 100]];
```

### A totally disabled feature:

```php
$server_config['foo'] = ['variants' => ['enabled' => 0]];
```

### Feature with winning variant turned on for everyone

```php
$server_config['foo'] = ['variants' => ['blue_background' => 100]];
```

### Single-variant feature ramped up to 1% of users.

```php
$server_config['foo'] = ['variants' => ['enabled' => 1]];
```

### Multi-variant feature ramped up to 1% of users for each variant.

```php
$server_config['foo'] = [
'variants' => [
Expand All @@ -128,31 +145,41 @@ cases along with the most concise way to write the configuration.
],
];
```

### Enabled for 10% of regular users.

```php
$server_config['foo'] = [
'variants' => ['enabled' => 10]
];
```

### Feature ramped up to 1% of requests, bucketing at random rather than by id

```php
$server_config['foo'] = [
'variants' => ['enabled' => 1],
'bucketing' => 'random'
];
```

### Feature ramped up to 40% of requests, bucketing by id rather than at random

```php
$server_config['foo'] = [
'variants' => ['enabled' => 40],
'bucketing' => 'id'
];
```

### Single-variant feature in 50/50 A/B test

```php
$server_config['foo'] = ['variants' => ['enabled' => 50]];
```

### Multi-variant feature in A/B test with 20% of users seeing each variant (and 40% left in control group).

```php
$server_config['foo'] = [
'variants' => [
Expand All @@ -162,6 +189,7 @@ cases along with the most concise way to write the configuration.
],
];
```

## Configuration details

Each feature’s config stanza controls when the feature is enabled and what
Expand All @@ -173,7 +201,7 @@ keys, the most important of which is `'variants'`.
The value of the `'variants'` property an array whose keys are names of variants
and whose values are the percentage of requests that should see each variant.

The remaining feature config property is `'bucketing'`. Bucketing specifies
The remaining feature config property is `'bucketing'`. Bucketing specifies
how users are bucketed when a feature is enabled for only a percentage of users.
The default value, `'random'`, causes each request to be bucketed independently,
meaning that the same user will be in different buckets on different requests.
Expand All @@ -189,12 +217,12 @@ There are a few ways to misuse the Feature API or misconfigure a feature that
may be detected. (Some of these are not currently detected but may be in the
future.)

1. Setting the percentage value of a variant in `'variants'` to a value less
than 0 or greater than 100.
1. Setting the percentage value of a variant in `'variants'` to a value less
than 0 or greater than 100.

2. Setting `'variants'` such that the sum of the variant percentages is
greater than 100.
2. Setting `'variants'` such that the sum of the variant percentages is
greater than 100.

3. Setting `'variants'` to a non-array value.
3. Setting `'variants'` to a non-array value.

4. Setting `'bucketing'` to any value that is not `'id'` or `'random'`.
4. Setting `'bucketing'` to any value that is not `'id'` or `'random'`.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
"toggle"
],
"require": {
"php": ">=8.2"
"php": ">=8.3"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"scripts": {
"test": "php ./vendor/bin/phpunit --stop-on-failure --fail-on-warning --fail-on-risky -v tests/"
"test": "php ./vendor/bin/phpunit --stop-on-failure --fail-on-warning --fail-on-risky tests/"
},
"autoload": {
"psr-4": {
Expand Down
10 changes: 5 additions & 5 deletions src/Bucketing/Id.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@
* hexdec('ffffffff') is the largest possible outcome
* of hash('crc32c', $idToHash)
*/
private const TOTAL = 4294967295;
private const HASH_ALGO = 'crc32c';
private const int TOTAL = 4294967295;
private const string HASH_ALGO = 'crc32c';

/**
* Convert Id string to a Hex
* Convert Hex to Dec int
* Get a percentage float
* Get a percentage int
*/
public function strToIntHash(string $idToHash = ''): float
public function strToIntHash(string $idToHash): int
{
$hex = hash(self::HASH_ALGO, $idToHash);
$dec = hexdec($hex);

$x = $dec / self::TOTAL;
return $x * 100;
return (int) round($x * 100);
}
}
5 changes: 2 additions & 3 deletions src/Bucketing/Random.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ public function __construct()
$this->randomizer = new Randomizer(new Xoshiro256StarStar());
}

public function strToIntHash(string $idToHash = ''): float
public function strToIntHash(string $idToHash): int
{
$decimal = $this->randomizer->getInt(0, PHP_INT_MAX) / PHP_INT_MAX;
return $decimal * 100;
return $this->randomizer->getInt(0, 100);
}
}
4 changes: 2 additions & 2 deletions src/Bucketing/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
interface Type
{
/**
* A hash number between 0 and 100 based on an id string
* A hash that maps the given string to a number between 0 and 100
* unless we are bucketing completely at random
*/
public function strToIntHash(string $idToHash = ''): float;
public function strToIntHash(string $idToHash): int;
}

0 comments on commit a25342f

Please sign in to comment.