Add SelfHandlingOptimizer for optimizers without a binary - #242
Merged
freekmurze merged 2 commits intoJun 29, 2026
Conversation
Some optimizers have no binary and no shell command, for example one that sends the image to an external optimization API. Add a SelfHandlingOptimizer interface (extending Optimizer) with a handle(Image, LoggerInterface) method; the chain delegates execution to it instead of building and running a Process, passing the chain's logger so the optimizer can log its own progress. The change is fully additive: SelfHandlingOptimizer extends Optimizer so instances still satisfy every existing type hint, and OptimizerChain only gains a branch inside runOptimizer()'s body, with no method signatures changed (subclasses overriding the protected methods stay compatible). Failures flow through the existing throws() handling from the previous release, and a failing handle() is logged like a failing binary. A BaseSelfHandlingOptimizer helper lets implementers write only canHandle() and handle(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BaseSelfHandlingOptimizer reimplemented the options, image path and tmp path state, the constructor and their accessors byte-for-byte from BaseOptimizer. Extend BaseOptimizer instead and keep only the no-op binaryName()/getCommand() overrides and the abstract handle().
Member
|
Thanks for the contribution, @mathiasgrimm! Merged and released in 1.10.0. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add
SelfHandlingOptimizerfor optimizers without a binaryProblem
OptimizerChainassumes every optimizer produces agetCommand()string that it runs through a SymfonyProcess. There is no way to write an optimizer that has no binary and no shell command, for example one that sends the image to an external optimization API.Solution
A new
SelfHandlingOptimizerinterface (extendingOptimizer) with a singlehandle()method. When the chain encounters one, it delegates execution tohandle()instead of building and running a process, passing the chain's logger so the optimizer can log its own progress:Add it to a chain with
addOptimizer()like any other optimizer.BaseSelfHandlingOptimizerreduces it to implementing justcanHandle()andhandle().Backwards compatibility
Fully additive, no breaking changes:
SelfHandlingOptimizer extends Optimizer, so instances satisfy every existingOptimizertype hint.OptimizerChainonly gains a branch insiderunOptimizer()'s body. No method signature changes, so any subclass overriding the protected methods (applyOptimizer,runOptimizer,handleException) stays compatible.Optimizerinterface,BaseOptimizer, the bundled optimizers, andcomposer.jsonare untouched.The only cost of staying non-breaking: a
SelfHandlingOptimizerinheritsgetCommand()/binaryName()fromOptimizer. They are never called (the chain branches beforegetCommand()), andBaseSelfHandlingOptimizerimplements them as documented no-ops.Logging & error handling
The self-handling flow mirrors the binary flow's logging. For each optimizer the chain logs the class name, then an "executing" line, followed by the optimizer's own log lines:
A failing
handle()emits anerror:line, just like a failing binary, and flows through the samethrows()mechanism from #241: by default it is logged and the chain continues;throws()(or a callable) lets you abort or inspect it.Tests
Adds
tests/SelfHandlingOptimizerTest.php(8 tests): delegation + image/logger passed, skip whencanHandle()is false, default-continue,throws()abort, custom-handler routing, temp-file cleanup (including whenhandle()throws), and a mixed binary + self-handling chain proving the binary optimizer flow is unchanged. Full suite: 40 passing.🤖 Generated with Claude Code