KMP PHP API Reference

DefaultWorkflowApprovalManager
in package
implements WorkflowApprovalManagerInterface

Default implementation of WorkflowApprovalManagerInterface.

Manages approval gate lifecycle: creation, transactional response recording, candidate discovery for picker UIs, and resolution detection.

Current-user pending eligibility is owned by WorkflowApprovalsTable so queues, badges, and response recording use one canonical decision path.

Table of Contents

Interfaces

WorkflowApprovalManagerInterface
Interface for managing workflow approval gates and responses.

Constants

MAX_OPTIMISTIC_RETRIES  : mixed = 3
Maximum retry attempts for optimistic lock conflicts.
RETRY_DELAY_US  : mixed = 50000
Base delay in microseconds between optimistic lock retries (50ms).

Properties

$container  : ContainerInterface|null

Methods

__construct()  : mixed
cancelApprovalsForInstance()  : ServiceResult
Cancel all pending approvals for a workflow instance.
createApproval()  : ServiceResult
Create an approval gate for a workflow node.
getApprovalsForInstance()  : array<string|int, WorkflowApproval>
Get all approvals for a workflow instance.
getEligibleApprovers()  : array<string|int, Member>
Get possible member candidates for approval picker/reassignment UIs.
getNextApproverCandidates()  : array<string|int, int>
Get candidate member IDs for the "next approver" picker in serial pick-next mode.
getPendingApprovalsForMember()  : array<string|int, WorkflowApproval>
List the approvals currently pending the given member's action.
isResolved()  : bool
Check if an approval gate has been resolved.
reassignApproval()  : ServiceResult
Reassign a pending approval to a different eligible member.
recordResponse()  : ServiceResult
Record a member's approval decision with optimistic-locking retries.
attemptRecordResponse()  : ServiceResult
Single transactional attempt to record an approval response.
findDynamicApprovers()  : array<string|int, Member>
Find Member entities eligible via the dynamic callback approver.
findEligibleMembers()  : array<string|int, Member>
Find members who are potential candidates for an approval picker.
findMembersByPermission()  : array<string|int, Member>
Find all members who have an active (unexpired) role granting the specified permission.
findMembersByRole()  : array<string|int, Member>
Find all members who have the specified active (unexpired) role.
getResolverBackedPendingDynamicApprovals()  : array<string|int, WorkflowApproval>
Load callback-only dynamic approvals whose eligibility cannot be represented by lookup fields.
getWorkflowInstanceResponderIds()  : array<string|int, int>
hasPriorDynamicWorkflowResponse()  : bool
Dynamic approvers may qualify through multiple routes, but each member can only respond once per workflow.
parseDeadline()  : DateTime
Parse a deadline string (e.g., "7d", "24h") into a DateTime.
resolveDynamicApproverIds()  : array<string|int, int>
Resolve eligible member IDs via configured callback service.
saveWithVersionCheck()  : void
Save an approval entity with optimistic version bump.

Constants

Properties

Methods

__construct()

public __construct([ContainerInterface|null $container = null ]) : mixed
Parameters
$container : ContainerInterface|null = null

Optional DI container for dynamic resolvers

createApproval()

Create an approval gate for a workflow node.

public createApproval(int $instanceId, string $nodeId, int $executionLogId, array<string|int, mixed> $config) : ServiceResult
Parameters
$instanceId : int
$nodeId : string
$executionLogId : int
$config : array<string|int, mixed>
Tags
inheritDoc
Return values
ServiceResult

getEligibleApprovers()

Get possible member candidates for approval picker/reassignment UIs.

public getEligibleApprovers(int $approvalId) : array<string|int, Member>
Parameters
$approvalId : int
Tags
inheritDoc
Return values
array<string|int, Member>

getNextApproverCandidates()

Get candidate member IDs for the "next approver" picker in serial pick-next mode.

public getNextApproverCandidates(int $approvalId[, int|null $currentMemberId = null ]) : array<string|int, int>

Returns the full pool of eligible members with the right permission, minus those who have already responded and the requesting member.

Parameters
$approvalId : int

Workflow approval ID

$currentMemberId : int|null = null

