Chevere Workflow allows you to conditionally execute jobs based on variables, response values, or custom logic. This is useful for creating dynamic workflows that adapt to different scenarios.
Basic conditional execution
Use withRunIf() to execute a job only when a condition is true:
use function Chevere\Workflow\ run ;
use function Chevere\Workflow\ sync ;
use function Chevere\Workflow\ variable ;
use function Chevere\Workflow\ workflow ;
$workflow = workflow (
greet : sync (
new Greet (),
username : variable ( 'username' ),
) -> withRunIf (
variable ( 'sayHello' )
),
);
$run = run (
$workflow ,
username : 'Rodolfo' ,
sayHello : true
);
// Job executed
$greet = $run -> response ( 'greet' ) -> string ();
Conditional types
Jobs support four types of conditions:
Boolean values
The simplest condition is a direct boolean:
$job = async ( new MyAction ())
-> withRunIf ( true ); // Always runs
$job = async ( new MyAction ())
-> withRunIf ( false ); // Never runs
Variables
Check workflow variables at runtime:
use Chevere\Demo\Actions\ Greet ;
use function Chevere\Workflow\ run ;
use function Chevere\Workflow\ sync ;
use function Chevere\Workflow\ variable ;
use function Chevere\Workflow\ workflow ;
$workflow = workflow (
greet : sync (
new Greet (),
username : variable ( 'username' ),
) -> withRunIf (
variable ( 'sayHello' )
),
);
$name = $argv [ 1 ] ?? '' ;
$run = run (
$workflow ,
username : $name ,
sayHello : $name !== ''
);
if ( $run -> skip () -> contains ( 'greet' )) {
echo "Greeting skipped \n " ;
exit ;
}
$greet = $run -> response ( 'greet' ) -> string ();
echo "{ $greet } \n " ;
Response references
Condition on values from previous jobs:
$workflow = workflow (
job1 : async ( new CheckPermissions ()),
job2 : async ( new ProcessData ())
-> withRunIf (
response ( 'job1' , 'hasPermission' )
),
);
You can reference the entire response (must return bool):
$job1 = sync ( function () : bool {
return true ;
});
$job2 = async ( new MyAction ())
-> withRunIf ( response ( 'job1' ));
Callables
Use closures for complex conditional logic:
$workflow = workflow (
job1 : async ( new GetUserData ()),
job2 : async ( new SendEmail ())
-> withRunIf (
fn ( RunInterface $run ) =>
$run -> response ( 'job1' , 'age' ) -> int () >= 18
),
);
Negative conditions
Use withRunIfNot() to execute a job when a condition is false:
$workflow = workflow (
check : async ( new ValidateInput ()),
error : async ( new HandleError ())
-> withRunIfNot (
response ( 'check' , 'isValid' )
),
);
Multiple conditions
You can specify multiple conditions. ALL conditions must be true for the job to execute:
$workflow = workflow (
job1 : async ( new CheckAuth ()),
job2 : async ( new CheckQuota ()),
process : async ( new ProcessRequest ())
-> withRunIf (
response ( 'job1' , 'authenticated' ),
response ( 'job2' , 'hasQuota' ),
variable ( 'enabled' )
),
);
When using multiple conditions with withRunIf(), the job runs only if ALL conditions are true. With withRunIfNot(), the job runs only if ALL conditions are false.
Combining conditions
You can use both withRunIf() and withRunIfNot() on the same job:
$job = async ( new MyAction ())
-> withRunIf ( variable ( 'featureEnabled' ))
-> withRunIfNot ( variable ( 'maintenanceMode' ));
Checking skipped jobs
The run instance tracks which jobs were skipped:
$run = run ( $workflow , enabled : false );
if ( $run -> skip () -> contains ( 'job1' )) {
echo "Job was skipped \n " ;
}
// Get all skipped jobs
$skipped = $run -> skip () -> toArray ();
print_r ( $skipped );
Accessing skipped job responses
Attempting to access a skipped job’s response throws an exception:
use OutOfBoundsException ;
$run = run ( $workflow , enabled : false );
try {
$response = $run -> response ( 'skippedJob' );
} catch ( OutOfBoundsException $e ) {
// Job was skipped, no response available
}
Dependent jobs
If a job depends on a skipped job, it will also be skipped:
foreach ( $job -> dependencies () as $dependency ) {
try {
$new -> run () -> response ( $dependency );
} catch ( OutOfBoundsException ) {
$new -> addJobSkip ( $name );
return $new ;
}
}
Example:
$workflow = workflow (
job1 : async ( new MyAction ())
-> withRunIf ( variable ( 'enabled' )),
job2 : async ( new OtherAction ())
-> withDepends ( 'job1' ),
);
$run = run ( $workflow , enabled : false );
// Both jobs are skipped
assert ( $run -> skip () -> contains ( 'job1' ));
assert ( $run -> skip () -> contains ( 'job2' ));
Implementation details
Conditions are evaluated in the Runner:
foreach ( $job -> runIf () as $runIf ) {
if ( $new -> getRunIfCondition ( $runIf ) === false ) {
$new -> addJobSkip ( $name );
return $new ;
}
}
foreach ( $job -> runIfNot () as $runIfNot ) {
if ( $new -> getRunIfCondition ( $runIfNot ) === true ) {
$new -> addJobSkip ( $name );
return $new ;
}
}
The condition evaluation logic:
private function getRunIfCondition (
VariableInterface | ResponseReferenceInterface | callable | bool $runIf
) : bool {
return match ( true ) {
is_bool ( $runIf ) => $runIf ,
$runIf instanceof VariableInterface =>
$this -> run -> arguments () -> required ( $runIf -> __toString ()) -> bool (),
$runIf instanceof ResponseReferenceInterface => $runIf -> key () !== null
? $this -> run -> response ( $runIf -> job ()) -> array ()[ $runIf -> key ()]
: $this -> run -> response ( $runIf -> job ()) -> bool (),
default => call_user_func ( $runIf , $this -> run ())
};
}
Conditional duplicate protection
You cannot specify the same condition multiple times:
use OverflowException ;
try {
$closure = fn () => true ;
$job = async ( new MyAction ())
-> withRunIf ( $closure , $closure ); // Error!
} catch ( OverflowException $e ) {
// Condition `callable#123` is already defined
}
Use cases
$workflow = workflow (
experimental : async ( new ExperimentalFeature ())
-> withRunIf ( variable ( 'experimentalEnabled' )),
stable : async ( new StableFeature ())
-> withRunIfNot ( variable ( 'experimentalEnabled' )),
);
$workflow = workflow (
validate : async ( new ValidateData ()),
process : async ( new ProcessData ())
-> withRunIf ( response ( 'validate' , 'isValid' )),
error : async ( new LogError ())
-> withRunIfNot ( response ( 'validate' , 'isValid' )),
);
Permission-based execution
$workflow = workflow (
checkAuth : async ( new AuthCheck ()),
adminTask : async ( new AdminAction ())
-> withRunIf ( response ( 'checkAuth' , 'isAdmin' )),
userTask : async ( new UserAction ())
-> withRunIfNot ( response ( 'checkAuth' , 'isAdmin' )),
);
Environment-specific jobs
$workflow = workflow (
debug : async ( new DebugLogger ())
-> withRunIf ( variable ( 'isDevelopment' )),
analytics : async ( new TrackAnalytics ())
-> withRunIfNot ( variable ( 'isDevelopment' )),
);
Best practices
Keep conditions simple
Prefer simple boolean checks over complex callable logic for better readability.
Check skip status before accessing responses
Always verify a job wasn’t skipped before accessing its response to avoid exceptions.
Use meaningful variable names
Name conditional variables clearly: isEnabled, hasPermission, shouldProcess.
Document conditional logic
Add comments explaining why jobs are conditional, especially with complex logic.