PHP 8.2 — Apply readonly classes
Problem Description
PHP 8.2 introduces the readonly class modifier, which allows marking an entire class as immutable. Classes where ALL properties are readonly can be converted to readonly class, which simplifies the code and improves its readability.
Location
Below is an analysis of classes in the project regarding the possibility of applying readonly class:
✅ Candidates for readonly class
| File |
Class |
Justification |
src/Resolver.php |
Resolver |
All properties are readonly |
src/Reboot/Strategy/StackRebootStrategy.php |
StackRebootStrategy |
One readonly property |
src/Scheduler/TaskErrorListener.php |
TaskErrorListener |
One readonly property |
src/Supervisor/ProcessErrorListener.php |
ProcessErrorListener |
One readonly property |
src/Scheduler/TaskHandler.php |
TaskHandler |
All properties are readonly |
src/Supervisor/ProcessHandler.php |
ProcessHandler |
All properties are readonly |
src/Middleware/StaticFilesMiddleware.php |
StaticFilesMiddleware |
One readonly property |
src/Middleware/SymfonyController.php |
SymfonyController |
One readonly property |
src/Event/ProcessStartEvent.php |
ProcessStartEvent |
All properties are readonly |
src/Event/TaskStartEvent.php |
TaskStartEvent |
All properties are readonly |
src/ServerAlreadyRunningException.php |
ServerAlreadyRunningException |
No own properties |
src/ServerNotRunningException.php |
ServerNotRunningException |
No own properties |
src/ServerStopFailedException.php |
ServerStopFailedException |
No own properties |
❌ Excluded classes (have mutable properties)
| File |
Class |
Reason for exclusion |
src/KernelFactory.php |
KernelFactory |
Has $kernel (nullable, mutable) |
src/Reboot/Strategy/ExceptionRebootStrategy.php |
ExceptionRebootStrategy |
Has $exception (mutable) |
src/Reboot/Strategy/MaxJobsRebootStrategy.php |
MaxJobsRebootStrategy |
Has $jobsCount (mutable) |
src/Scheduler/Trigger/CronExpressionTrigger.php |
CronExpressionTrigger |
Has $expression (immutable, but not readonly) |
src/Event/ProcessErrorEvent.php |
ProcessErrorEvent |
Has $error with setter (mutable) |
src/Event/TaskErrorEvent.php |
TaskErrorEvent |
Has $error with setter (mutable) |
src/Attribute/AsTask.php |
AsTask |
Attributes require mutable public properties |
src/Attribute/AsProcess.php |
AsProcess |
Attributes require mutable public properties |
Analysis
Example transformation — src/Resolver.php
Before:
final class Resolver implements ResolverInterface
{
public function __construct(private readonly ResolverInterface $resolver, private readonly array $options)
{
}
}
After:
final readonly class Resolver implements ResolverInterface
{
public function __construct(private ResolverInterface $resolver, private array $options)
{
}
}
Example transformation — exception classes
Before:
final class ServerAlreadyRunningException extends \RuntimeException
{
public function __construct()
{
parent::__construct('Workerman is already running.');
}
}
After:
final readonly class ServerAlreadyRunningException extends \RuntimeException
{
public function __construct()
{
parent::__construct('Workerman is already running.');
}
}
Proposed Solution
For each candidate class:
- Change class declaration from
final class Foo to final readonly class Foo
- Remove the
readonly keyword from individual property declarations (they become automatically readonly at the class level)
List of changes to make:
src/Resolver.php — line 9
src/Reboot/Strategy/StackRebootStrategy.php — line 7
src/Scheduler/TaskErrorListener.php — line 11
src/Supervisor/ProcessErrorListener.php — line 11
src/Scheduler/TaskHandler.php — line 12
src/Supervisor/ProcessHandler.php — line 12
src/Middleware/StaticFilesMiddleware.php — line 10
src/Middleware/SymfonyController.php — line 13
src/Event/ProcessStartEvent.php — line 9
src/Event/TaskStartEvent.php — line 9
src/ServerAlreadyRunningException.php — line 7
src/ServerNotRunningException.php — line 7
src/ServerStopFailedException.php — line 7
Priority
🟡 MEDIUM — code modernization, better encapsulation. These changes improve code readability and clearly communicate class immutability, but are not critical for functionality.
Tests
After making changes:
- Run
composer install to install dependencies
- Run
vendor/bin/phpunit to ensure all tests pass
- Run
vendor/bin/rector process --dry-run to verify no additional changes
- Check static code analysis (e.g., PHPStan)
Note: readonly class means that ALL class properties must be readonly. Before making changes, carefully verify each class to ensure it has no hidden mutable properties.
PHP 8.2 — Apply readonly classes
Problem Description
PHP 8.2 introduces the
readonly classmodifier, which allows marking an entire class as immutable. Classes where ALL properties arereadonlycan be converted toreadonly class, which simplifies the code and improves its readability.Location
Below is an analysis of classes in the project regarding the possibility of applying
readonly class:✅ Candidates for readonly class
src/Resolver.phpResolverreadonlysrc/Reboot/Strategy/StackRebootStrategy.phpStackRebootStrategyreadonlypropertysrc/Scheduler/TaskErrorListener.phpTaskErrorListenerreadonlypropertysrc/Supervisor/ProcessErrorListener.phpProcessErrorListenerreadonlypropertysrc/Scheduler/TaskHandler.phpTaskHandlerreadonlysrc/Supervisor/ProcessHandler.phpProcessHandlerreadonlysrc/Middleware/StaticFilesMiddleware.phpStaticFilesMiddlewarereadonlypropertysrc/Middleware/SymfonyController.phpSymfonyControllerreadonlypropertysrc/Event/ProcessStartEvent.phpProcessStartEventreadonlysrc/Event/TaskStartEvent.phpTaskStartEventreadonlysrc/ServerAlreadyRunningException.phpServerAlreadyRunningExceptionsrc/ServerNotRunningException.phpServerNotRunningExceptionsrc/ServerStopFailedException.phpServerStopFailedException❌ Excluded classes (have mutable properties)
src/KernelFactory.phpKernelFactory$kernel(nullable, mutable)src/Reboot/Strategy/ExceptionRebootStrategy.phpExceptionRebootStrategy$exception(mutable)src/Reboot/Strategy/MaxJobsRebootStrategy.phpMaxJobsRebootStrategy$jobsCount(mutable)src/Scheduler/Trigger/CronExpressionTrigger.phpCronExpressionTrigger$expression(immutable, but not readonly)src/Event/ProcessErrorEvent.phpProcessErrorEvent$errorwith setter (mutable)src/Event/TaskErrorEvent.phpTaskErrorEvent$errorwith setter (mutable)src/Attribute/AsTask.phpAsTasksrc/Attribute/AsProcess.phpAsProcessAnalysis
Example transformation — src/Resolver.php
Before:
After:
Example transformation — exception classes
Before:
After:
Proposed Solution
For each candidate class:
final class Footofinal readonly class Fooreadonlykeyword from individual property declarations (they become automatically readonly at the class level)List of changes to make:
src/Resolver.php— line 9src/Reboot/Strategy/StackRebootStrategy.php— line 7src/Scheduler/TaskErrorListener.php— line 11src/Supervisor/ProcessErrorListener.php— line 11src/Scheduler/TaskHandler.php— line 12src/Supervisor/ProcessHandler.php— line 12src/Middleware/StaticFilesMiddleware.php— line 10src/Middleware/SymfonyController.php— line 13src/Event/ProcessStartEvent.php— line 9src/Event/TaskStartEvent.php— line 9src/ServerAlreadyRunningException.php— line 7src/ServerNotRunningException.php— line 7src/ServerStopFailedException.php— line 7Priority
🟡 MEDIUM — code modernization, better encapsulation. These changes improve code readability and clearly communicate class immutability, but are not critical for functionality.
Tests
After making changes:
composer installto install dependenciesvendor/bin/phpunitto ensure all tests passvendor/bin/rector process --dry-runto verify no additional changesNote:
readonly classmeans that ALL class properties must be readonly. Before making changes, carefully verify each class to ensure it has no hidden mutable properties.