Current user ID to exclude (they're approving now)

Return values
array<string|int, int>

Member IDs eligible to be picked as next approver

getPendingApprovalsForMember()

List the approvals currently pending the given member's action.

public getPendingApprovalsForMember(int $memberId) : array<string|int, WorkflowApproval>

Uses the same canonical eligibility logic as badges, mobile queues, and controller response guards so listed approvals remain actionable.

Parameters
$memberId : int
Return values
array<string|int, WorkflowApproval>

isResolved()

Check if an approval gate has been resolved.

public isResolved(int $approvalId) : bool
Parameters
$approvalId : int
Tags
inheritDoc
Return values
bool

reassignApproval()

Reassign a pending approval to a different eligible member.

public reassignApproval(int $approvalId, int $newApproverId, int $adminMemberId[, string|null $reason = null ]) : ServiceResult
Parameters
$approvalId : int

Workflow approval ID

$newApproverId : int

Member ID of the new approver

$adminMemberId : int

Member ID of the admin performing the reassignment

$reason : string|null = null

Optional reason for reassignment

Return values
ServiceResult

Contains approvalId, instanceId, nodeId, previousApproverId, newApproverId on success

recordResponse()

Record a member's approval decision with optimistic-locking retries.

public recordResponse(int $approvalId, int $memberId, string $decision[, string|null $comment = null ][, int|null $nextApproverId = null ][, array<string|int, mixed> $approverConfigUpdates = [] ]) : ServiceResult

Uses a three-layer concurrency defense:

  1. FOR UPDATE row lock — serialises concurrent transactions
  2. Atomic SQL increment — prevents lost count updates
  3. Optimistic version check — detects any out-of-band modification
Parameters
$approvalId : int

Workflow approval ID

$memberId : int

Responding member ID

$decision : string

'approve' or 'reject'

$comment : string|null = null

Optional comment

$nextApproverId : int|null = null

Optional next approver for serial pick-next chains

$approverConfigUpdates : array<string|int, mixed> = []

Config values to commit atomically with the response

Return values
ServiceResult

attemptRecordResponse()

Single transactional attempt to record an approval response.

private attemptRecordResponse(int $approvalId, int $memberId, string $decision, string|null $comment, int|null $nextApproverId, array<string|int, mixed> $approverConfigUpdates) : ServiceResult
Parameters
$approvalId : int
$memberId : int
$decision : string
$comment : string|null
$nextApproverId : int|null
$approverConfigUpdates : array<string|int, mixed>
Tags
throws
OptimisticLockException

When the version check fails.

Return values
ServiceResult

findEligibleMembers()

Find members who are potential candidates for an approval picker.

private findEligibleMembers(WorkflowApproval $approval) : array<string|int, Member>

This is not the final current-user response eligibility check. Response recording delegates to WorkflowApprovalsTable::isPendingApprovalForMember().

Parameters
$approval : WorkflowApproval
Return values
array<string|int, Member>

findMembersByPermission()

Find all members who have an active (unexpired) role granting the specified permission.

private findMembersByPermission(string $permissionName) : array<string|int, Member>
Parameters
$permissionName : string
Return values
array<string|int, Member>

findMembersByRole()

Find all members who have the specified active (unexpired) role.

private findMembersByRole(string $roleName) : array<string|int, Member>
Parameters
$roleName : string
Return values
array<string|int, Member>

getResolverBackedPendingDynamicApprovals()

Load callback-only dynamic approvals whose eligibility cannot be represented by lookup fields.

private getResolverBackedPendingDynamicApprovals(int $memberId) : array<string|int, WorkflowApproval>
Parameters
$memberId : int

Member ID.

Return values
array<string|int, WorkflowApproval>

getWorkflowInstanceResponderIds()

private getWorkflowInstanceResponderIds(int $workflowInstanceId) : array<string|int, int>
Parameters
$workflowInstanceId : int
Return values
array<string|int, int>

hasPriorDynamicWorkflowResponse()

Dynamic approvers may qualify through multiple routes, but each member can only respond once per workflow.

private hasPriorDynamicWorkflowResponse(WorkflowApproval $approval, int $memberId) : bool
Parameters
$approval : WorkflowApproval
$memberId : int
Return values
bool

parseDeadline()

Parse a deadline string (e.g., "7d", "24h") into a DateTime.

private parseDeadline(string $deadline) : DateTime
Parameters
$deadline : string
Return values
DateTime

resolveDynamicApproverIds()

Resolve eligible member IDs via configured callback service.

private resolveDynamicApproverIds(WorkflowApproval $approval) : array<string|int, int>

Expects approver_config: {"service": "App\Services\MyService", "method": "getEligibleApprovers"} The callback receives the WorkflowApproval and must return int[] of member IDs.

Parameters
$approval : WorkflowApproval
Tags
throws
RuntimeException

If config is missing or callback is invalid

Return values
array<string|int, int>

saveWithVersionCheck()

Save an approval entity with optimistic version bump.

private saveWithVersionCheck(mixed $approvalsTable, WorkflowApproval $approval) : void

Increments the version on save and verifies the row was updated. Must be called within a transaction that already holds the FOR UPDATE lock.

Parameters
$approvalsTable : mixed
$approval : WorkflowApproval
Tags
throws
OptimisticLockException

When the version has been changed by another process.

On this page

Search results