Variables are named placeholders that allow workflows to accept dynamic input at runtime. They enable reusable workflow definitions that can process different data on each execution.
Creating variables
Use the variable() helper function to create workflow variables:
use function Chevere\Workflow\variable;
$username = variable('username');
$email = variable('email');
$isAdmin = variable('isAdmin');
Variable interface
Variables implement the VariableInterface from src/Interfaces/VariableInterface.php:
interface VariableInterface extends Stringable
{
}
The implementation in src/Variable.php:21-42 enforces valid naming:
final class Variable implements VariableInterface
{
public function __construct(
private string $name
) {
$matches = (new Regex('/^[a-zA-Z_]\w+$/'))
->match($name);
if ($matches === []) {
throw new InvalidArgumentException(
(string) message(
'Invalid variable name `%name%`',
name: $name
)
);
}
}
public function __toString(): string
{
return $this->name;
}
}
Naming rules
Variable names must follow PHP variable naming conventions:
Start with letter or underscore
variable('username') // Valid
variable('_private') // Valid
variable('2fast') // Invalid
Contain only word characters
Letters, numbers, and underscores after the first character:variable('user_name') // Valid
variable('user123') // Valid
variable('user-name') // Invalid
variable('user.name') // Invalid
Use descriptive names
Choose names that clearly indicate the variable’s purpose:variable('userEmail') // Good
variable('maxRetryAttempts') // Good
variable('x') // Avoid
variable('temp') // Avoid
Using variables in jobs
Variables are passed as job arguments and resolved 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'),
),
);
$run = run(
$workflow,
username: $argv[1] ?? 'World'
);
Type inference
Variable types are inferred from the job parameter they’re assigned to. From src/Jobs.php:183-224, the system validates variable compatibility:
private function handleArguments(string $name, JobInterface $job): void
{
foreach ($job->arguments() as $argument => $value) {
$argument = strval($argument);
$parameters = $job->parameters();
$parameter = null;
if ($parameters->has($argument)) {
$parameter = $parameters->get($argument);
}
// ... parameter resolution logic
$collection = match (true) {
$value instanceof VariableInterface => 'variables',
$value instanceof ResponseReferenceInterface => 'references',
default => false
};
if (! $collection || $parameter === null) {
continue;
}
try {
$this->mapParameter($argument, $collection, $parameter, $value);
} catch (Throwable $e) {
throw new JobsException(name: $name, job: $job, throwable: $e);
}
}
}
Type compatibility
When a variable is used in multiple jobs, types must be compatible:
workflow(
process: async(
new ProcessUser(),
userId: variable('id') // Parameter expects int
),
notify: async(
new NotifyUser(),
userId: variable('id') // Parameter expects int - Compatible!
)
)
Using the same variable with incompatible types will throw a TypeError at workflow construction.
Incompatible example:
workflow(
process: async(
new ProcessData(),
data: variable('input') // Expects string
),
count: async(
new CountItems(),
items: variable('input') // Expects array - Type error!
)
)
Conditional variables
Variables used in withRunIf() or withRunIfNot() must be boolean:
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') // Must be bool
),
);
$run = run(
$workflow,
username: $name,
sayHello: $name !== '' // bool value
);
From src/Jobs.php:430-455:
private function handleRunIfVariable(string $name, mixed $runIf): void
{
if (! $runIf instanceof VariableInterface) {
return;
}
if ($this->variables->has($runIf->__toString())) {
$parameter = $this->variables->get($runIf->__toString());
if (! ($parameter instanceof BoolParameterInterface)) {
throw new TypeError(
(string) message(
'Variable **%variable%** (previously declared as `%type%`) is not of type `bool` at Job **%job%**',
variable: $runIf->__toString(),
type: $parameter->type()->primitive(),
job: $name,
)
);
}
} else {
$this->variables = $this->variables
->withPut(
$runIf->__toString(),
bool(),
);
}
}
Providing variable values
Supply variable values when running the workflow:
// Single variable
$run = run($workflow, username: 'Alice');
// Multiple variables
$run = run(
$workflow,
username: 'Alice',
email: '[email protected]',
isAdmin: true
);
// From array
$values = [
'username' => 'Alice',
'email' => '[email protected]',
];
$run = run($workflow, ...$values);
Workflow parameters
Variables become workflow parameters, accessible via $workflow->parameters():
$workflow = workflow(
greet: sync(new Greet(), username: variable('username')),
);
// Access parameter definitions
$parameters = $workflow->parameters();
// ParametersInterface with username parameter
From src/Workflow.php:115-128:
private function putVariable(
VariableInterface $variable,
ParameterInterface $parameter
): void {
if ($this->parameters->has($variable->__toString())) {
return;
}
$this->parameters = $this->parameters
->withRequired(
$variable->__toString(),
$parameter,
);
}
Best practices
Use consistent naming
Follow a naming convention throughout your workflow:// camelCase (recommended)
variable('userName')
variable('maxRetries')
// snake_case
variable('user_name')
variable('max_retries')
Document expected types
Add comments or documentation about variable types:/**
* @param string $username User's display name
* @param int $userId Numeric user identifier
* @param bool $sendEmail Whether to send notification
*/
$workflow = workflow(
process: async(
new ProcessUser(),
name: variable('username'),
id: variable('userId'),
)->withRunIf(variable('sendEmail')),
);
Validate at workflow edges
Validate input before passing to workflow:$input = $_POST['username'] ?? '';
if (empty($input) || strlen($input) > 100) {
throw new InvalidArgumentException('Invalid username');
}
$run = run($workflow, username: $input);
Reuse variables logically
Share variables across jobs when it makes semantic sense:workflow(
// Same userId used in related operations
fetch: async(new FetchUser(), userId: variable('userId')),
notify: async(new NotifyUser(), userId: variable('userId')),
log: async(new LogActivity(), userId: variable('userId')),
)
Variable tracking
The workflow tracks all variables across jobs from src/Workflow.php:154-167:
private function putVariableReference(
mixed $value,
ParameterInterface $parameter
): void {
$isVariable = $value instanceof VariableInterface;
$isResponse = $value instanceof ResponseReferenceInterface;
if (! ($isVariable || $isResponse)) {
return;
}
if ($isVariable) {
$this->putVariable($value, $parameter);
return;
}
// ... response handling
}