The skip framework determines whether a failed item should be skipped (logged and ignored) or whether the exception should propagate and fail the step.
namespace Lemric\BatchProcessing\Skip;
interface SkipPolicyInterface
{
/**
* @return bool true = skip this item, false = propagate exception
*
* @throws SkipLimitExceededException when the skip limit is exceeded
*/
public function shouldSkip(\Throwable $t, int $skipCount): bool;
}The most commonly used policy. Skips items matching configured exception
types, up to a maximum limit. Lemric\BatchProcessing\Exception\SkippableException
is always skippable up to the limit.
use Lemric\BatchProcessing\Skip\LimitCheckingItemSkipPolicy;
$policy = new LimitCheckingItemSkipPolicy(
skipLimit: 100,
skippableExceptions: [
\InvalidArgumentException::class => true,
\PDOException::class => false, // explicitly non-skippable
],
);Skips all exceptions unconditionally (use with caution):
use Lemric\BatchProcessing\Skip\AlwaysSkipItemSkipPolicy;
$policy = new AlwaysSkipItemSkipPolicy();Never skips — all exceptions propagate. This is the default.
Delegates to different skip policies based on the exception class:
use Lemric\BatchProcessing\Skip\{ExceptionClassifierSkipPolicy, AlwaysSkipItemSkipPolicy, NeverSkipItemSkipPolicy};
$policy = new ExceptionClassifierSkipPolicy(
policies: [
\PDOException::class => new NeverSkipItemSkipPolicy(),
\InvalidArgumentException::class => new AlwaysSkipItemSkipPolicy(),
],
defaultPolicy: new NeverSkipItemSkipPolicy(),
);Walks the full exception class hierarchy (including parent classes) to decide whether to skip.
Combines multiple skip policies.
Tracks skip counts independently per exception type (used internally by
SkipCounter).
$step = $stepBuilderFactory->get('importStep')
->chunk(500, $reader, $processor, $writer)
->faultTolerant()
->skip(\Lemric\BatchProcessing\Exception\SkippableException::class)
->skip(\InvalidArgumentException::class)
->skipLimit(200)
->build();Or pass a fully-built policy:
$step = $stepBuilderFactory->get('importStep')
->chunk(500, $reader, $processor, $writer)
->skipPolicy(new LimitCheckingItemSkipPolicy(50, [\InvalidArgumentException::class => true]))
->build();SkipListenerInterface exposes one callback per phase:
public function onSkipInRead(\Throwable $t): void;
public function onSkipInProcess(mixed $item, \Throwable $t): void;
public function onSkipInWrite(mixed $item, \Throwable $t): void;Skip counts are tracked independently per phase:
$stepExecution->getReadSkipCount();
$stepExecution->getProcessSkipCount();
$stepExecution->getWriteSkipCount();
$stepExecution->getSkipCount(); // total