Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
{
"name": "laravel/pennant",
"name": "qrstuff/pennant",
"description": "A simple, lightweight library for managing feature flags.",
"keywords": ["laravel", "pennant", "feature", "flags"],
"homepage": "https://github.com/laravel/pennant",
"homepage": "https://bitbucket.org/qrcg/pennant",
"license": "MIT",
"support": {
"issues": "https://github.com/laravel/pennant/issues",
"source": "https://github.com/laravel/pennant"
},
"authors": [
{
"name": "QRStuff Team",
"email": "engineering@qrstuff.com"
},
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.1",
"illuminate/console": "^10.0|^11.0|^12.0",
"illuminate/container": "^10.0|^11.0|^12.0",
"illuminate/contracts": "^10.0|^11.0|^12.0",
"illuminate/database": "^10.0|^11.0|^12.0",
"illuminate/queue": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0",
"php": "^8.0",
"illuminate/console": "^9.0|^10.0|^11.0|^12.0",
"illuminate/container": "^9.0|^10.0|^11.0|^12.0",
"illuminate/contracts": "^9.0|^10.0|^11.0|^12.0",
"illuminate/database": "^9.0|^10.0|^11.0|^12.0",
"illuminate/queue": "^9.0|^10.0|^11.0|^12.0",
"illuminate/support": "^9.0|^10.0|^11.0|^12.0",
"symfony/console": "^6.0|^7.0",
"symfony/finder": "^6.0|^7.0"
},
"require-dev": {
"laravel/octane": "^1.4|^2.0",
"orchestra/testbench": "^8.36|^9.15|^10.8",
"orchestra/testbench": "^7.0|^8.36|^9.15|^10.8",
"phpstan/phpstan": "^1.10"
},
"autoload": {
Expand Down
54 changes: 44 additions & 10 deletions src/Drivers/DatabaseDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Connection;
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Database\QueryException;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Laravel\Pennant\Contracts\CanListStoredFeatures;
Expand Down Expand Up @@ -101,11 +101,8 @@ public function __construct(DatabaseManager $db, Dispatcher $events, Repository

/**
* Define an initial feature flag state resolver.
*
* @param string $feature
* @param (callable(mixed $scope): mixed) $resolver
*/
public function define($feature, $resolver): void
public function define(string $feature, callable $resolver): void
{
$this->featureStateResolvers[$feature] = $resolver;
}
Expand Down Expand Up @@ -180,7 +177,11 @@ public function getAll($features): array
if ($inserts->isNotEmpty()) { // @phpstan-ignore method.impossibleType
try {
$this->insertMany($inserts->all());
} catch (UniqueConstraintViolationException $e) {
} catch (\Exception $e) {
if (! $this->isUniqueConstraintViolation($e)) {
throw $e;
}

if ($this->retryDepth === 2) {
throw new RuntimeException('Unable to insert feature values into the database.', previous: $e);
}
Expand Down Expand Up @@ -215,7 +216,11 @@ public function get($feature, $scope): mixed

try {
$this->insert($feature, $scope, $value);
} catch (UniqueConstraintViolationException $e) {
} catch (\Exception $e) {
if (! $this->isUniqueConstraintViolation($e)) {
throw $e;
}

if ($this->retryDepth === 1) {
throw new RuntimeException('Unable to insert feature value into the database.', previous: $e);
}
Expand Down Expand Up @@ -386,11 +391,11 @@ public function delete($feature, $scope): void
}

/**
* Purge the given feature from storage.
* Purge the given features from storage.
*
* @param array|null $features
* @param array<int, string>|null $features
*/
public function purge($features): void
public function purge(?array $features): void
{
if ($features === null) {
$this->newQuery()->delete();
Expand Down Expand Up @@ -424,4 +429,33 @@ protected function connection()
$this->config->get("pennant.stores.{$this->name}.connection") ?? null
);
}

/**
* Determine if the given exception is a unique constraint violation.
*
* @param \Exception $e
* @return bool
*/
protected function isUniqueConstraintViolation($e)
{
if (class_exists('Illuminate\Database\UniqueConstraintViolationException') &&
$e instanceof \Illuminate\Database\UniqueConstraintViolationException) {
return true;
}

if (! $e instanceof QueryException) {
return false;
}

$connection = $this->connection();
$driverName = $connection->getDriverName();

return match ($driverName) {
'sqlite' => str_contains($e->getMessage(), 'UNIQUE constraint failed'),
'mysql' => $e->getCode() === '23000' && str_contains($e->getMessage(), '1062 Duplicate entry'),
'pgsql' => $e->getCode() === '23505',
'sqlsrv' => $e->getCode() === '2601' || $e->getCode() === '2627',
default => false,
};
}
}
1 change: 1 addition & 0 deletions src/FeatureManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use InvalidArgumentException;
use Laravel\Pennant\Contracts\Driver;
use Laravel\Pennant\Contracts\FeatureScopeSerializeable;
use Laravel\Pennant\Drivers\ArrayDriver;
use Laravel\Pennant\Drivers\DatabaseDriver;
Expand Down
32 changes: 30 additions & 2 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,40 @@
namespace Tests;

use Laravel\Pennant\FeatureManager;
use Orchestra\Testbench\Concerns\WithWorkbench;
use Orchestra\Testbench\TestCase as OrchestraTestCase;

abstract class TestCase extends OrchestraTestCase
{
use WithWorkbench;
/**
* Get package providers.
*
* @param \Illuminate\Foundation\Application $app
* @return array<int, string>
*/
protected function getPackageProviders($app)
{
return [
\Laravel\Pennant\PennantServiceProvider::class,
];
}

/**
* Define environment setup.
*
* @param \Illuminate\Foundation\Application $app
* @return void
*/
protected function getEnvironmentSetUp($app)
{
$app['config']->set('database.default', 'testing');

if (file_exists(__DIR__.'/../workbench/database/migrations')) {
$app['config']->set('database.migrations', [
realpath(__DIR__.'/../database/migrations'),
realpath(__DIR__.'/../workbench/database/migrations'),
]);
}
}

/**
* Create an instance of the manager.
Expand Down