Skip to content

Strategies

Strategies add conditional logic to suspensions. Instead of a simple on/off suspension, strategies evaluate conditions at check time to determine if the suspension is currently active.

StrategyDescription
SimpleStrategyAlways active (default)
TimeWindowStrategyActive during specific hours/days
IpAddressStrategyActive based on IP address
CountryStrategyActive based on geographic location
DeviceFingerprintStrategyActive based on device fingerprint
ConditionalStrategyCustom callback-based logic

Restrict suspension to specific hours and days:

use Cline\Suspend\Facades\Suspend;
// Active only during business hours
Suspend::for($user)
->using('time_window', [
'start' => '09:00',
'end' => '17:00',
'days' => [1, 2, 3, 4, 5], // Mon-Fri (1=Mon, 7=Sun)
'timezone' => 'America/New_York',
])
->suspend('Restricted during business hours');
// Active only on weekends
Suspend::for($user)
->using('time_window', [
'start' => '00:00',
'end' => '23:59',
'days' => [6, 7], // Sat-Sun
'timezone' => 'UTC',
])
->suspend('Weekend restriction');
OptionTypeDescription
startstringStart time in HH:MM format
endstringEnd time in HH:MM format
daysarrayDays of week (1=Monday through 7=Sunday)
timezonestringTimezone for evaluation

Restrict suspension based on IP address:

// Only suspended when outside allowed IPs
Suspend::for($user)
->using('ip_address', [
'allowed_ips' => [
'192.168.1.0/24',
'10.0.0.0/8',
],
])
->suspend('Office network only');
// Suspended when matching blocked IPs
Suspend::for($user)
->using('ip_address', [
'blocked_ips' => [
'1.2.3.4',
'5.6.7.0/24',
],
])
->suspend('Blocked IP range');
OptionTypeDescription
allowed_ipsarrayOnly suspended when NOT matching these IPs
blocked_ipsarrayOnly suspended when matching these IPs

Supports CIDR notation for both IPv4 and IPv6.

Restrict suspension based on geographic location:

// Only suspended in specific countries
Suspend::for($user)
->using('country', [
'blocked_countries' => ['XX', 'YY', 'ZZ'],
])
->suspend('Service not available in your region');
// Suspended everywhere except allowed countries
Suspend::for($user)
->using('country', [
'allowed_countries' => ['US', 'CA', 'GB'],
])
->suspend('Available only in select countries');
OptionTypeDescription
blocked_countriesarraySuspended only in these countries
allowed_countriesarraySuspended everywhere except these countries

Countries use ISO 3166-1 alpha-2 codes.

Restrict suspension based on device fingerprinting:

// Only suspended on specific devices
Suspend::for($user)
->using('device_fingerprint', [
'blocked_fingerprints' => [
'abc123def456',
'xyz789ghi012',
],
])
->suspend('Blocked device');
// Suspended except on trusted devices
Suspend::for($user)
->using('device_fingerprint', [
'allowed_fingerprints' => ['trusted-device-hash'],
])
->suspend('Untrusted device');
OptionTypeDescription
blocked_fingerprintsarraySuspended only on these devices
allowed_fingerprintsarraySuspended except on these devices

Create custom conditions with callbacks:

Suspend::for($user)
->using('conditional', [
'callback' => function ($context) {
// Custom logic here
return $context['subscription_expired'] ?? false;
},
])
->suspend('Subscription required');

You can’t directly combine multiple strategies on a single suspension, but you can create multiple suspensions with different strategies:

// Time-based restriction
Suspend::for($user)
->using('time_window', [
'start' => '09:00',
'end' => '17:00',
'timezone' => 'America/New_York',
])
->suspend('Work hours restriction');
// Geographic restriction
Suspend::for($user)
->using('country', [
'blocked_countries' => ['XX'],
])
->suspend('Geographic restriction');
// User is suspended if ANY suspension is active

Create your own strategy for complex business logic:

use Cline\Suspend\Contracts\Strategy;
class SubscriptionStrategy implements Strategy
{
public function type(): string
{
return 'subscription';
}
public function isActive(array $metadata, array $context): bool
{
$requiredPlan = $metadata['required_plan'] ?? 'premium';
$userPlan = $context['plan'] ?? 'free';
$plans = ['free' => 0, 'basic' => 1, 'premium' => 2, 'enterprise' => 3];
return ($plans[$userPlan] ?? 0) < ($plans[$requiredPlan] ?? 0);
}
public function validate(array $metadata): bool
{
return isset($metadata['required_plan']);
}
}

Register your strategy:

// In a service provider
use Cline\Suspend\Facades\Suspend;
public function boot()
{
Suspend::registerStrategy(new SubscriptionStrategy());
}

Use it:

Suspend::for($user)
->using('subscription', [
'required_plan' => 'premium',
])
->suspend('Premium feature');
// Check with context
$isSuspended = Suspend::for($user)
->withContext(['plan' => auth()->user()->plan])
->isSuspended();

Strategies receive context when checking suspensions:

$isSuspended = Suspend::for($user)
->withContext([
'ip' => $request->ip(),
'country' => $geoResolver->resolve($request->ip()),
'fingerprint' => $request->header('X-Device-Fingerprint'),
'plan' => $user->subscription?->plan,
])
->isSuspended();

The middleware automatically provides IP and country context.

Strategy metadata is stored with the suspension and evaluated at check time:

$suspension = Suspend::for($user)
->using('time_window', [
'start' => '09:00',
'end' => '17:00',
'timezone' => 'UTC',
])
->suspend('Test');
// Metadata is stored in the suspension
$suspension->strategy_type; // 'time_window'
$suspension->strategy_metadata; // ['start' => '09:00', ...]

Strategies validate their metadata before suspension creation:

// This will fail - missing required metadata
Suspend::for($user)
->using('time_window', [])
->suspend('Invalid');
// MissingStrategyMetadataException: Strategy 'time_window' requires 'start' metadata