├── .github ├── CODEOWNERS ├── dependabot.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── tests ├── Fixtures │ ├── Symfony │ │ ├── app │ │ │ ├── data │ │ │ │ └── .gitkeep │ │ │ ├── .gitignore │ │ │ ├── public │ │ │ │ ├── robots.txt │ │ │ │ └── favicon.ico │ │ │ ├── config │ │ │ │ ├── api │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── static │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── prod │ │ │ │ │ └── parameters.yaml │ │ │ │ ├── profiler │ │ │ │ │ ├── framework.yaml │ │ │ │ │ ├── web_profiler.yaml │ │ │ │ │ └── routing │ │ │ │ │ │ └── routing.yaml │ │ │ │ ├── reactor │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── twig.yaml │ │ │ │ ├── auto │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── blackfire_monitoring │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── exception_handler_symfony │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── cov │ │ │ │ │ ├── swoole.yaml │ │ │ │ │ └── parameters.yaml │ │ │ │ ├── tideways │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── doctrine_migrations.yaml │ │ │ │ ├── framework.yaml │ │ │ │ ├── mime │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── access_log │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── non_reloadable_files │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── exception_handler_json │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── session │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── coroutines │ │ │ │ │ └── framework.yaml │ │ │ │ ├── coroutines_blackfire │ │ │ │ │ └── framework.yaml │ │ │ │ ├── swoole.yaml │ │ │ │ ├── messenger │ │ │ │ │ └── swoole.yaml │ │ │ │ ├── monolog.php │ │ │ │ ├── exception_handler_custom │ │ │ │ │ └── swoole.yaml │ │ │ │ └── doctrine.yaml │ │ │ ├── routing.yml │ │ │ ├── templates │ │ │ │ └── base.html.twig │ │ │ └── console │ │ ├── TestBundle │ │ │ ├── Resources │ │ │ │ └── mapping │ │ │ │ │ └── .gitkeep │ │ │ ├── Message │ │ │ │ ├── RunDummy.php │ │ │ │ ├── CreateFileMessage.php │ │ │ │ └── SleepAndAppend.php │ │ │ ├── Service │ │ │ │ ├── NonSharedExample.php │ │ │ │ ├── ShouldBeProxified2.php │ │ │ │ ├── RepositoryFactory.php │ │ │ │ ├── UnusedServiceToRemove.php │ │ │ │ ├── DummyService.php │ │ │ │ ├── SleepingCounter.php │ │ │ │ ├── AlwaysReset.php │ │ │ │ ├── AlwaysResetSafe.php │ │ │ │ ├── NoAutowiring │ │ │ │ │ └── ResetCountingRegistry.php │ │ │ │ ├── ShouldBeProxified.php │ │ │ │ ├── InMemoryRepository.php │ │ │ │ ├── DecorationTestDummyService.php │ │ │ │ ├── AdvancedDoctrineUsage.php │ │ │ │ └── SleepingCounterChecker.php │ │ │ ├── TestBundle.php │ │ │ ├── Controller │ │ │ │ ├── LogController.php │ │ │ │ ├── ReplacedContentTestController.php.tmpl │ │ │ │ ├── ReplacedContentTestController.php │ │ │ │ ├── TidewaysController.php │ │ │ │ ├── BlackfireController.php │ │ │ │ ├── IndexController.php │ │ │ │ ├── ThrowableController.php │ │ │ │ ├── TwigController.php │ │ │ │ ├── TaskController.php │ │ │ │ ├── EventsController.php │ │ │ │ └── SessionController.php │ │ │ ├── Resetter │ │ │ │ └── CountingResetter.php │ │ │ ├── Logging │ │ │ │ └── InMemoryLogger.php │ │ │ ├── DependencyInjection │ │ │ │ └── CompilerPass │ │ │ │ │ ├── OverrideDoctrineCompilerPass.php │ │ │ │ │ └── SleepingCounterCompileProcessor.php │ │ │ ├── ExceptionHandler │ │ │ │ └── TestCustomExceptionHandler.php │ │ │ ├── MessageHandler │ │ │ │ ├── CreateFileMessageHandler.php │ │ │ │ ├── RunDummyHandler.php │ │ │ │ └── SleepAndAppendHandler.php │ │ │ ├── Entity │ │ │ │ ├── AdvancedTest.php │ │ │ │ └── Test.php │ │ │ └── Migrations │ │ │ │ ├── Version20220920150015.php │ │ │ │ └── Version20230117125728.php │ │ ├── TestCacheKernel.php │ │ └── CoverageBundle │ │ │ ├── EventListeners │ │ │ ├── CoverageStartOnConsoleCommandEventListener.php │ │ │ └── CoverageFinishOnConsoleTerminate.php │ │ │ ├── ServerLifecycle │ │ │ ├── CoverageStartOnServerStart.php │ │ │ ├── CoverageStartOnServerManagerStart.php │ │ │ ├── CoverageStartOnServerWorkerStart.php │ │ │ ├── CoverageFinishOnServerShutdown.php │ │ │ └── CoverageStartOnServerManagerStop.php │ │ │ ├── TaskHandler │ │ │ └── CodeCoverageTaskHandler.php │ │ │ └── RequestHandler │ │ │ └── CodeCoverageRequestHandler.php │ └── resources │ │ └── .gitignore ├── k6 │ └── libs │ │ └── shim │ │ ├── urijs.js │ │ ├── lodash.js │ │ ├── cheerio.js │ │ ├── crypto-js.js │ │ ├── full.js │ │ ├── expect.js │ │ ├── xml2Json.js │ │ └── jsonSchema.js ├── Unit │ ├── Component │ │ └── AtomicCounter │ │ │ ├── AtomicStub.php │ │ │ ├── AtomicSpy.php │ │ │ └── AtomicCounterTest.php │ ├── Server │ │ ├── Configurator │ │ │ ├── ConfiguratorDummy.php │ │ │ ├── ConfiguratorSpy.php │ │ │ ├── WithRequestHandlerTest.php │ │ │ ├── WithWorkerExitHandlerTest.php │ │ │ └── WithWorkerStopHandlerTest.php │ │ ├── Runtime │ │ │ └── HMR │ │ │ │ ├── HMRSpy.php │ │ │ │ └── NonReloadableFilesTest.php │ │ ├── RequestHandler │ │ │ └── RequestHandlerDummy.php │ │ ├── IntMother.php │ │ └── Php8 │ │ │ └── SwooleServerMock.php │ ├── Bridge │ │ └── Symfony │ │ │ ├── ErrorHandler │ │ │ └── ThrowableHandlerFactoryTest.php │ │ │ └── HttpFoundation │ │ │ └── SetRequestRuntimeConfigurationTest.php │ ├── Client │ │ └── HttpClientTest.php │ └── Functions │ │ ├── TestObject.php │ │ └── GetObjectPropertyTest.php ├── Feature │ ├── BlackfireProfilerRegisteredTest.php │ └── BlackfireMonitoringRegisteredTest.php └── run-feature-tests-code-coverage.sh ├── docs ├── img │ └── blackfire-io.png └── swoole-blackfire.md ├── src ├── Server │ ├── Session │ │ ├── Exception │ │ │ ├── SessionExceptionInterface.php │ │ │ ├── LogicException.php │ │ │ └── RuntimeException.php │ │ └── StorageInterface.php │ ├── Middleware │ │ ├── MiddlewareFactory.php │ │ └── Middleware.php │ ├── Configurator │ │ ├── ConfiguratorInterface.php │ │ ├── WithRequestHandler.php │ │ ├── WithWorkerStartHandler.php │ │ ├── WithServerShutdownHandler.php │ │ ├── WithServerManagerStopHandler.php │ │ ├── WithServerManagerStartHandler.php │ │ ├── WithWorkerExitHandler.php │ │ ├── WithWorkerStopHandler.php │ │ ├── WithWorkerErrorHandler.php │ │ ├── CallableChainConfigurator.php │ │ ├── WithTaskHandler.php │ │ ├── CallableChainConfiguratorFactory.php │ │ ├── WithTaskFinishedHandler.php │ │ ├── WithServerStartHandler.php │ │ └── WithHttpServerConfiguration.php │ ├── TaskHandler │ │ ├── TaskHandlerInterface.php │ │ ├── NoOpTaskHandler.php │ │ ├── NoOpTaskFinishedHandler.php │ │ └── TaskFinishedHandlerInterface.php │ ├── LifecycleHandler │ │ ├── ServerStartHandlerInterface.php │ │ ├── ServerShutdownHandlerInterface.php │ │ ├── NoOpServerStartHandler.php │ │ ├── NoOpServerShutdownHandler.php │ │ ├── NoOpServerManagerStopHandler.php │ │ ├── NoOpServerManagerStartHandler.php │ │ ├── ServerManagerStopHandlerInterface.php │ │ ├── ServerManagerStartHandlerInterface.php │ │ └── SigIntHandler.php │ ├── Runtime │ │ ├── HMR │ │ │ └── HotModuleReloaderInterface.php │ │ ├── BootableInterface.php │ │ ├── CallableBootManager.php │ │ └── CallableBootManagerFactory.php │ ├── Exception │ │ ├── NotRunningException.php │ │ ├── UninitializedException.php │ │ ├── IllegalInitializationException.php │ │ ├── PortUnavailableException.php │ │ └── UnexpectedPortException.php │ ├── RequestHandler │ │ ├── ExceptionHandler │ │ │ ├── ExceptionHandlerInterface.php │ │ │ ├── ProductionExceptionHandler.php │ │ │ └── JsonExceptionHandler.php │ │ ├── RequestHandlerInterface.php │ │ └── ExceptionRequestHandler.php │ ├── WorkerHandler │ │ ├── NoOpWorkerErrorHandler.php │ │ ├── NoOpWorkerExitHandler.php │ │ ├── NoOpWorkerStartHandler.php │ │ ├── NoOpWorkerStopHandler.php │ │ ├── WorkerExitHandlerInterface.php │ │ ├── WorkerStopHandlerInterface.php │ │ ├── WorkerErrorHandlerInterface.php │ │ ├── WorkerStartHandlerInterface.php │ │ └── HMRWorkerStartHandler.php │ └── Api │ │ ├── ApiServerInterface.php │ │ ├── ApiServerClientFactory.php │ │ ├── WithApiServerConfiguration.php │ │ └── ApiServerClient.php ├── Component │ ├── Locking │ │ ├── MutexFactory.php │ │ ├── Mutex.php │ │ ├── Channel │ │ │ └── ChannelMutexFactory.php │ │ ├── FirstTimeOnly │ │ │ └── FirstTimeOnlyMutexFactory.php │ │ └── RecursiveOwner │ │ │ └── RecursiveOwnerMutexFactory.php │ └── AtomicCounter.php ├── Bridge │ ├── Symfony │ │ ├── Container │ │ │ ├── Resetter.php │ │ │ ├── StabilityChecker.php │ │ │ ├── ServicePool │ │ │ │ ├── ServicePool.php │ │ │ │ ├── ServicePoolContainer.php │ │ │ │ └── UnmanagedFactoryServicePool.php │ │ │ ├── SimpleResetter.php │ │ │ ├── Modifier │ │ │ │ └── Builder │ │ │ │ │ └── Builder.php │ │ │ ├── UsageBeforeInitialization.php │ │ │ └── Proxy │ │ │ │ ├── FileLocatorFactory.php │ │ │ │ ├── ContextualProxy.php │ │ │ │ ├── ProxyDirectoryHandler.php │ │ │ │ ├── Instantiator.php │ │ │ │ └── Generation │ │ │ │ ├── MethodForwarderBuilder.php │ │ │ │ └── MethodGenerator │ │ │ │ └── GetWrappedServicePoolValue.php │ │ ├── HttpFoundation │ │ │ ├── RequestFactoryInterface.php │ │ │ ├── ResponseProcessorInterface.php │ │ │ ├── ResponseProcessorInjectorInterface.php │ │ │ ├── ResponseProcessor.php │ │ │ ├── NoOpStreamedResponseProcessor.php │ │ │ ├── SetRequestRuntimeConfiguration.php │ │ │ ├── ResponseProcessorInjector.php │ │ │ ├── StreamedResponseProcessor.php │ │ │ ├── AccessLogOnKernelTerminate.php │ │ │ ├── CloudFrontRequestFactory.php │ │ │ ├── SwooleRequestResponseContextManager.php │ │ │ └── RequestFactory.php │ │ ├── HttpKernel │ │ │ ├── KernelPoolInterface.php │ │ │ ├── SimpleKernelPool.php │ │ │ ├── CoroutineKernelPool.php │ │ │ └── ContextReleasingHttpKernelRequestHandler.php │ │ ├── Bundle │ │ │ ├── DependencyInjection │ │ │ │ └── CompilerPass │ │ │ │ │ └── StatefulServices │ │ │ │ │ ├── CompileProcessor.php │ │ │ │ │ ├── SafeStatefulServiceTag.php │ │ │ │ │ ├── NonSharedSvcPoolConfigurator.php │ │ │ │ │ ├── StatefulServiceTag.php │ │ │ │ │ └── FinalClassesProcessor.php │ │ │ ├── Command │ │ │ │ ├── ServerRunCommand.php │ │ │ │ └── ParametersHelperTrait.php │ │ │ ├── Exception │ │ │ │ ├── CouldNotCreatePidFileException.php │ │ │ │ └── PidFileNotAccessibleException.php │ │ │ └── EventDispatcher │ │ │ │ ├── EventDispatchingServerStartHandler.php │ │ │ │ ├── EventDispatchingWorkerErrorHandler.php │ │ │ │ ├── EventDispatchingWorkerExitHandler.php │ │ │ │ ├── EventDispatchingWorkerStopHandler.php │ │ │ │ └── EventDispatchingWorkerStartHandler.php │ │ ├── Event │ │ │ ├── ServerStartedEvent.php │ │ │ ├── RequestWithSessionFinishedEvent.php │ │ │ ├── WorkerErrorEvent.php │ │ │ ├── WorkerExitedEvent.php │ │ │ ├── WorkerStartedEvent.php │ │ │ └── WorkerStoppedEvent.php │ │ ├── ErrorHandler │ │ │ ├── ThrowableHandlerFactory.php │ │ │ ├── ExceptionHandlerFactory.php │ │ │ ├── ErrorResponder.php │ │ │ └── ResponseDelayingExceptionHandler.php │ │ └── Messenger │ │ │ ├── Exception │ │ │ └── ReceiverNotAvailableException.php │ │ │ ├── ContextReleasingTransportHandler.php │ │ │ ├── ServiceResettingTransportHandler.php │ │ │ ├── SwooleServerTaskReceiver.php │ │ │ ├── ExceptionLoggingTransportHandler.php │ │ │ ├── SwooleServerTaskTransportHandler.php │ │ │ ├── SwooleServerTaskSender.php │ │ │ ├── SwooleServerTaskTransportFactory.php │ │ │ └── SwooleServerTaskTransport.php │ ├── Log │ │ └── AccessLogFormatterInterface.php │ ├── Tideways │ │ └── Apm │ │ │ ├── WithApm.php │ │ │ ├── TidewaysMiddlewareFactory.php │ │ │ ├── Apm.php │ │ │ └── ProfilingMiddleware.php │ ├── Upscale │ │ └── Blackfire │ │ │ ├── Profiling │ │ │ ├── WithProfiler.php │ │ │ └── ProfilerActivator.php │ │ │ └── Monitoring │ │ │ ├── WithApm.php │ │ │ ├── BlackfireMiddlewareFactory.php │ │ │ ├── Apm.php │ │ │ └── MonitoringMiddleware.php │ └── Doctrine │ │ ├── ORM │ │ ├── EntityManagerResetter.php │ │ └── EntityManagerStabilityChecker.php │ │ ├── ServicePooledRepositoryFactory.php │ │ └── DBAL │ │ └── ConnectionKeepAliveResetter.php ├── functions_include.php └── Client │ ├── Exception │ ├── MissingContentTypeException.php │ ├── UnsupportedHttpMethodException.php │ ├── UnsupportedContentTypeException.php │ └── ClientConnectionErrorException.php │ └── Http.php ├── .gitignore ├── .dockerignore ├── mutagen.yml ├── .codecov.yml ├── .editorconfig ├── commitlint.config.js ├── Makefile ├── phpunit.xml ├── phpstan.neon.dist ├── phpstan.tests.neon ├── .codeclimate.yml └── LICENSE /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Rastusik 2 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/.gitignore: -------------------------------------------------------------------------------- 1 | /var 2 | -------------------------------------------------------------------------------- /tests/Fixtures/resources/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Resources/mapping/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/api/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | api: true 4 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/static/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | static: default 4 | -------------------------------------------------------------------------------- /docs/img/blackfire-io.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelfederation/swoole-bundle/HEAD/docs/img/blackfire-io.png -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/prod/parameters.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | container.dumper.inline_factories: true 3 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/profiler/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | profiler: { only_exceptions: false } 3 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/reactor/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | running_mode: reactor 4 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/routing.yml: -------------------------------------------------------------------------------- 1 | controller: 2 | resource: '@TestBundle/Controller' 3 | type: annotation 4 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | debug: '%kernel.debug%' 3 | strict_variables: '%kernel.debug%' 4 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/profiler/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: true 3 | intercept_redirects: false 4 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/auto/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | static: auto 4 | hmr: 5 | enabled: auto 6 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/blackfire_monitoring/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | services: 4 | blackfire_monitoring: true 5 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/exception_handler_symfony/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | exception_handler: 4 | type: symfony 5 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelfederation/swoole-bundle/HEAD/tests/Fixtures/Symfony/app/public/favicon.ico -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/cov/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | settings: 4 | worker_count: 1 5 | reactor_count: 1 6 | -------------------------------------------------------------------------------- /tests/k6/libs/shim/urijs.js: -------------------------------------------------------------------------------- 1 | /* global postman */ 2 | 3 | import URI from '../urijs.js'; 4 | 5 | const Extend = Symbol.for('extend'); 6 | 7 | postman[Extend].module.urijs = URI; 8 | -------------------------------------------------------------------------------- /tests/k6/libs/shim/lodash.js: -------------------------------------------------------------------------------- 1 | /* global postman */ 2 | 3 | import lodash from '../lodash.js'; 4 | 5 | const Extend = Symbol.for('extend'); 6 | 7 | postman[Extend].module.lodash = lodash; 8 | -------------------------------------------------------------------------------- /tests/k6/libs/shim/cheerio.js: -------------------------------------------------------------------------------- 1 | /* global postman */ 2 | 3 | import cheerio from '../cheerio.js'; 4 | 5 | const Extend = Symbol.for('extend'); 6 | 7 | postman[Extend].module.cheerio = cheerio; 8 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/tideways/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | services: 4 | tideways_apm: 5 | enabled: true 6 | service_name: 'swoole_bundle_test' 7 | -------------------------------------------------------------------------------- /tests/k6/libs/shim/crypto-js.js: -------------------------------------------------------------------------------- 1 | /* global postman */ 2 | 3 | import cryptoJs from '../crypto-js.js'; 4 | 5 | const Extend = Symbol.for('extend'); 6 | 7 | postman[Extend].module['crypto-js'] = cryptoJs; 8 | -------------------------------------------------------------------------------- /tests/k6/libs/shim/full.js: -------------------------------------------------------------------------------- 1 | import './core'; 2 | import './cheerio'; 3 | import './crypto-js.js'; 4 | import './expect.js'; 5 | import './jsonSchema.js'; 6 | import './lodash.js'; 7 | import './xml2Json'; 8 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | migrations_paths: 3 | TestBundleDoctrineMigrations: '%kernel.project_dir%/../TestBundle/Migrations' 4 | transactional: false 5 | -------------------------------------------------------------------------------- /src/Server/Session/Exception/SessionExceptionInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Welcome! 6 | 7 | 8 |

