Skip to content
Merged
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
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,65 @@ class LaravelCacheStore implements StoreInterface
}
```

## Framework Integration

### Custom Resolver

For framework adapters (like Laravel, Symfony, etc.), you can override how `Cloak::make()` resolves instances using `resolveUsing()`:

```php
use DynamikDev\Cloak\Cloak;

// Set a custom resolver (typically in a service provider)
Cloak::resolveUsing(fn() => app(Cloak::class));

// Now Cloak::make() resolves from your container
$cloak = Cloak::make(); // Uses your container binding
```

This allows framework packages to:
- Integrate with dependency injection containers
- Use framework-specific storage drivers
- Apply framework configuration automatically
- Let developers extend and customize via container bindings

**Example Laravel Service Provider:**

```php
use DynamikDev\Cloak\Cloak;
use Illuminate\Support\ServiceProvider;

class CloakServiceProvider extends ServiceProvider
{
public function register()
{
// Bind Cloak to the container with your configuration
$this->app->bind(Cloak::class, function ($app) {
return Cloak::using($app->make(CacheStore::class))
->withDetectors(config('cloak.detectors', Detector::all()));
});

// Make helpers use container resolution
Cloak::resolveUsing(fn() => app(Cloak::class));
}
}
```

Now developers can customize behavior through container bindings:

```php
// In AppServiceProvider
$this->app->bind(Cloak::class, function ($app) {
return CustomCloak::using($app->make(CacheStore::class));
});
```

**Clearing the Resolver:**

```php
Cloak::clearResolver(); // Reverts to default behavior
```

## Extending Cloak

Cloak follows a compositional architecture, making it easy to extend with custom implementations.
Expand Down
26 changes: 26 additions & 0 deletions src/Cloak.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class Cloak
use HasLifecycleCallbacks;
use ManagesStorage;

/** @var (callable(): self)|null */
protected static $resolver = null;

/** @var array<int, DetectorInterface>|null */
protected ?array $defaultDetectors = null;

Expand All @@ -42,9 +45,32 @@ public static function using(StoreInterface $store): self

public static function make(?StoreInterface $store = null): self
{
if (self::$resolver !== null) {
return (self::$resolver)();
}

return new self($store ?? self::getDefaultStore());
}

/**
* Set a custom resolver for creating Cloak instances.
* This allows framework adapters to override the default factory behavior.
*
* @param callable(): self $resolver
*/
public static function resolveUsing(callable $resolver): void
{
self::$resolver = $resolver;
}

/**
* Clear the custom resolver, reverting to default factory behavior.
*/
public static function clearResolver(): void
{
self::$resolver = null;
}

/**
* Set the default detectors to use when none are specified.
*
Expand Down
48 changes: 48 additions & 0 deletions tests/CloakTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,51 @@

expect($result)->toMatch('/Card: \{\{CREDIT_CARD_[a-zA-Z0-9]{6}_1\}\}/');
});

it('uses custom resolver when set', function () {
$customStore = new ArrayStore();
$customCloak = Cloak::using($customStore);

Cloak::resolveUsing(fn() => $customCloak);

$resolved = Cloak::make();

expect($resolved)->toBe($customCloak);

Cloak::clearResolver();
});

it('resolver is called on every make() call', function () {
$callCount = 0;

Cloak::resolveUsing(function () use (&$callCount) {
$callCount++;
return Cloak::using(new ArrayStore());
});

Cloak::make();
Cloak::make();
Cloak::make();

expect($callCount)->toBe(3);

Cloak::clearResolver();
});

it('clearResolver reverts to default behavior', function () {
$customStore = new ArrayStore();
Cloak::resolveUsing(fn() => Cloak::using($customStore));

Cloak::clearResolver();

$resolved = Cloak::make();

expect($resolved)->toBeInstanceOf(Cloak::class);
expect($resolved)->not->toBe(Cloak::using($customStore));
});

it('make uses default store when no resolver set', function () {
$cloak = Cloak::make();

expect($cloak)->toBeInstanceOf(Cloak::class);
});