crazy-goat / workerman-bundle
Symfony bundle providing a Workerman runtime for high-performance long-running HTTP servers, with built-in task scheduler, process supervisor, PHAR packaging, and event-loop integration.
Package info
github.com/crazy-goat/workerman-bundle
Type:symfony-bundle
pkg:composer/crazy-goat/workerman-bundle
Requires
- php: ^8.2
- ext-pcntl: *
- ext-posix: *
- league/mime-type-detection: ^1.13
- psr/log: ^3.0
- symfony/config: ^6.4|^7.0|^8.0
- symfony/console: ^6.4|^7.0|^8.0
- symfony/dependency-injection: ^6.4|^7.0|^8.0
- symfony/http-kernel: ^6.4|^7.0|^8.0
- symfony/runtime: ^6.4|^7.0|^8.0
- workerman/workerman: ^5.0
Requires (Dev)
- dragonmantank/cron-expression: ^3.4
- guzzlehttp/guzzle: ^7.8
- php-cs-fixer/shim: ^3.75
- phpunit/phpunit: ^10.4
- rector/rector: ^2.0
- symfony/framework-bundle: ^6.4|^7.0|^8.0
Suggests
- ext-event: For better performance
- ext-inotify: For effective file monitoring
- dragonmantank/cron-expression: For parse cron expressions
- dev-master
- v0.22.0
- v0.21.0
- v0.20.0
- v0.19.0
- v0.18.0
- v0.17.0
- v0.16.0
- v0.15.0
- v0.14.0
- v0.13.0
- v0.12.0
- v0.11.0
- v0.10.0
- v0.9.9
- v0.9.8
- v0.9.7
- v0.9.6
- v0.9.5
- v0.9.4
- v0.9.3
- v0.9.2
- v0.9.1
- v0.9.0
- dev-security/unlink-error-handling-binary-file-response
- dev-fix/365-staticfiles-path-joining
- dev-feature/317-gate-memory-reset-peak-usage
- dev-fix/342-readme-static-files-middleware
- dev-perf/cache-method-exists-in-service-handler
- dev-fix/307-exception-reboot-strategy-memory-leak
- dev-fix/311-readme-orphaned-footnote
- dev-docs/300-license-reference-in-readme
- dev-docs/request-phpdoc-321
- dev-fix/347-inotify-watcher-phpdoc-types
- dev-fix/configloader-setbuildconfig-ordering
- dev-refactor/configloader-split-getconfig
- dev-fix/341-remove-redundant-function-exists-in-inotify-watcher
- dev-issue-294-request-converter-test-coverage
- dev-security/serverworker-ssl-file-validation
- dev-feature/276-taskhandler-processhandler-event-ordering-tests
- dev-fix/338-taskerrorevent-immutable
- dev-fix/296-http-request-handler-error-log
- dev-perf/schedulerworker-avoid-per-tick-closure
- dev-release/0.21.0-changelog
- dev-fix/298-superpowers-export-ignore
- dev-feature/299-composer-keywords-description
- dev-fix/316-pharhelper-remove-getprojectdir
- dev-docs/282-bin-directory-unexplained
- dev-docs/troubleshooting-long-running-workers
- dev-feature/http-request-handler-tests
- dev-fix/270-runtime-directory-restrictive-mode
- dev-feat/261-static-files-304-support
- dev-fix/289-readme-reboot-strategy-fqcn
- dev-feature/runner-end-to-end-test
- dev-fix/phar-compat-static-files-middleware
- dev-feature/259-phar-alias-security
- dev-feature/295-servermanager-magic-timeout-constants
- dev-feature/247-attribute-tests
- dev-feature/275-278-extract-shared-handler-listener-base
- dev-refactor/285-extract-directory-iterator-boilerplate
- dev-perf/request-converter-early-out-files
- dev-feat/precompose-middleware-pipeline
- dev-fix/zip-archive-tests
- dev-perf/remove-timer-add-on-every-request
- dev-feature/301-request-converter-refactor
- dev-fix/291-http-request-handler-refactor
- dev-changelog-0.20.0
- dev-feature/305-listen-scheme-enum
- dev-perf/246-polling-monitor-sharded-scan
- dev-docs/269-workerman-connections-docs
- dev-fix/zip-slip-protection-sfx-downloader
- dev-feature/235-static-files-security-allowlist
- dev-docs/257-version-matrix-mismatch
- dev-feature/241-test-byte-formatter
- dev-feature/chunked-response-default-strategy
- dev-feature/211-servermanager-refactoring
- dev-feature/209-scheduler-worker-behavioral-tests
- dev-refactor/214-servermanager-getconfig
- dev-docs/243-config-options-documentation
- dev-docs/231-middlewares-documentation
- dev-refactor/210-runner-extract-methods
- dev-issue-218-file-monitor-worker-test
- dev-fix/217-cookie-header-smuggling
- dev-fix/225-binaryfileresponsestrategy-onclose-overwrite
- dev-docs/232-servers-listen-docs
- dev-refactor/219-scheduler-run-callback
- dev-refactor/216-configuration-tree-builder-split
- dev-chore/changelog-0.18.0
- dev-issue-169-ci-lint-php-version
- dev-release/0.16.0
- dev-fix-155-servermanager-status-polling
- dev-fix/150-cpucount-null-safety
- dev-fix/152-trusted-proto
- dev-fix/pin-setup-php-to-sha
- dev-feature/php82-server-action-enum
- dev-chore/update-changelog-branch-protection
- dev-fix/issue-18-ssl-cert-validation
- dev-feature/kernel-state-reset-22
- dev-fix/streamed-binary-file-response-chunking
- dev-fix/multipart-empty-content
- dev-fix/60-server-protocol-e2e-tests
- dev-docs/update-changelog-69
- dev-fix/69-e2e-streamed-response-tests
- dev-feature/71-streamed-response-sse
- dev-feature/93-typed-exception-hierarchy
- dev-feature/95-branch-protection-docs
- dev-feature/file-upload-validator-87
- dev-fix/issue-85
- dev-fix/73-non-blocking-terminate
- dev-add-release-workflow
- dev-fix/issue-20-infinite-loop-graceful-stop
- dev-pr-13
- dev-add-test-bootstrap
- dev-replace-timer-with-globalevent
- dev-update-versions-and-security-fix
- dev-test-agains-sf-73
- dev-fix-double-content-type-headers
- dev-symfony-first
- dev-add-middlewares
- dev-test/separate-lint-from-tests
This package is auto-updated.
Last update: 2026-06-02 19:27:20 UTC
README
Workerman is a high-performance, asynchronous event-driven PHP framework written in pure PHP.
This bundle provides a Workerman integration in Symfony, allowing you to easily create a http server, scheduler and supervisor all in one place.
This bundle allows you to replace a traditional web application stack like php-fpm + nginx + cron + supervisord, all written in pure PHP (no Go, no external binaries).
The request handler works in an event loop which means the Symfony kernel and the dependency injection container are preserved between requests,
making your application faster with less (or no) code changes.
Contributing
Please see CONTRIBUTING.md for information about branch protection rules and development workflow.
What new in this fork
servers.reuse_port- on linux machines u can use kernel load balancer ifSO_REUSEPORTis enabled- By default
luzrain/workerman-bundleparse data topsr7request and then to symfonyRequest. Thisworkerman-bundlewill create symfony request without psr7 it increase performance, but it is still experimental.
Getting started
Install composer packages
composer require crazy-goat/workerman-bundle
Enable the bundle
<?php // config/bundles.php return [ // ... \CrazyGoat\WorkermanBundle\WorkermanBundle::class => ['all' => true], ];
Configure the bundle
A minimal configuration might look like this.
For all available options with documentation, see the command output.
$ bin/console config:dump-reference workerman
# config/services.yaml services: workerman.middleware.static_files: class: CrazyGoat\WorkermanBundle\Middleware\StaticFilesMiddleware arguments: $rootDirectory: '%kernel.project_dir%/public' # config/packages/workerman.yaml workerman: servers: - name: 'Symfony webserver' listen: http://127.0.0.1:8080 processes: 4 middlewares: - workerman.middleware.static_files reload_strategy: exception: active: true file_monitor: active: true
Note: The example above binds an unprivileged port (
8080) so it works withoutsudo.To bind a port below 1024 (e.g.
80or443) you must run the process as root or grant theCAP_NET_BIND_SERVICEcapability on Linux.In production, consider using the
userandgroupconfig keys to drop privileges after binding, or front with a reverse proxy (e.g. nginx, Caddy).
listenis effectively required. Omitting it creates a worker that does not accept connections — no traffic reaches your application. Supported URI schemes:http://,https://,ws://(WebSocket),wss://(WebSocket over SSL),tcp://(raw TCP).
Configuration reference
All top-level workerman configuration options:
| Key | Type | Default | Description |
|---|---|---|---|
runtime_dir |
string |
%kernel.project_dir% |
Writable runtime directory for cache, logs, and PID files. In PHAR mode defaults to the directory containing the PHAR. Can be overridden with WORKERMAN_RUNTIME_DIR env var. See build-packaging.md. |
user |
string|null |
null (current user) |
Unix user of processes. |
group |
string|null |
null (current group) |
Unix group of processes. |
stop_timeout |
int |
2 |
Max seconds of child process work before force kill. |
cache_warmup_timeout |
int |
30 |
Max seconds to wait for cache warmup in forked process. Can be overridden with WORKERMAN_CACHE_WARMUP_TIMEOUT env var. |
status_timeout |
int |
5 |
Max seconds to wait for status file generation after sending SIGIOT. |
pid_file |
string |
%kernel.project_dir%/var/run/workerman.pid |
File to store master process PID. |
log_file |
string |
%kernel.project_dir%/var/log/workerman.log |
Log file. |
stdout_file |
string |
%kernel.project_dir%/var/log/workerman.stdout.log |
File to write all output (echo, var_dump, etc.) to when running as daemon. |
max_package_size |
int |
10485760 (10 MB) |
Maximum accepted package size in bytes. |
connection_timeout |
int |
120 |
Max seconds to wait for a complete request before closing the connection (slowloris protection). See security.md. |
keepalive_timeout |
int |
30 |
Max idle seconds for keep-alive connections before closing. See security.md. |
response_chunk_size |
int |
2048 |
Response chunk size in bytes. |
trusted_hosts |
string[] |
[] |
List of regex patterns for trusted hostnames. Requests with a non-matching Host header are rejected with SuspiciousOperationException. See security.md. |
Start application
Using the Symfony console command (the bin/console below refers to your application's Symfony console, not the bin/ directory shipped by this bundle):
$ bin/console workerman:server start
$ bin/console workerman:server start -d # daemon mode
Note: All
bin/console workerman:*commands throughout this document refer to your application's Symfony console, not the scripts in this bundle'sbin/directory. Seebin/README.mdfor the bundle's own development scripts.
Or using the runtime directly:
$ APP_RUNTIME=CrazyGoat\\WorkermanBundle\\Runtime php public/index.php start
Manage the server
$ bin/console workerman:server stop # stop the server $ bin/console workerman:server stop -g # graceful stop $ bin/console workerman:server restart # restart $ bin/console workerman:server restart -d # restart in daemon mode $ bin/console workerman:server reload # reload workers (hot reload) $ bin/console workerman:server reload -g # graceful reload $ bin/console workerman:server status # show server status $ bin/console workerman:server connections # show active connections
workerman:server connections output
The command lists every active TCP connection across all worker processes. Example output:
--------------------------------------------------------------------- WORKERMAN CONNECTION STATUS --------------------------------------------------------------------------------
PID Worker CID Trans Protocol ipv4 ipv6 Recv-Q Send-Q Bytes-R Bytes-W Status Local Address Foreign Address
12345 webserver 1 tcp Http 1 0 0B 0B 12.3KB 4.1KB ESTABLISHED 127.0.0.1:8080 127.0.0.1:54321
| Column | Description |
|---|---|
PID |
Process ID of the worker handling the connection |
Worker |
Name of the worker process (truncated to 14 characters) |
CID |
Unique connection identifier assigned by Workerman |
Trans |
Transport layer protocol (tcp, udp, ssl) |
Protocol |
Application protocol (Http, Websocket, Text, or the transport name when no protocol is set). Names longer than 15 characters are truncated to 13 characters + .. |
ipv4 |
1 if the connection uses IPv4, 0 otherwise |
ipv6 |
1 if the connection uses IPv6, 0 otherwise |
Recv-Q |
Bytes waiting to be read from the receive buffer (formatted with B/KB/MB/GB/TB suffix) |
Send-Q |
Bytes waiting to be sent in the send buffer (formatted with B/KB/MB/GB/TB suffix) |
Bytes-R |
Total bytes received over the lifetime of the connection |
Bytes-W |
Total bytes written over the lifetime of the connection |
Status |
Current connection state: INITIAL, CONNECTING, ESTABLISHED, CLOSING, ENDING, or CLOSED |
Local Address |
Local socket address in ip:port format |
Foreign Address |
Remote peer socket address in ip:port format |
Platform note: Connection introspection relies on Workerman's internal tracking and is available on POSIX-compatible platforms (Linux, macOS). The output is generated by sending
SIGIOto the master process, which collects data from each worker. Windows is not supported because the command usesposix_kill().
Note: For better performance, Workerman recommends installing the php-event extension.
Note: If you have the
grpcPHP extension installed, you must set the environment variableGRPC_ENABLE_FORK_SUPPORT=1before starting the server. The grpc extension spawns background threads that deadlock in forked child processes (e.g. scheduler tasks) unless fork support is explicitly enabled. See grpc/grpc#31241 for details.
Programmatic reload
You can trigger a worker reload from your application code using Utils::reload():
<?php use CrazyGoat\WorkermanBundle\Utils; // Reload only the current worker process Utils::reload(); // Reload all worker processes Utils::reload(reloadAllWorkers: true);
This sends a SIGUSR1 signal to the worker (or parent) process. It is equivalent to running bin/console workerman:server reload but can be called from any context — controllers, services, scheduled tasks, or deploy hooks.
Note:
Utils::reload()requires thepcntlandposixPHP extensions. Both are always available in the Workerman runtime process.
Reload strategies
Because of the asynchronous nature of the server, the workers reuse loaded resources on each request. This means that in some cases we need to restart workers.
For example, after an exception is thrown, to prevent services from being in an unrecoverable state. Or every time you change the code in the IDE.
There are a few restart strategies that are implemented and can be enabled or disabled depending on the environment.
- exception
Reload worker each time that an exception is thrown during the request handling. - max_requests
Reload worker on every N request to prevent memory leaks. - file_monitor
Reload all workers each time you change the files. - always
Reload worker after each request. - memory
Reload worker when memory usage reaches a certain threshold. Three options are available:active(default:false) toggles the strategy,limit(default:134217728— 128 MB) is the RSS threshold in bytes that triggers a worker reload, andgc_limit(default:100663296— 96 MB) runsgc_collect_cycles()preemptively before the reload check.workerman: reload_strategy: memory: active: true limit: 268435456 # 256 MB gc_limit: 201326592 # 192 MB
Note: It is highly recommended to install the php-inotify extension for file monitoring. Without it, monitoring will work in polling mode, which can be very cpu and disk intensive for large projects.
See all available options for each strategy in the command output.
$ bin/console config:dump-reference workerman reload_strategy
Implement your own reload strategies
You can create reload strategy with your own logic by implementing the RebootStrategyInterface and adding the workerman.reboot_strategy tag to the service.
<?php use CrazyGoat\WorkermanBundle\Reboot\Strategy\RebootStrategyInterface; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; #[AutoconfigureTag('workerman.reboot_strategy')] final class TestRebootStrategy implements RebootStrategyInterface { public function shouldReboot(): bool { return true; } }
Middlewares
Middlewares allow you to intercept and process requests before they reach the Symfony controller, or modify responses before they are sent to the client.
A middleware is any service implementing CrazyGoat\WorkermanBundle\Middleware\MiddlewareInterface:
<?php use CrazyGoat\WorkermanBundle\Http\Request; use CrazyGoat\WorkermanBundle\Middleware\MiddlewareInterface; use Workerman\Protocols\Http\Response; final readonly class MyMiddleware implements MiddlewareInterface { public function __invoke(Request $request, callable $next): Response { // Pre-processing: inspect or modify the request if ($request->header('X-Custom') === null) { return new Response(400); } $response = $next($request); // Post-processing: inspect or modify the response $response->header('X-Processed-By', 'MyMiddleware'); return $response; } }
Registering middlewares
Register your middleware as a service in the Symfony container, then reference its service ID under workerman.servers[].middlewares:
# config/services.yaml services: App\Middleware\MyMiddleware: ~
# config/packages/workerman.yaml workerman: servers: - name: 'Symfony webserver' listen: http://127.0.0.1:8080 processes: 4 middlewares: - App\Middleware\MyMiddleware
Static files middleware
The deprecated serve_files and root_dir server options are replaced by the StaticFilesMiddleware. To serve static files from a public directory, register the middleware with the root directory path:
# config/services.yaml services: workerman.middleware.static_files: class: CrazyGoat\WorkermanBundle\Middleware\StaticFilesMiddleware arguments: $rootDirectory: '%kernel.project_dir%/public'
# config/packages/workerman.yaml workerman: servers: - name: 'Symfony webserver' listen: http://127.0.0.1:8080 processes: 4 middlewares: - workerman.middleware.static_files
The StaticFilesMiddleware resolves requests against the configured root directory, serves matching files directly, and passes through to the next handler for non-file requests. Directory traversal attacks are prevented by ensuring the resolved path stays within the root directory.
Execution order
Middlewares are executed in reverse registration order (last registered, first executed). This means the first middleware in the middlewares list wraps the innermost layer. Using onion model terminology:
Request → Middleware 1 → Middleware 2 → ... → Symfony controller → ... → Middleware 2 → Middleware 1 → Response
This allows outer middlewares to handle cross-cutting concerns (authentication, logging, rate limiting) before inner middlewares or the Symfony controller process the request.
Scheduler
Periodic tasks can be configured with attributes or with tags in configuration files.
Schedule string can be formatted in several ways:
- An integer to define the frequency as a number of seconds. Example: 60
- An ISO8601 datetime format. Example: 2023-08-01T01:00:00+08:00
- An ISO8601 duration format. Example: PT1M
- A relative date format as supported by DateInterval. Example: 1 minutes
- A cron expression. Example: */1 * * * *
Note: You need to install the dragonmantank/cron-expression package if you want to use cron expressions as schedule strings
<?php use CrazyGoat\WorkermanBundle\Attribute\AsTask; /** * Attribute parameters * name: Task name * schedule: Task schedule in any format * method: method to call, __invoke by default * jitter: Maximum jitter in seconds that adds a random time offset to the schedule. Use to prevent multiple tasks from running at the same time */ #[AsTask(name: 'My scheduled task', schedule: '1 minutes')] final class TaskService { public function __invoke() { // ... } }
Supervisor
Supervisor can be configured with attributes or with tags in configuration files.
Processes are kept alive and wake up if one of them dies.
<?php use CrazyGoat\WorkermanBundle\Attribute\AsProcess; /** * Attribute parameters * name: Process name * processes: number of processes * method: method to call, __invoke by default */ #[AsProcess(name: 'My worker', processes: 1)] final class ProcessService { public function __invoke() { // ... } }
Packaging (experimental)
⚠️ Experimental: PHAR and standalone binary packaging are new features. The API may change in future releases.
The bundle provides commands to package your Symfony application as a standalone PHAR archive or a native binary:
# Build a PHAR archive $ php -d phar.readonly=0 bin/console workerman:build:phar # Build a standalone binary (requires phpmicro.sfx) $ php -d phar.readonly=0 bin/console workerman:build:bin # Options $ php -d phar.readonly=0 bin/console workerman:build:phar --help $ php -d phar.readonly=0 bin/console workerman:build:bin --help
See docs/build-packaging.md for full documentation, build configuration options, and known limitations.
For an overview of all documentation files, see docs/.
For security-related documentation including Host-header protection and trusted hosts configuration, see docs/security.md.
For long-running worker gotchas, state pollution, stale DB connections, blocking IO, and other common issues, see docs/troubleshooting.md.
License
This bundle is open-sourced software licensed under the MIT license.