Welcome to Swoole Server

9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: ThisIsVeryNotSecret! 3 | default_locale: en 4 | php_errors: 5 | log: true 6 | test: ~ 7 | session: 8 | enabled: false 9 | router: 10 | utf8: true 11 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/mime/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | static: 4 | strategy: 'advanced' 5 | public_dir: '%kernel.project_dir%/public' 6 | mime_types: 7 | txt: 'text/html' 8 | -------------------------------------------------------------------------------- /src/Bridge/Log/AccessLogFormatterInterface.php: -------------------------------------------------------------------------------- 1 | { 6 | if (err) { 7 | throw err; 8 | } 9 | json = result; 10 | }); 11 | return json; 12 | } 13 | 14 | global.xml2Json = xml2Json; 15 | -------------------------------------------------------------------------------- /mutagen.yml: -------------------------------------------------------------------------------- 1 | sync: 2 | defaults: 3 | ignore: 4 | vcs: true 5 | paths: 6 | - .DS_Store # macOS files 7 | - .idea 8 | permissions: 9 | defaultFileMode: 644 10 | defaultDirectoryMode: 755 11 | swoolebundle: 12 | alpha: "." 13 | beta: "docker://app@swoole-bundle-cli-80/usr/src/app" 14 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/RepositoryFactory.php: -------------------------------------------------------------------------------- 1 | {$this->resetFn}(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Server/Exception/UninitializedException.php: -------------------------------------------------------------------------------- 1 | value; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/Command/ServerRunCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('Run Swoole HTTP server.'); 15 | 16 | parent::configure(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/Exception/CouldNotCreatePidFileException.php: -------------------------------------------------------------------------------- 1 | counter; 15 | } 16 | 17 | public function getCounter(): int 18 | { 19 | return $this->counter; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/exception_handler_json/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | exception_handler: 4 | type: json 5 | verbosity: default 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Container/Modifier/Builder/Builder.php: -------------------------------------------------------------------------------- 1 | tick = true; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/coroutines/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | app: cache.adapter.array 4 | system: cache.adapter.array 5 | 6 | messenger: 7 | enabled: true 8 | reset_on_message: true # do not forget this or an equivalent! :) 9 | transports: 10 | swoole: swoole://task 11 | routing: 12 | 'K911\Swoole\Tests\Fixtures\Symfony\TestBundle\Message\SleepAndAppend': swoole 13 | 'K911\Swoole\Tests\Fixtures\Symfony\TestBundle\Message\RunDummy': swoole 14 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/coroutines_blackfire/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | app: cache.adapter.array 4 | system: cache.adapter.array 5 | 6 | messenger: 7 | enabled: true 8 | reset_on_message: true # do not forget this or an equivalent! :) 9 | transports: 10 | swoole: swoole://task 11 | routing: 12 | 'K911\Swoole\Tests\Fixtures\Symfony\TestBundle\Message\SleepAndAppend': swoole 13 | 'K911\Swoole\Tests\Fixtures\Symfony\TestBundle\Message\RunDummy': swoole 14 | -------------------------------------------------------------------------------- /tests/Unit/Server/RequestHandler/RequestHandlerDummy.php: -------------------------------------------------------------------------------- 1 | server; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | // https://github.com/conventional-changelog/commitlint/blob/master/docs/reference-rules.md 2 | module.exports = { 3 | extends: ['@commitlint/config-conventional'], 4 | rules: { 5 | 'body-max-line-length': [1, 'always', 500], 6 | 'footer-max-line-length': [1, 'always', 200], 7 | 'header-max-length': [1, 'always', 100], 8 | 'scope-case': [2, 'always', ['lower-case', 'kebab-case']], 9 | 'subject-case': [2, 'never', ['start-case', 'pascal-case', 'upper-case']] 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /tests/Unit/Server/Configurator/ConfiguratorSpy.php: -------------------------------------------------------------------------------- 1 | configured = true; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/AlwaysReset.php: -------------------------------------------------------------------------------- 1 | wasReset = true; 16 | } 17 | 18 | public function getWasReset(): bool 19 | { 20 | return $this->wasReset; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Component/Locking/FirstTimeOnly/FirstTimeOnlyMutexFactory.php: -------------------------------------------------------------------------------- 1 | wrapped->newMutex()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Component/Locking/RecursiveOwner/RecursiveOwnerMutexFactory.php: -------------------------------------------------------------------------------- 1 | wrapped->newMutex()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Bridge/Tideways/Apm/WithApm.php: -------------------------------------------------------------------------------- 1 | apm->instrument($server); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/AlwaysResetSafe.php: -------------------------------------------------------------------------------- 1 | wasReset = true; 16 | } 17 | 18 | public function getWasReset(): bool 19 | { 20 | return $this->wasReset; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Unit/Component/AtomicCounter/AtomicSpy.php: -------------------------------------------------------------------------------- 1 | incremented = 1 === $value; 21 | 22 | return $this->incremented ? 1 : 0; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Event/RequestWithSessionFinishedEvent.php: -------------------------------------------------------------------------------- 1 | sessionId; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/ResponseProcessorInjectorInterface.php: -------------------------------------------------------------------------------- 1 | profiler); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Client/Exception/UnsupportedHttpMethodException.php: -------------------------------------------------------------------------------- 1 | fileName; 18 | } 19 | 20 | public function content(): string 21 | { 22 | return $this->content; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/swoole.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | env(PORT): 9501 3 | env(HOST): 0.0.0.0 4 | env(TRUSTED_HOSTS): localhost,127.0.0.1,docker.stack 5 | env(TRUSTED_PROXIES): '*,192.168.0.0/16' 6 | env(WORKER_COUNT): 6 7 | env(REACTOR_COUNT): 3 8 | 9 | swoole: 10 | http_server: 11 | port: '%env(int:PORT)%' 12 | host: '%env(HOST)%' 13 | trusted_hosts: '%env(TRUSTED_HOSTS)%' 14 | trusted_proxies: '%env(TRUSTED_PROXIES)%' 15 | settings: 16 | worker_count: '%env(int:WORKER_COUNT)%' 17 | reactor_count: '%env(int:REACTOR_COUNT)%' 18 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/ErrorHandler/ThrowableHandlerFactory.php: -------------------------------------------------------------------------------- 1 | getMethod('handleThrowable'); 15 | $method->setAccessible(true); 16 | 17 | return $method; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean/coverage 2 | clean/coverage: 3 | @rm -rf cov/* 4 | @rm -rf clover.xml 5 | 6 | .PHONY: clean/tests/resources 7 | clean/tests/resources: 8 | @rm -rf tests/Fixtures/resources/*.pid 9 | @rm -rf tests/Fixtures/resources/*.txt 10 | 11 | .PHONY: clean/fixtures/cache 12 | clean/fixtures/cache: 13 | @rm -rf tests/Fixtures/Symfony/app/var/cache/* 14 | 15 | .PHONY: clean/fixtures/logs 16 | clean/fixtures/logs: 17 | @rm -rf tests/Fixtures/Symfony/app/var/log/* 18 | 19 | .PHONY: clean 20 | clean: clean/coverage clean/fixtures/cache clean/fixtures/logs clean/tests/resources 21 | -------------------------------------------------------------------------------- /src/Client/Exception/UnsupportedContentTypeException.php: -------------------------------------------------------------------------------- 1 | finish() is called. 11 | * 12 | * @see https://www.swoole.co.uk/docs/modules/swoole-server/callback-functions#onfinish 13 | */ 14 | interface TaskFinishedHandlerInterface 15 | { 16 | /** 17 | * @param mixed $data 18 | */ 19 | public function handle(Server $server, int $taskId, $data): void; 20 | } 21 | -------------------------------------------------------------------------------- /src/Component/AtomicCounter.php: -------------------------------------------------------------------------------- 1 | counter->add(1); 18 | } 19 | 20 | public function get(): int 21 | { 22 | return $this->counter->get(); 23 | } 24 | 25 | public static function fromZero(): self 26 | { 27 | return new self(new Atomic(0)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/NoAutowiring/ResetCountingRegistry.php: -------------------------------------------------------------------------------- 1 | resetCount; 16 | parent::reset(); 17 | } 18 | 19 | public function getResetCount(): int 20 | { 21 | return $this->resetCount; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/DependencyInjection/CompilerPass/StatefulServices/SafeStatefulServiceTag.php: -------------------------------------------------------------------------------- 1 | tag['reset_on_each_request'] ?? null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/k6/libs/shim/jsonSchema.js: -------------------------------------------------------------------------------- 1 | /* global postman */ 2 | 3 | import Ajv from '../ajv.js'; 4 | 5 | const Extend = Symbol.for('extend'); 6 | 7 | Object.assign(postman[Extend], { 8 | jsonSchema(store, schema, options) { 9 | const ajv = new Ajv(options); 10 | const validate = ajv.compile(schema); 11 | store.test.push(response => validate(store.response.body.json)); 12 | }, 13 | 14 | jsonSchemaNot(store, schema, options) { 15 | const ajv = new Ajv(options); 16 | const validate = ajv.compile(schema); 17 | store.test.push(response => !validate(store.response.body.json)); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | src/ 7 | 8 | 9 | 10 | 11 | tests/Unit 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/ShouldBeProxified.php: -------------------------------------------------------------------------------- 1 | dummy->getWasReset(); 18 | } 19 | 20 | public function getSafeDummy(): AlwaysResetSafe 21 | { 22 | return $this->safeDummy; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Server/Api/ApiServerInterface.php: -------------------------------------------------------------------------------- 1 | profilerActivator->activate($server); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithRequestHandler.php: -------------------------------------------------------------------------------- 1 | on('request', [$this->requestHandler, 'handle']); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Bridge/Upscale/Blackfire/Monitoring/WithApm.php: -------------------------------------------------------------------------------- 1 | apm = $apm; 17 | } 18 | 19 | /** 20 | * {@inheritdoc} 21 | */ 22 | public function configure(Server $server): void 23 | { 24 | $this->apm->instrument($server); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithWorkerStartHandler.php: -------------------------------------------------------------------------------- 1 | on('WorkerStart', [$this->handler, 'handle']); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Bridge/Doctrine/ORM/EntityManagerResetter.php: -------------------------------------------------------------------------------- 1 | clear(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithServerShutdownHandler.php: -------------------------------------------------------------------------------- 1 | on('shutdown', [$this->handler, 'handle']); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Container/Proxy/FileLocatorFactory.php: -------------------------------------------------------------------------------- 1 | directoryHandler->ensureProxyDirExists(); 19 | 20 | return new FileLocator($proxiesDirectory); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Bridge/Tideways/Apm/Apm.php: -------------------------------------------------------------------------------- 1 | injector->injectMiddlevare($server, $this->middlewareFactory); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithServerManagerStopHandler.php: -------------------------------------------------------------------------------- 1 | on('ManagerStop', [$this->handler, 'handle']); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "composer" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | target-branch: "develop" 13 | ignore: 14 | - dependency-name: "*" 15 | update-types: [ "version-update:semver-major" ] 16 | open-pull-requests-limit: 1 17 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithServerManagerStartHandler.php: -------------------------------------------------------------------------------- 1 | on('ManagerStart', [$this->handler, 'handle']); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Unit/Bridge/Symfony/ErrorHandler/ThrowableHandlerFactoryTest.php: -------------------------------------------------------------------------------- 1 | getName(); 16 | 17 | self::assertTrue('handleThrowable' === $methodName || 'handleException' === $methodName); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/LogController.php: -------------------------------------------------------------------------------- 1 | server; 23 | } 24 | 25 | public function getWorkerId(): int 26 | { 27 | return $this->workerId; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Event/WorkerExitedEvent.php: -------------------------------------------------------------------------------- 1 | server; 23 | } 24 | 25 | public function getWorkerId(): int 26 | { 27 | return $this->workerId; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Event/WorkerStartedEvent.php: -------------------------------------------------------------------------------- 1 | server; 23 | } 24 | 25 | public function getWorkerId(): int 26 | { 27 | return $this->workerId; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Event/WorkerStoppedEvent.php: -------------------------------------------------------------------------------- 1 | server; 23 | } 24 | 25 | public function getWorkerId(): int 26 | { 27 | return $this->workerId; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Server/WorkerHandler/WorkerExitHandlerInterface.php: -------------------------------------------------------------------------------- 1 | taskworker) { 19 | * echo "Hello from task worker process"; 20 | * } 21 | * ``` 22 | */ 23 | public function handle(Server $worker, int $workerId): void; 24 | } 25 | -------------------------------------------------------------------------------- /src/Server/WorkerHandler/WorkerStopHandlerInterface.php: -------------------------------------------------------------------------------- 1 | taskworker) { 19 | * echo "Hello from task worker process"; 20 | * } 21 | * ``` 22 | */ 23 | public function handle(Server $worker, int $workerId): void; 24 | } 25 | -------------------------------------------------------------------------------- /tests/Unit/Server/Runtime/HMR/NonReloadableFilesTest.php: -------------------------------------------------------------------------------- 1 | createMock(Filesystem::class); 16 | $fsmock->expects($this->exactly(2))->method('dumpFile')->withAnyParameters(); 17 | $nrf = new NonReloadableFiles('cache', '/var/www', $fsmock); 18 | $nrf->boot(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | inferPrivatePropertyTypeFromConstructor: true 3 | checkMissingIterableValueType: false 4 | excludePaths: 5 | - src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php 6 | - src/Bridge/Symfony/Container/ContainerModifier.php 7 | - src/Bridge/Monolog/StreamHandler.php 8 | 9 | # Internal error: Expected to find an ancestor with class name Swoole\Timer on Swoole\Server, but none was found. 10 | - src/Server/WorkerHandler/HMRWorkerStartHandler.php 11 | - hack 12 | ignoreErrors: 13 | # Put false positives here 14 | - '#PHPDoc tag @var for variable \$row contains unresolvable type#' 15 | -------------------------------------------------------------------------------- /src/Bridge/Upscale/Blackfire/Monitoring/BlackfireMiddlewareFactory.php: -------------------------------------------------------------------------------- 1 | monitoring = $monitoring; 16 | } 17 | 18 | public function createMiddleware(callable $nextMiddleware): callable 19 | { 20 | return new MonitoringMiddleware($nextMiddleware, $this->monitoring); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithWorkerExitHandler.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 17 | } 18 | 19 | /** 20 | * {@inheritdoc} 21 | */ 22 | public function configure(Server $server): void 23 | { 24 | $server->on('WorkerExit', [$this->handler, 'handle']); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithWorkerStopHandler.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 17 | } 18 | 19 | /** 20 | * {@inheritdoc} 21 | */ 22 | public function configure(Server $server): void 23 | { 24 | $server->on('WorkerStop', [$this->handler, 'handle']); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Server/WorkerHandler/WorkerErrorHandlerInterface.php: -------------------------------------------------------------------------------- 1 | taskworker) { 19 | * echo "Hello from task worker process"; 20 | * } 21 | * ``` 22 | */ 23 | public function handle(Server $worker, int $workerId): void; 24 | } 25 | -------------------------------------------------------------------------------- /src/Server/WorkerHandler/WorkerStartHandlerInterface.php: -------------------------------------------------------------------------------- 1 | taskworker) { 19 | * echo "Hello from task worker process"; 20 | * } 21 | * ``` 22 | */ 23 | public function handle(Server $worker, int $workerId): void; 24 | } 25 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Resetter/CountingResetter.php: -------------------------------------------------------------------------------- 1 | counter; 20 | $this->decorated->reset($service); 21 | } 22 | 23 | public function getCounter(): int 24 | { 25 | return $this->counter; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithWorkerErrorHandler.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 17 | } 18 | 19 | /** 20 | * {@inheritdoc} 21 | */ 22 | public function configure(Server $server): void 23 | { 24 | $server->on('WorkerError', [$this->handler, 'handle']); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/Command/ParametersHelperTrait.php: -------------------------------------------------------------------------------- 1 | parameterBag->get('kernel.project_dir'); 17 | 18 | if (!\is_string($projectDir)) { 19 | throw new \UnexpectedValueException('Invalid project directory.'); 20 | } 21 | 22 | return $projectDir; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Logging/InMemoryLogger.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | private static $messages = []; 13 | 14 | public static function logMessage(string $message): void 15 | { 16 | self::$messages[] = $message; 17 | print_r($message.PHP_EOL); 18 | } 19 | 20 | /** 21 | * @return array 22 | */ 23 | public static function getAndClear(): array 24 | { 25 | $messages = self::$messages; 26 | 27 | return $messages; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Message/SleepAndAppend.php: -------------------------------------------------------------------------------- 1 | fileName; 19 | } 20 | 21 | public function getSleepMs(): int 22 | { 23 | return $this->sleepMs; 24 | } 25 | 26 | public function getAppend(): string 27 | { 28 | return $this->append; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Container/Proxy/ContextualProxy.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function getServicePool(): ServicePool; 18 | 19 | /** 20 | * @param ServicePool $servicePool 21 | * 22 | * @return ContextualProxy&RealObjectType 23 | */ 24 | public static function staticProxyConstructor(ServicePool $servicePool): object; 25 | } 26 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/ReplacedContentTestController.php.tmpl: -------------------------------------------------------------------------------- 1 | 'text/plain']); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Server/Configurator/CallableChainConfigurator.php: -------------------------------------------------------------------------------- 1 | $configurators 13 | */ 14 | public function __construct(private iterable $configurators) 15 | { 16 | } 17 | 18 | /** 19 | * {@inheritdoc} 20 | */ 21 | public function configure(Server $server): void 22 | { 23 | /** @var callable $configurator */ 24 | foreach ($this->configurators as $configurator) { 25 | $configurator($server); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/ReplacedContentTestController.php: -------------------------------------------------------------------------------- 1 | 'text/plain']); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: kind/feature, priority/backlog 6 | assignees: k911 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/DependencyInjection/CompilerPass/OverrideDoctrineCompilerPass.php: -------------------------------------------------------------------------------- 1 | setParameter('doctrine.class', ResetCountingRegistry::class); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/messenger/swoole.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | messenger: 3 | enabled: true 4 | transports: 5 | swoole: swoole://task 6 | routing: 7 | 'K911\Swoole\Tests\Fixtures\Symfony\TestBundle\Message\CreateFileMessage': swoole 8 | swoole: 9 | task_worker: 10 | settings: 11 | worker_count: auto 12 | 13 | services: 14 | _defaults: 15 | autowire: true 16 | autoconfigure: true 17 | public: false 18 | 19 | 'K911\Swoole\Tests\Fixtures\Symfony\TestBundle\Controller\TaskController': 20 | tags: 21 | - controller.service_arguments 22 | 23 | 'K911\Swoole\Tests\Fixtures\Symfony\TestBundle\MessageHandler\CreateFileMessageHandler': 24 | tags: 25 | - messenger.message_handler 26 | -------------------------------------------------------------------------------- /src/Bridge/Doctrine/ServicePooledRepositoryFactory.php: -------------------------------------------------------------------------------- 1 | decorated->getRepository($this->pooledEm, $entityName); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /phpstan.tests.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | inferPrivatePropertyTypeFromConstructor: true 3 | checkMissingIterableValueType: false 4 | excludePaths: 5 | - tests/Fixtures/Symfony/app/var 6 | - tests/Fixtures/Symfony/app/TestAppKernel 7 | 8 | # Internal error: Expected to find an ancestor with class name Swoole\Timer on Swoole\Server, but none was found. 9 | - tests/Unit/Server/SwooleHttpServerDummy.php 10 | - tests/Unit/Server/SwooleServerMock.php 11 | - tests/Unit/Server/Php8/SwooleServerMock.php 12 | - tests/Unit/Server/SwooleHttpServerMock.php 13 | ignoreErrors: 14 | # Put false positives here 15 | 16 | # Symfony configuration files 17 | - '#Variable \$container might not be defined#' 18 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/InMemoryRepository.php: -------------------------------------------------------------------------------- 1 | storedValue) { 17 | throw new \RuntimeException('Repository was not reset.'); 18 | } 19 | 20 | $this->storedValue = $test; 21 | } 22 | 23 | public function reset(): void 24 | { 25 | $this->storedValue = null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/ExceptionHandler/TestCustomExceptionHandler.php: -------------------------------------------------------------------------------- 1 | header(Http::HEADER_CONTENT_TYPE, Http::CONTENT_TYPE_TEXT_PLAIN); 17 | $response->status(500); 18 | $response->end('Very custom exception handler'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/ErrorHandler/ExceptionHandlerFactory.php: -------------------------------------------------------------------------------- 1 | kernel, 22 | $request, 23 | $this->throwableHandler 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/DependencyInjection/CompilerPass/StatefulServices/NonSharedSvcPoolConfigurator.php: -------------------------------------------------------------------------------- 1 | $servicePool 18 | */ 19 | public function configure(BaseServicePool $servicePool): void 20 | { 21 | $this->container->addPool($servicePool); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpKernel/SimpleKernelPool.php: -------------------------------------------------------------------------------- 1 | kernel->boot(); 18 | } 19 | 20 | public function get(): KernelInterface 21 | { 22 | return $this->kernel; 23 | } 24 | 25 | /** 26 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 27 | */ 28 | public function return(KernelInterface $kernel): void 29 | { 30 | // no need to be implemented 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/MessageHandler/CreateFileMessageHandler.php: -------------------------------------------------------------------------------- 1 | fileName(), '\\/'); 16 | $result = file_put_contents($filePath, $message->content()); 17 | Assertion::true(false !== $result, 'Could not create test file.'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithTaskHandler.php: -------------------------------------------------------------------------------- 1 | configuration->getTaskWorkerCount() > 0) { 25 | $server->on('task', [$this->handler, 'handle']); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/EventDispatcher/EventDispatchingServerStartHandler.php: -------------------------------------------------------------------------------- 1 | eventDispatcher->dispatch(new ServerStartedEvent($server), ServerStartedEvent::NAME); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Server/RequestHandler/ExceptionHandler/ProductionExceptionHandler.php: -------------------------------------------------------------------------------- 1 | header(Http::HEADER_CONTENT_TYPE, Http::CONTENT_TYPE_TEXT_PLAIN); 18 | $response->status(500); 19 | $response->end(self::ERROR_MESSAGE); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/CoverageBundle/EventListeners/CoverageStartOnConsoleCommandEventListener.php: -------------------------------------------------------------------------------- 1 | getCommand()->getName()); 19 | $this->coverageManager->start(sprintf('test_cmd_%s', $slug)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/TidewaysController.php: -------------------------------------------------------------------------------- 1 | Profiler::$wasStarted, 26 | 'stopped' => Profiler::$wasStopped, 27 | ]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Client/Http.php: -------------------------------------------------------------------------------- 1 | eventDispatcher->dispatch(new WorkerErrorEvent($server, $workerId), WorkerErrorEvent::NAME); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/EventDispatcher/EventDispatchingWorkerExitHandler.php: -------------------------------------------------------------------------------- 1 | eventDispatcher->dispatch(new WorkerExitedEvent($server, $workerId), WorkerExitedEvent::NAME); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/EventDispatcher/EventDispatchingWorkerStopHandler.php: -------------------------------------------------------------------------------- 1 | eventDispatcher->dispatch(new WorkerStoppedEvent($server, $workerId), WorkerStoppedEvent::NAME); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/EventDispatcher/EventDispatchingWorkerStartHandler.php: -------------------------------------------------------------------------------- 1 | eventDispatcher->dispatch(new WorkerStartedEvent($server, $workerId), WorkerStartedEvent::NAME); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/monolog.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'type' => 'stream', 11 | 'path' => '%kernel.logs_dir%/%kernel.environment%.log', 12 | 'level' => 'debug', 13 | 'channels' => ['!event'], 14 | ], 15 | ]; 16 | 17 | $container->addResource(new ClassExistenceResource(Application::class)); 18 | if (\class_exists(Application::class)) { 19 | $handlers['console'] = [ 20 | 'type' => 'console', 21 | 'process_psr_3_messages' => false, 22 | 'channels' => ['!event', '!console'], 23 | ]; 24 | } 25 | 26 | $container->loadFromExtension('monolog', [ 27 | 'handlers' => $handlers, 28 | ]); 29 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Messenger/ContextReleasingTransportHandler.php: -------------------------------------------------------------------------------- 1 | coWrapper->defer(); 27 | $this->decorated->handle($server, $task); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Server/Api/ApiServerClientFactory.php: -------------------------------------------------------------------------------- 1 | sockets->hasApiSocket(), 'Swoole HTTP Server is not configured properly. To access API trough HTTP interface, you must enable and provide proper address of configured API Server.'); 20 | 21 | return new ApiServerClient(HttpClient::fromSocket( 22 | $this->sockets->getApiSocket(), 23 | $options 24 | )); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Server/Configurator/CallableChainConfiguratorFactory.php: -------------------------------------------------------------------------------- 1 | map(function ($configurator): callable { 17 | Assertion::isInstanceOf($configurator, ConfiguratorInterface::class); 18 | 19 | return [$configurator, 'configure']; 20 | }) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithTaskFinishedHandler.php: -------------------------------------------------------------------------------- 1 | configuration->getTaskWorkerCount() > 0) { 25 | $server->on('finish', [$this->handler, 'handle']); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/CoverageBundle/EventListeners/CoverageFinishOnConsoleTerminate.php: -------------------------------------------------------------------------------- 1 | coverageManager->stop(); 19 | 20 | $slug = str_replace(['-', ':'], '_', $commandEvent->getCommand()->getName()); 21 | $this->coverageManager->finish(sprintf('test_cmd_%s', $slug)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/DependencyInjection/CompilerPass/SleepingCounterCompileProcessor.php: -------------------------------------------------------------------------------- 1 | proxifyService(SleepingCounter::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Bridge/Doctrine/ORM/EntityManagerStabilityChecker.php: -------------------------------------------------------------------------------- 1 | isOpen(); 20 | } 21 | 22 | public static function getSupportedClass(): string 23 | { 24 | return EntityManager::class; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Messenger/ServiceResettingTransportHandler.php: -------------------------------------------------------------------------------- 1 | resetter->reset(); 27 | $this->decorated->handle($server, $task); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/BlackfireController.php: -------------------------------------------------------------------------------- 1 | \BlackfireProbe::wasStarted(), /* @phpstan-ignore-line */ 25 | 'stopped' => \BlackfireProbe::wasStopped(), /* @phpstan-ignore-line */ 26 | ]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/DependencyInjection/CompilerPass/StatefulServices/StatefulServiceTag.php: -------------------------------------------------------------------------------- 1 | tag['limit'] ?? null; 19 | } 20 | 21 | public function getResetter(): ?string 22 | { 23 | return $this->tag['resetter'] ?? null; 24 | } 25 | 26 | public function getResetOnEachRequest(): ?bool 27 | { 28 | return $this->tag['reset_on_each_request'] ?? null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Container/ServicePool/ServicePoolContainer.php: -------------------------------------------------------------------------------- 1 | > $pools 11 | */ 12 | public function __construct(private array $pools) 13 | { 14 | } 15 | 16 | /** 17 | * @param ServicePool $pool 18 | */ 19 | public function addPool(ServicePool $pool): void 20 | { 21 | $this->pools[] = $pool; 22 | } 23 | 24 | public function releaseFromCoroutine(int $cId): void 25 | { 26 | foreach ($this->pools as $pool) { 27 | $pool->releaseFromCoroutine($cId); 28 | } 29 | } 30 | 31 | public function count(): int 32 | { 33 | return count($this->pools); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Bridge/Upscale/Blackfire/Monitoring/Apm.php: -------------------------------------------------------------------------------- 1 | injector = $injector; 19 | $this->middlewareFactory = $middlewareFactory; 20 | } 21 | 22 | /** 23 | * Install monitoring instrumentation. 24 | */ 25 | public function instrument(Server $server): void 26 | { 27 | $this->injector->injectMiddlevare($server, $this->middlewareFactory); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Client/Exception/ClientConnectionErrorException.php: -------------------------------------------------------------------------------- 1 | proxyDirExists) { 22 | return; 23 | } 24 | 25 | if ($this->fileSystem->exists($this->proxyDir)) { 26 | $this->proxyDirExists = true; 27 | 28 | return; 29 | } 30 | 31 | $this->fileSystem->mkdir($this->proxyDir); 32 | $this->proxyDirExists = true; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/IndexController.php: -------------------------------------------------------------------------------- 1 | 'world!'], 200); 21 | } 22 | 23 | /** 24 | * @Route( 25 | * methods={"GET"}, 26 | * path="/dummy-sleep" 27 | * ) 28 | */ 29 | public function sleep(): JsonResponse 30 | { 31 | \co::sleep(2); 32 | 33 | return new JsonResponse(['hello' => 'world!'], 200); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Bridge/Doctrine/DBAL/ConnectionKeepAliveResetter.php: -------------------------------------------------------------------------------- 1 | aliveKeeper->keepAlive($service, $this->connectionName); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/ResponseProcessor.php: -------------------------------------------------------------------------------- 1 | sendfile($httpFoundationResponse->getFile()->getRealPath()); 20 | } else { 21 | $swooleResponse->end($httpFoundationResponse->getContent()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Container/Proxy/Instantiator.php: -------------------------------------------------------------------------------- 1 | $servicePool 19 | * @param class-string $wrappedSvcClass 20 | * 21 | * @return ContextualProxy&RealObjectType 22 | */ 23 | public function newInstance(ServicePool $servicePool, string $wrappedSvcClass): object 24 | { 25 | return $this->proxyGenerator->createProxy( 26 | $servicePool, 27 | $wrappedSvcClass 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Entity/AdvancedTest.php: -------------------------------------------------------------------------------- 1 | uuid; 35 | } 36 | 37 | public function increment(): int 38 | { 39 | return ++$this->counter; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/DecorationTestDummyService.php: -------------------------------------------------------------------------------- 1 | decorated->process(); 16 | } 17 | 18 | public function getDecorated(): DummyService 19 | { 20 | return $this->decorated; 21 | } 22 | 23 | /** 24 | * this method has to be here because SF container decorating logic leaves removes the kernel.reset tag 25 | * from the decorated service and adds it to the decorating service. 26 | */ 27 | public function reset(): void 28 | { 29 | $this->decorated->reset(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/exception_handler_custom/swoole.yaml: -------------------------------------------------------------------------------- 1 | swoole: 2 | http_server: 3 | exception_handler: 4 | type: custom 5 | handler_id: test_bundle.custom.exception_handler 6 | 7 | services: 8 | _defaults: 9 | autowire: true 10 | autoconfigure: true 11 | public: false 12 | 13 | test_bundle.custom.exception_handler: 14 | alias: K911\Swoole\Tests\Fixtures\Symfony\TestBundle\ExceptionHandler\TestCustomExceptionHandler 15 | 16 | K911\Swoole\Tests\Fixtures\Symfony\TestBundle\ExceptionHandler\TestCustomExceptionHandler: {} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/ErrorHandler/ErrorResponder.php: -------------------------------------------------------------------------------- 1 | handlerFactory->newExceptionHandler($request); 22 | $this->errorHandler->setExceptionHandler($exceptionHandler); 23 | $this->errorHandler->handleException($throwable); 24 | 25 | return $exceptionHandler->getResponse(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Feature/BlackfireProfilerRegisteredTest.php: -------------------------------------------------------------------------------- 1 | 'dev']); 19 | $kernel->boot(); 20 | 21 | $container = $kernel->getContainer(); 22 | $testContainer = $container->get('test.service_container'); 23 | 24 | self::assertTrue($testContainer->has(Profiler::class)); 25 | self::assertTrue($testContainer->has(WithProfiler::class)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Entity/Test.php: -------------------------------------------------------------------------------- 1 | uuid = $uuid->toString(); 34 | } 35 | 36 | public function getId(): int 37 | { 38 | return $this->id; 39 | } 40 | 41 | public function getUuid(): string 42 | { 43 | return $this->uuid; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Server/RequestHandler/ExceptionRequestHandler.php: -------------------------------------------------------------------------------- 1 | decorated->handle($request, $response); 26 | } catch (\Throwable $exception) { 27 | $this->exceptionHandler->handle($request, $exception, $response); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/NoOpStreamedResponseProcessor.php: -------------------------------------------------------------------------------- 1 | decorated->process($httpFoundationResponse, $swooleResponse); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpKernel/CoroutineKernelPool.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | private array $kernels = []; 15 | 16 | public function __construct(private KernelInterface $kernel) 17 | { 18 | } 19 | 20 | public function boot(): void 21 | { 22 | $this->kernel->boot(); 23 | } 24 | 25 | public function get(): KernelInterface 26 | { 27 | if (empty($this->kernels)) { 28 | return clone $this->kernel; 29 | } 30 | 31 | return array_shift($this->kernels); 32 | } 33 | 34 | public function return(KernelInterface $kernel): void 35 | { 36 | $this->kernels[] = $kernel; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/AdvancedDoctrineUsage.php: -------------------------------------------------------------------------------- 1 | doctrine->getManager(); 22 | $newEntity = new AdvancedTest($this->uuidFactory->uuid4()); 23 | 24 | for ($i = 0; $i < 10; ++$i) { 25 | $incr = $newEntity->increment(); 26 | $em->persist($newEntity); 27 | $em->flush(); 28 | } 29 | 30 | return $incr; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpKernel/ContextReleasingHttpKernelRequestHandler.php: -------------------------------------------------------------------------------- 1 | coWrapper->defer(); 28 | $this->decorated->handle($request, $response); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Container/Proxy/Generation/MethodForwarderBuilder.php: -------------------------------------------------------------------------------- 1 | getDeclaringClass()->getName(), $method->getName()), 18 | $servicePoolHolderProperty 19 | ); 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/ThrowableController.php: -------------------------------------------------------------------------------- 1 | setMiddleware($this->wrap($server->getMiddleware(), $this->profiler)); 22 | } 23 | 24 | /** 25 | * Decorate a given middleware for profiling. 26 | */ 27 | private function wrap(callable $middleware, Profiler $profiler): ProfilerDecorator 28 | { 29 | return new ProfilerDecorator($middleware, $profiler); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/run-feature-tests-code-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | EXIT_CODE=0 4 | MAX_TRIES=5 5 | 6 | TEST_NO=0 7 | for f in ./tests/Feature/*.php; do 8 | ((TEST_NO++)) 9 | echo "[Test $TEST_NO] $f"; 10 | 11 | TEST_EXIT_CODE=0 12 | for ((TRY_NO=1; TRY_NO <= MAX_TRIES; TRY_NO++)); do 13 | echo "[Test $TEST_NO] Try $TRY_NO of $MAX_TRIES"; 14 | 15 | vendor/bin/phpunit "$f" --coverage-php "cov/feature-tests-$TEST_NO.cov" --colors=always 16 | TEST_EXIT_CODE=$? 17 | 18 | # Make sure server is killed for next test 19 | PID=$(lsof -t -i :9999) 20 | if [[ "" != "$PID" ]]; then 21 | kill -9 "$PID" || true 22 | sleep 1; 23 | fi 24 | 25 | if [[ "$TEST_EXIT_CODE" = "0" ]]; then 26 | break; 27 | fi 28 | sleep 1; 29 | done 30 | 31 | if [[ "$TEST_EXIT_CODE" != "0" ]]; then 32 | EXIT_CODE=1; 33 | fi 34 | 35 | done 36 | 37 | exit ${EXIT_CODE} 38 | -------------------------------------------------------------------------------- /src/Server/LifecycleHandler/SigIntHandler.php: -------------------------------------------------------------------------------- 1 | signalInterrupt = \defined('SIGINT') ? (int) \constant('SIGINT') : 2; 17 | } 18 | 19 | /** 20 | * {@inheritdoc} 21 | */ 22 | public function handle(Server $server): void 23 | { 24 | // 2 => SIGINT 25 | Process::signal($this->signalInterrupt, function () use ($server) { 26 | $server->stop(); 27 | $server->shutdown(); 28 | }); 29 | 30 | if ($this->decorated instanceof ServerStartHandlerInterface) { 31 | $this->decorated->handle($server); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Messenger/SwooleServerTaskReceiver.php: -------------------------------------------------------------------------------- 1 | configuration->isReactorRunningMode()) { 26 | return; 27 | } 28 | 29 | $server->on('start', [$this->handler, 'handle']); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/MessageHandler/RunDummyHandler.php: -------------------------------------------------------------------------------- 1 | logger->info('Run dummy start', [ 22 | 'time' => time(), 23 | 'coroutine_id' => \Co::getCid(), 24 | ]); 25 | $this->dummyService->process(); 26 | $this->logger->info('Run dummy end', [ 27 | 'time' => time(), 28 | 'coroutine_id' => \Co::getCid(), 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/swoole-blackfire.md: -------------------------------------------------------------------------------- 1 | # Swoole Server Blackfire integration 2 | 3 | Blackfire (https://blackfire.io/docs/) is a profiler for PHP applications. 4 | 5 | By default, blackfire does not work with Swoole Server, however, thanks to the work of [https://github.com/upscalesoftware/swoole-blackfire](https://github.com/upscalesoftware/swoole-blackfire) the Swoole server can be instrumented to produce data for blackfire. 6 | 7 | ## How to use? 8 | 9 | First of all, setup blackfire following their docs [https://blackfire.io/docs/up-and-running/installation](https://blackfire.io/docs/up-and-running/installation) 10 | 11 | Then, install the swoole-blackfire library 12 | 13 | ```shell script 14 | composer require upscale/swoole-blackfire 15 | ``` 16 | 17 | That's it! The bundle will automatically detect that the library was installed and it will instrument the server. 18 | 19 | If, for some reason, you want to explicitly disable the profiler, you can do so from the bundle configuration, see [here](configuration-reference.md) -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerStart.php: -------------------------------------------------------------------------------- 1 | codeCoverageManager->start('test_server'); 25 | 26 | if ($this->decorated instanceof ServerStartHandlerInterface) { 27 | $this->decorated->handle($server); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/SetRequestRuntimeConfiguration.php: -------------------------------------------------------------------------------- 1 | exceptionArrayTransformer->transform($exception, $this->verbosity); 23 | 24 | $response->header(Http::HEADER_CONTENT_TYPE, Http::CONTENT_TYPE_APPLICATION_JSON); 25 | $response->status(500); 26 | $response->end(json_encode($data, \JSON_THROW_ON_ERROR)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Unit/Component/AtomicCounter/AtomicCounterTest.php: -------------------------------------------------------------------------------- 1 | get()); 16 | } 17 | 18 | public function testIncrement(): void 19 | { 20 | $atomicSpy = new AtomicSpy(); 21 | self::assertFalse($atomicSpy->incremented); 22 | 23 | $counter = new AtomicCounter($atomicSpy); 24 | $counter->increment(); 25 | 26 | self::assertTrue($atomicSpy->incremented); 27 | } 28 | 29 | public function testGet(): void 30 | { 31 | $count = 10; 32 | $counter = new AtomicCounter(new AtomicStub($count)); 33 | 34 | self::assertSame($count, $counter->get()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Service/SleepingCounterChecker.php: -------------------------------------------------------------------------------- 1 | wasChecked = true; 22 | ++$this->checks; 23 | 24 | return true; 25 | } 26 | 27 | public function wasChecked(): bool 28 | { 29 | return $this->wasChecked; 30 | } 31 | 32 | public function getChecks(): int 33 | { 34 | return $this->checks; 35 | } 36 | 37 | public static function getSupportedClass(): string 38 | { 39 | return SleepingCounter::class; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Messenger/ExceptionLoggingTransportHandler.php: -------------------------------------------------------------------------------- 1 | decorated->handle($server, $task); 26 | } catch (\Throwable $e) { 27 | $this->logger->critical( 28 | sprintf('Task worker exception: %s', $e->getMessage()), 29 | [ 30 | 'exception' => $e, 31 | ] 32 | ); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | plugins: 3 | shellcheck: 4 | enabled: true 5 | 6 | duplication: 7 | enabled: true 8 | config: 9 | languages: 10 | - php 11 | 12 | # rules: https://github.com/squizlabs/PHP_CodeSniffer/tree/master/src/Standards 13 | phpcodesniffer: 14 | enabled: true 15 | checks: 16 | Generic Files LineLength TooLong: 17 | enabled: false 18 | 19 | # rules: https://phpmd.org/rules/index.html 20 | phpmd: 21 | enabled: true 22 | checks: 23 | Naming/LongVariable: 24 | enabled: false 25 | Naming/ShortVariable: 26 | enabled: false 27 | CleanCode/StaticAccess: 28 | enabled: false 29 | 30 | # rules: https://github.com/SonarSource/sonar-php/tree/master/php-checks/src/main/resources/org/sonar/l10n/php/rules/php 31 | sonar-php: 32 | enabled: true 33 | 34 | exclude_patterns: 35 | - tests 36 | - src/Bridge/Symfony/Bundle/DependencyInjection 37 | # TODO: Remove when part of package (not internal) 38 | - src/Client/HttpClient.php 39 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/ErrorHandler/ResponseDelayingExceptionHandler.php: -------------------------------------------------------------------------------- 1 | response = $this->throwableHandler->invoke( 25 | $this->kernel, 26 | $e, 27 | $this->request, 28 | HttpKernelInterface::MAIN_REQUEST 29 | ); 30 | } 31 | 32 | public function getResponse(): ?Response 33 | { 34 | return $this->response; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerManagerStart.php: -------------------------------------------------------------------------------- 1 | codeCoverageManager->start('test_manager'); 25 | 26 | if ($this->decorated instanceof ServerManagerStartHandlerInterface) { 27 | $this->decorated->handle($server); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/TwigController.php: -------------------------------------------------------------------------------- 1 | logger->error('Profiler logging test.'); 34 | 35 | return new Response($this->environment->render('base.html.twig')); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Messenger/SwooleServerTaskTransportHandler.php: -------------------------------------------------------------------------------- 1 | data, Envelope::class); 24 | /* @var $data Envelope */ 25 | $data = $task->data; 26 | $this->bus->dispatch($data); 27 | 28 | if ($this->decorated instanceof TaskHandlerInterface) { 29 | $this->decorated->handle($server, $task); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Unit/Client/HttpClientTest.php: -------------------------------------------------------------------------------- 1 | 1]; 18 | 19 | $client = HttpClient::fromDomain($host, $port, $ssl, $options); 20 | 21 | $expected = [ 22 | 'host' => $host, 23 | 'port' => $port, 24 | 'ssl' => $ssl, 25 | 'options' => $options, 26 | ]; 27 | 28 | self::assertSame($expected, $client->__serialize()); 29 | 30 | $serializedClient = \serialize($client); 31 | $unserializedClient = \unserialize($serializedClient, ['allowed_classes' => [HttpClient::class]]); 32 | 33 | self::assertInstanceOf(HttpClient::class, $unserializedClient); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/ResponseProcessorInjector.php: -------------------------------------------------------------------------------- 1 | attributes->set( 22 | self::ATTR_KEY_RESPONSE_PROCESSOR, 23 | function (HttpFoundationResponse $httpFoundationResponse) use ($swooleResponse): void { 24 | $this->responseProcessor->process($httpFoundationResponse, $swooleResponse); 25 | } 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Server/WorkerHandler/HMRWorkerStartHandler.php: -------------------------------------------------------------------------------- 1 | decorated instanceof WorkerStartHandlerInterface) { 25 | $this->decorated->handle($worker, $workerId); 26 | } 27 | 28 | if ($worker->taskworker) { 29 | return; 30 | } 31 | 32 | $worker->tick($this->interval, function () use ($worker): void { 33 | $this->hmr->tick($worker); 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerWorkerStart.php: -------------------------------------------------------------------------------- 1 | codeCoverageManager->start(sprintf('test_worker_%d', $workerId)); 25 | 26 | if ($this->decorated instanceof WorkerStartHandlerInterface) { 27 | $this->decorated->handle($worker, $workerId); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Messenger/SwooleServerTaskSender.php: -------------------------------------------------------------------------------- 1 | last(SentStamp::class); 26 | $alias = null === $sentStamp ? 'swoole-task' : $sentStamp->getSenderAlias() ?? $sentStamp->getSenderClass(); 27 | 28 | $this->httpServer->dispatchTask($envelope->with(new ReceivedStamp($alias))); 29 | 30 | return $envelope; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Migrations/Version20220920150015.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE test (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:guid)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB'); 24 | } 25 | 26 | public function down(Schema $schema): void 27 | { 28 | // this down() migration is auto-generated, please modify it to your needs 29 | $this->addSql('DROP TABLE test'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerShutdown.php: -------------------------------------------------------------------------------- 1 | decorated instanceof ServerShutdownHandlerInterface) { 25 | $this->decorated->handle($server); 26 | } 27 | 28 | $this->codeCoverageManager->stop(); 29 | $this->codeCoverageManager->finish('test_server'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Migrations/Version20230117125728.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE advanced_test (uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:guid)\', counter INT NOT NULL, PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB'); 24 | } 25 | 26 | public function down(Schema $schema): void 27 | { 28 | // this down() migration is auto-generated, please modify it to your needs 29 | $this->addSql('DROP TABLE advanced_test'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Server/Configurator/WithHttpServerConfiguration.php: -------------------------------------------------------------------------------- 1 | set($this->configuration->getSwooleSettings()); 22 | 23 | $defaultSocket = $this->configuration->getServerSocket(); 24 | if (0 === $defaultSocket->port()) { 25 | $this->configuration->changeServerSocket($defaultSocket->withPort($server->port)); 26 | } 27 | 28 | $maxConcurrency = $this->configuration->getMaxConcurrency(); 29 | 30 | if (null === $maxConcurrency) { 31 | return; 32 | } 33 | 34 | \Co::set(['max_concurrency' => $maxConcurrency]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Feature/BlackfireMonitoringRegisteredTest.php: -------------------------------------------------------------------------------- 1 | deleteVarDirectory(); 17 | } 18 | 19 | /** 20 | * Ensure that WithProfiler and Profiler are registered. 21 | */ 22 | public function testWiring(): void 23 | { 24 | $kernel = static::createKernel(['environment' => 'blackfire_monitoring']); 25 | $kernel->boot(); 26 | 27 | $container = $kernel->getContainer(); 28 | $testContainer = $container->get('test.service_container'); 29 | 30 | self::assertTrue($testContainer->has(Apm::class)); 31 | self::assertTrue($testContainer->has(WithApm::class)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerManagerStop.php: -------------------------------------------------------------------------------- 1 | decorated instanceof ServerManagerStopHandlerInterface) { 25 | $this->decorated->handle($server); 26 | } 27 | 28 | $this->codeCoverageManager->stop(); 29 | $this->codeCoverageManager->finish('test_manager'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/MessageHandler/SleepAndAppendHandler.php: -------------------------------------------------------------------------------- 1 | logger->info('Sleep and append start', [ 19 | 'time' => time(), 20 | 'coroutine_id' => \Co::getCid(), 21 | 'text' => $message->getAppend(), 22 | ]); 23 | usleep($message->getSleepMs() * 1000); 24 | file_put_contents($message->getFileName(), $message->getAppend().PHP_EOL, FILE_APPEND); 25 | $this->logger->info('Sleep and append end', [ 26 | 'time' => time(), 27 | 'coroutine_id' => \Co::getCid(), 28 | 'text' => $message->getAppend(), 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Bridge/Tideways/Apm/ProfilingMiddleware.php: -------------------------------------------------------------------------------- 1 | nextMiddleware = \Closure::fromCallable($nextMiddleware); 21 | } 22 | 23 | public function __invoke(Request $request, Response $response): void 24 | { 25 | if (!class_exists(Profiler::class) || 'cli' !== php_sapi_name()) { 26 | // only run when Tideways is installed and the CLI sapi is used (that is how Swoole works) 27 | call_user_func($this->nextMiddleware, $request, $response); 28 | 29 | return; 30 | } 31 | 32 | $this->profiler->profile($this->nextMiddleware, $request, $response); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Server/Session/StorageInterface.php: -------------------------------------------------------------------------------- 1 | id, $task->worker_id, bin2hex(random_bytes(4))); 25 | $this->codeCoverageManager->start($testName); 26 | 27 | $this->decorated->handle($server, $task); 28 | 29 | $this->codeCoverageManager->stop(); 30 | $this->codeCoverageManager->finish($testName); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Messenger/SwooleServerTaskTransportFactory.php: -------------------------------------------------------------------------------- 1 | server) 23 | ); 24 | } 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function supports(string $dsn, array $options): bool 30 | { 31 | return 0 === mb_strpos($dsn, 'swoole://task'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Server/Runtime/CallableBootManager.php: -------------------------------------------------------------------------------- 1 | $bootables 16 | */ 17 | public function __construct(private iterable $bootables, private bool $booted = false) 18 | { 19 | } 20 | 21 | /** 22 | * {@inheritdoc} 23 | * 24 | * Method MUST be called directly before Swoole server start. 25 | * 26 | * @throws \Assert\AssertionFailedException When already booted 27 | */ 28 | public function boot(array $runtimeConfiguration = []): void 29 | { 30 | Assertion::false($this->booted, 'Boot method has already been called. Cannot boot services multiple times.'); 31 | $this->booted = true; 32 | 33 | /** @var callable $bootable */ 34 | foreach ($this->bootables as $bootable) { 35 | $bootable($runtimeConfiguration); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 K911 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Container/Proxy/Generation/MethodGenerator/GetWrappedServicePoolValue.php: -------------------------------------------------------------------------------- 1 | setBody('return $this->'.$servicePoolHolderProperty->getName().';'); 27 | $this->setReturnType(ServicePool::class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/CoverageBundle/RequestHandler/CodeCoverageRequestHandler.php: -------------------------------------------------------------------------------- 1 | codeCoverageManager->start($testName); 27 | 28 | $this->decorated->handle($request, $response); 29 | 30 | $this->codeCoverageManager->stop(); 31 | $this->codeCoverageManager->finish($testName); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/config/doctrine.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | 'env(DATABASE_HOST)': 'db' 3 | 4 | doctrine: 5 | dbal: 6 | default_connection: default 7 | connections: 8 | default: 9 | # coroutines don't work well with sqlite because it is blocking 10 | driver: 'pdo_mysql' 11 | charset: utf8 12 | user: user 13 | password: pass 14 | host: '%env(DATABASE_HOST)%' 15 | port: 3306 16 | dbname: db 17 | orm: 18 | default_entity_manager: default 19 | auto_generate_proxy_classes: '%kernel.debug%' 20 | entity_managers: 21 | default: 22 | connection: default 23 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 24 | auto_mapping: true 25 | mappings: 26 | App: 27 | is_bundle: false 28 | type: annotation 29 | # this is fake just for the sake of running the tests independent of doctrine relation 30 | dir: '%kernel.project_dir%/../TestBundle/Resources/mapping' 31 | prefix: 'K911\Swoole\Tests\Fixtures\Symfony\TestBundle\Entity' 32 | alias: TestBundle 33 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/TaskController.php: -------------------------------------------------------------------------------- 1 | get('fileName', 'test-default-file.txt'); 26 | $content = $request->get('content', (new \DateTimeImmutable())->format(\DATE_ATOM)); 27 | $message = new CreateFileMessage($fileName, $content); 28 | $bus->dispatch($message); 29 | 30 | return new Response('OK', 200, ['Content-Type' => 'text/plain']); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Container/ServicePool/UnmanagedFactoryServicePool.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class UnmanagedFactoryServicePool extends BaseServicePool 16 | { 17 | /** 18 | * @var \Closure(): T 19 | */ 20 | private \Closure $instantiator; 21 | 22 | /** 23 | * @param \Closure(): T $instantiator 24 | */ 25 | public function __construct( 26 | \Closure $instantiator, 27 | Mutex $mutex, 28 | int $instancesLimit = 50, 29 | ?Resetter $resetter = null 30 | ) { 31 | $this->instantiator = $instantiator; 32 | 33 | parent::__construct($mutex, $instancesLimit, $resetter); 34 | } 35 | 36 | /** 37 | * @return T 38 | */ 39 | protected function newServiceInstance(): object 40 | { 41 | $instantiator = $this->instantiator; 42 | 43 | return $instantiator(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Server/Api/WithApiServerConfiguration.php: -------------------------------------------------------------------------------- 1 | sockets->hasApiSocket()) { 26 | return; 27 | } 28 | 29 | $apiSocketPort = $this->sockets->getApiSocket()->port(); 30 | foreach ($server->ports as $port) { 31 | if ($port->port === $apiSocketPort) { 32 | $port->on('request', [$this->requestHandler, 'handle']); 33 | 34 | return; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Server/Api/ApiServerClient.php: -------------------------------------------------------------------------------- 1 | client->send('/api/server')['response']['body']; 22 | } 23 | 24 | /** 25 | * Shutdown Swoole HTTP Server. 26 | */ 27 | public function shutdown(): void 28 | { 29 | $this->client->send('/api/server', Http::METHOD_DELETE); 30 | } 31 | 32 | /** 33 | * Reload Swoole HTTP Server workers. 34 | */ 35 | public function reload(): void 36 | { 37 | $this->client->send('/api/server', Http::METHOD_PATCH); 38 | } 39 | 40 | /** 41 | * Get Swoole HTTP Server metrics. 42 | */ 43 | public function metrics(): array 44 | { 45 | return $this->client->send('/api/server/metrics')['response']['body']; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Server/Runtime/CallableBootManagerFactory.php: -------------------------------------------------------------------------------- 1 | filter(function ($bootable) use ($isAlreadyRegistered): bool { 25 | Assertion::isInstanceOf($bootable, BootableInterface::class); 26 | 27 | return $isAlreadyRegistered(spl_object_id($bootable)); 28 | }) 29 | ->map(fn (BootableInterface $bootable): callable => [$bootable, 'boot']) 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Unit/Functions/TestObject.php: -------------------------------------------------------------------------------- 1 | privateProp = $value; 25 | $this->protectedProp = $value; 26 | $this->publicProp = $value; 27 | $this->dynamicProp = $value; 28 | } 29 | 30 | public function getPrivateProp(): string 31 | { 32 | return $this->privateProp; 33 | } 34 | 35 | public function getProtectedProp(): string 36 | { 37 | return $this->protectedProp; 38 | } 39 | 40 | public function getPublicProp(): string 41 | { 42 | return $this->publicProp; 43 | } 44 | 45 | public function getDynamicProp(): string 46 | { 47 | return $this->dynamicProp; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Bridge/Upscale/Blackfire/Monitoring/MonitoringMiddleware.php: -------------------------------------------------------------------------------- 1 | nextMiddleware = \Closure::fromCallable($nextMiddleware); 20 | $this->monitoring = $monitoring; 21 | } 22 | 23 | public function __invoke(Request $request, Response $response): void 24 | { 25 | if (!class_exists(\BlackfireProbe::class) || 'cli' !== php_sapi_name()) { 26 | // only run when Blackfire is installed and the CLI sapi is used (that is how Swoole works) 27 | call_user_func($this->nextMiddleware, $request, $response); 28 | 29 | return; 30 | } 31 | 32 | $this->monitoring->monitor($this->nextMiddleware, $request, $response); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Unit/Server/Php8/SwooleServerMock.php: -------------------------------------------------------------------------------- 1 | taskworker = $taskworker; 19 | } 20 | 21 | public function tick(int $ms, callable $callback, ...$params): int|bool 22 | { 23 | $this->registeredTick = true; 24 | $this->registeredTickTuple = [$ms, $callback]; 25 | 26 | return true; 27 | } 28 | 29 | public static function make(bool $taskworker = false): self 30 | { 31 | if (!self::$instance instanceof self) { 32 | self::$instance = new self($taskworker); 33 | } 34 | 35 | self::$instance->clean(); 36 | 37 | return self::$instance; 38 | } 39 | 40 | private function clean(): void 41 | { 42 | $this->registeredTick = false; 43 | $this->registeredTickTuple = []; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/StreamedResponseProcessor.php: -------------------------------------------------------------------------------- 1 | write($payload); 28 | } 29 | 30 | return ''; 31 | }, $this->bufferOutputSize); 32 | $httpFoundationResponse->sendContent(); 33 | ob_end_clean(); 34 | $swooleResponse->end(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Messenger/SwooleServerTaskTransport.php: -------------------------------------------------------------------------------- 1 | sender->send($envelope); 24 | } 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function get(): iterable 30 | { 31 | return $this->receiver->get(); 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function ack(Envelope $envelope): void 38 | { 39 | $this->receiver->ack($envelope); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function reject(Envelope $envelope): void 46 | { 47 | $this->receiver->reject($envelope); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/EventsController.php: -------------------------------------------------------------------------------- 1 | $this->eventHandler->isServerStarted(), 28 | 'workerStarted' => $this->eventHandler->isWorkerStarted(), 29 | 'workerStopped' => $this->eventHandler->isWorkerStopped(), 30 | 'workerExited' => $this->eventHandler->isWorkerExited(), 31 | 'workerError' => $this->eventHandler->isWorkerError(), 32 | ], 33 | 200 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: kind/bug/possible, priority/important 6 | assignees: k911 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Steps To Reproduce** 14 | Steps to reproduce the buggy behavior, for example script to execute in bash/zsh or link to gist/repository with buggy PHP code 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Environment (please complete the following information):** 20 | - OS: [e.g. macOS, Linux] 21 | - PHP Version: [e.g. 7.2.19] 22 | - Swoole Version: [e.g. 4.4.1] 23 | - Symfony Version: [e.g. 4.2.10] 24 | - Running in docker: Yes/No 25 | 26 | **Additional context** 27 | Add any other context about the problem here. For example if your app is running in Docker container, please provide source of Dockerfile or it's name when it is accessible in any public docker registry. 28 | 29 | **Logs** 30 | If possible/applicable please provide symfony/swoole logs here. 31 | 32 |
Symfony/Swoole Logs 33 |

