Skip to main content
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:
1

Start with letter or underscore

variable('username')    // Valid
variable('_private')    // Valid
variable('2fast')       // Invalid
2

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
3

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:
demo/hello-world.php
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:
demo/run-if.php
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

1

Use consistent naming

Follow a naming convention throughout your workflow:
// camelCase (recommended)
variable('userName')
variable('maxRetries')

// snake_case
variable('user_name')
variable('max_retries')
2

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')),
);
3

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);
4

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
}

Build docs developers (and LLMs) love