34 | 35 | ``` 36 | # PASTE LOG LINES HERE 37 | ``` 38 | 39 |

40 |
41 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/AccessLogOnKernelTerminate.php: -------------------------------------------------------------------------------- 1 | isMainRequest()) { 23 | return; 24 | } 25 | 26 | $message = $this->formatter->format(new AccessLogDataMap($event->getRequest(), $event->getResponse())); 27 | $this->accessLogLogger->info($message); 28 | } 29 | 30 | public static function getSubscribedEvents(): array 31 | { 32 | return [ 33 | KernelEvents::TERMINATE => ['onKernelTerminate', -2048], 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/CloudFrontRequestFactory.php: -------------------------------------------------------------------------------- 1 | decorated->make($request); 24 | if ($httpFoundationRequest->headers->has('cloudfront_forwarded_proto')) { 25 | /** @var string|string[] $cloudFrontForwardedProto */ 26 | $cloudFrontForwardedProto = $httpFoundationRequest->headers->get('cloudfront_forwarded_proto'); 27 | $httpFoundationRequest->headers->set('x_forwarded_proto', $cloudFrontForwardedProto); 28 | } 29 | 30 | return $httpFoundationRequest; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Unit/Functions/GetObjectPropertyTest.php: -------------------------------------------------------------------------------- 1 | testObject = new TestObject(TestObject::GOOD_VALUE); 21 | } 22 | 23 | public function testGetPublicProperty(): void 24 | { 25 | self::assertSame(TestObject::GOOD_VALUE, get_object_property($this->testObject, 'publicProp')); 26 | } 27 | 28 | public function testGetProtectedProperty(): void 29 | { 30 | self::assertSame(TestObject::GOOD_VALUE, get_object_property($this->testObject, 'protectedProp')); 31 | } 32 | 33 | public function testGetPrivateProperty(): void 34 | { 35 | self::assertSame(TestObject::GOOD_VALUE, get_object_property($this->testObject, 'privateProp')); 36 | } 37 | 38 | public function testGetDynamicProperty(): void 39 | { 40 | self::assertSame(TestObject::GOOD_VALUE, get_object_property($this->testObject, 'dynamicProp')); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/SwooleRequestResponseContextManager.php: -------------------------------------------------------------------------------- 1 | attributes->set(static::REQUEST_ATTR_KEY, $swooleRequest); 22 | $request->attributes->set(static::RESPONSE_ATTR_KEY, $swooleResponse); 23 | } 24 | 25 | public function findRequest( 26 | HttpFoundationRequest $request 27 | ): SwooleRequest { 28 | return $request->attributes->get(static::REQUEST_ATTR_KEY); 29 | } 30 | 31 | public function findResponse( 32 | HttpFoundationRequest $request 33 | ): SwooleResponse { 34 | return $request->attributes->get(static::RESPONSE_ATTR_KEY); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Unit/Server/Configurator/WithRequestHandlerTest.php: -------------------------------------------------------------------------------- 1 | requestHandlerDummy = new RequestHandlerDummy(); 30 | 31 | $this->configurator = new WithRequestHandler($this->requestHandlerDummy); 32 | } 33 | 34 | public function testConfigure(): void 35 | { 36 | $serverMock = SwooleHttpServerMock::make(); 37 | 38 | $this->configurator->configure($serverMock); 39 | 40 | self::assertTrue($serverMock->registeredEvent); 41 | self::assertSame(['request', [$this->requestHandlerDummy, 'handle']], $serverMock->registeredEventPair); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/TestBundle/Controller/SessionController.php: -------------------------------------------------------------------------------- 1 | has('luckyNumber')) { 23 | $session->set('luckyNumber', random_int(1, 1000000)); 24 | } 25 | 26 | $metadata = $session->getMetadataBag(); 27 | 28 | return new JsonResponse([ 29 | 'hello' => 'world!', 30 | 'sessionMetadata' => [ 31 | 'created_at' => $metadata->getCreated(), 32 | 'updated_at' => $metadata->getLastUsed(), 33 | 'lifetime' => $metadata->getLifetime(), 34 | ], 35 | 'luckyNumber' => $session->get('luckyNumber'), 36 | ], 200); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/HttpFoundation/RequestFactory.php: -------------------------------------------------------------------------------- 1 | server, \CASE_UPPER); 18 | 19 | // Add formatted headers to server 20 | foreach ($request->header as $key => $value) { 21 | $server['HTTP_'.mb_strtoupper(str_replace('-', '_', $key))] = $value; 22 | } 23 | 24 | $queryString = $server['QUERY_STRING'] ?? ''; 25 | $server['REQUEST_URI'] ??= ''; 26 | $server['REQUEST_URI'] .= '' !== $queryString ? '?'.$queryString : ''; 27 | $rawContent = $request->rawContent(); 28 | 29 | return new HttpFoundationRequest( 30 | $request->get ?? [], 31 | $request->post ?? [], 32 | [], 33 | $request->cookie ?? [], 34 | $request->files ?? [], 35 | $server, 36 | false === $rawContent ? '' : $rawContent 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Unit/Bridge/Symfony/HttpFoundation/SetRequestRuntimeConfigurationTest.php: -------------------------------------------------------------------------------- 1 | configuration = new SetRequestRuntimeConfiguration(); 21 | } 22 | 23 | public function testBoot(): void 24 | { 25 | $configuration = [ 26 | 'trustedHosts' => ['127.0.0.1', 'localhost'], 27 | 'trustedProxies' => ['192.168.1.0/24', '73.41.22.1', 'varnish'], 28 | 'trustedHeaderSet' => Request::HEADER_X_FORWARDED_AWS_ELB, 29 | ]; 30 | 31 | $this->configuration->boot($configuration); 32 | 33 | self::assertSame(['{127.0.0.1}i', '{localhost}i'], Request::getTrustedHosts()); 34 | 35 | self::assertSame($configuration['trustedProxies'], Request::getTrustedProxies()); 36 | self::assertSame($configuration['trustedHeaderSet'], Request::getTrustedHeaderSet()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Bridge/Symfony/Bundle/DependencyInjection/CompilerPass/StatefulServices/FinalClassesProcessor.php: -------------------------------------------------------------------------------- 1 | setCacheDir($container); 19 | } 20 | 21 | public function process(string $className): void 22 | { 23 | if (isset($this->processedClasses[$className])) { 24 | return; 25 | } 26 | 27 | $this->processedClasses[$className] = true; 28 | FinalClassModifier::removeFinalFlagsFromClass($className); 29 | FinalClassModifier::dumpCache($this->cacheDir); 30 | } 31 | 32 | private function setCacheDir(ContainerBuilder $container): void 33 | { 34 | $cacheDir = $container->getParameter('kernel.cache_dir'); 35 | 36 | if (!\is_string($cacheDir)) { 37 | throw new \RuntimeException('Kernel cache directory is not a string.'); 38 | } 39 | 40 | $this->cacheDir = $cacheDir; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Unit/Server/Configurator/WithWorkerExitHandlerTest.php: -------------------------------------------------------------------------------- 1 | noOpWorkerExitHandler = new NoOpWorkerExitHandler(); 30 | 31 | $this->configurator = new WithWorkerExitHandler($this->noOpWorkerExitHandler); 32 | } 33 | 34 | public function testConfigure(): void 35 | { 36 | $swooleServerOnEventSpy = SwooleHttpServerMock::make(); 37 | 38 | $this->configurator->configure($swooleServerOnEventSpy); 39 | 40 | self::assertTrue($swooleServerOnEventSpy->registeredEvent); 41 | self::assertSame(['WorkerExit', [$this->noOpWorkerExitHandler, 'handle']], $swooleServerOnEventSpy->registeredEventPair); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/Server/Configurator/WithWorkerStopHandlerTest.php: -------------------------------------------------------------------------------- 1 | noOpWorkerStopHandler = new NoOpWorkerStopHandler(); 30 | 31 | $this->configurator = new WithWorkerStopHandler($this->noOpWorkerStopHandler); 32 | } 33 | 34 | public function testConfigure(): void 35 | { 36 | $swooleServerOnEventSpy = SwooleHttpServerMock::make(); 37 | 38 | $this->configurator->configure($swooleServerOnEventSpy); 39 | 40 | self::assertTrue($swooleServerOnEventSpy->registeredEvent); 41 | self::assertSame(['WorkerStop', [$this->noOpWorkerStopHandler, 'handle']], $swooleServerOnEventSpy->registeredEventPair); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Fixtures/Symfony/app/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'test'); 19 | $overrideProdEnv = $input->getParameterOption(['--override-prod-env', '-o'], $_SERVER['OVERRIDE_PROD_ENV'] ?? null); 20 | $debug = ($_SERVER['APP_DEBUG'] ?? ('prod' !== $env)) && !$input->hasParameterOption(['--no-debug', '']); 21 | 22 | if ($debug) { 23 | \umask(0000); 24 | 25 | if (\class_exists(Debug::class)) { 26 | Debug::enable(); 27 | } 28 | } 29 | 30 | $argv = $_SERVER['argv']; 31 | $argv = array_filter($argv, fn (string $arg): bool => strpos($arg, '--override-prod-env') === false && strpos($arg, '-o ') === false); 32 | $input = new ArgvInput($argv); 33 | 34 | $kernel = new TestAppKernel($env, $debug, $overrideProdEnv); 35 | $application = new Application($kernel); 36 | $application->run($input); 37 | --------------------------------------------------------------------------------