├── VERSION ├── Resources ├── doc │ └── .gitkeep └── config │ └── swoole-bridge.yaml ├── Tests └── DependencyInjection │ └── Compiler │ └── .gitkeep ├── .gitignore ├── phpunit.xml.dist ├── DependencyInjection ├── Compiler │ └── AddProcessorsPass.php ├── Configuration.php └── SwooleBridgeExtension.php ├── LICENSE ├── SwooleBridgeBundle.php ├── composer.json ├── CHANGES ├── Command └── SwooleBridgeServerCommand.php ├── bumpversion.sh └── README.md /VERSION: -------------------------------------------------------------------------------- 1 | 2.1.1 2 | -------------------------------------------------------------------------------- /Resources/doc/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/DependencyInjection/Compiler/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Composer 2 | composer.lock 3 | /vendor/ 4 | #Editor 5 | .idea/ 6 | #Unit Test 7 | phpunit.xml -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | ./tests 9 | 10 | 11 | 12 | 13 | 14 | ./src 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /DependencyInjection/Compiler/AddProcessorsPass.php: -------------------------------------------------------------------------------- 1 | hasDefinition('swoole_bridge.handler')) { 17 | return; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 insidestyles 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 | -------------------------------------------------------------------------------- /SwooleBridgeBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new AddProcessorsPass()); 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function getContainerExtension(): ?ExtensionInterface 37 | { 38 | return new SwooleBridgeExtension(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "insidestyles/swoole-bridge-bundle", 3 | "description": "Swoole Bridge For Php Frameworks", 4 | "type": "symfony-bundle", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "fuong", 9 | "email": "insidestyles@gmail.com" 10 | } 11 | ], 12 | "prefer-stable": true, 13 | "require": { 14 | "php": ">=7.2", 15 | "symfony/framework-bundle": "^5.0|^6.0", 16 | "insidestyles/swoole-bridge": "^1.0", 17 | "symfony/psr-http-message-bridge": "^1.0|^2.0", 18 | "symfony/console": "^5.0|^6.0", 19 | "laminas/laminas-diactoros": "^2.0", 20 | "nyholm/psr7": "^1.1" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": ">=6.5", 24 | "eaglewu/swoole-ide-helper": "dev-master" 25 | }, 26 | "suggest": { 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Insidestyles\\SwooleBridgeBundle\\": "" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "": "Tests/DependencyInjection" 36 | } 37 | }, 38 | "extra": { 39 | "symfony": { 40 | "allow-contrib": "true" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | Version 2.1.1: 2 | - small change 3 | 4 | Version 2.1.0: 5 | - Merge branch '2.x' 6 | - custom configs 7 | 8 | Version 2.0.0: 9 | - init 2.0 10 | 11 | Version 1.1.1: 12 | - Merge pull request #4 from Edwolfbear/master 13 | - updated for symfony 5 14 | 15 | Version 1.1.0: 16 | - update composer for symfony 5 17 | 18 | Version 1.0.0: 19 | - fix Invalid stream reference provided 20 | - Update README.md 21 | - Merge pull request #2 from insidestyles/fix/missing_command_base_class 22 | - extend base Symfony Command class 23 | 24 | Version 0.4.1: 25 | - doc change 26 | - Merge pull request #1 from dzikismigol/master 27 | - add CLI command tag 28 | - use DI in favor of injecting container in CLI command 29 | - fx 30 | - update doc 31 | - example doc for handle mysql server has run away 32 | - example doc for handle mysql server has run away 33 | - Update README.md 34 | 35 | Version 0.4.0: 36 | 37 | 38 | Version 0.3.3: 39 | - change type 40 | 41 | Version 0.3.2: 42 | - allow contrib 43 | 44 | Version 0.3.1: 45 | - revert optional log 46 | 47 | Version 0.3.0: 48 | - require logger 49 | 50 | Version 0.2.0: 51 | - update swoole-bridge 0.3 52 | - update readme 53 | 54 | Version 0.1.0 55 | - Init 56 | - Initial commit 57 | 58 | -------------------------------------------------------------------------------- /Command/SwooleBridgeServerCommand.php: -------------------------------------------------------------------------------- 1 | setName('swoole:bridge:server') 28 | ->setDescription('Start swoole server'); 29 | } 30 | 31 | protected function execute(InputInterface $input, OutputInterface $output): void 32 | { 33 | $http = new Server($this->host, $this->port); 34 | $http->set($this->configs); 35 | $http->on('start', function (Server $server) use ($output): void { 36 | $output->writeln("Server started on {$this->host}:{$this->port}").PHP_EOL; 37 | }); 38 | $http->on( 39 | 'request', 40 | function (Request $request, Response $response) { 41 | $this->handler->handle($request, $response); 42 | } 43 | ); 44 | 45 | $http->start(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | getRootNode(); 23 | 24 | $rootNode 25 | ->children() 26 | ->arrayNode('server') 27 | ->children() 28 | ->scalarNode('host') 29 | ->example('127.0.0.1') 30 | ->cannotBeEmpty() 31 | ->isRequired() 32 | ->end() 33 | ->scalarNode('port') 34 | ->example(8080) 35 | ->cannotBeEmpty() 36 | ->isRequired() 37 | ->end() 38 | ->booleanNode('origin_check') 39 | ->defaultValue(false) 40 | ->example(true) 41 | ->end() 42 | ->arrayNode('configs') 43 | ->useAttributeAsKey('code') 44 | ->prototype('scalar') 45 | ->end() 46 | ->end() 47 | ->end() 48 | ->end() 49 | ->end(); 50 | 51 | return $treeBuilder; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /DependencyInjection/SwooleBridgeExtension.php: -------------------------------------------------------------------------------- 1 | getConfiguration($configs, $container); 24 | $configs = $this->processConfiguration($configuration, $configs); 25 | if (isset($configs['server'])) { 26 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); 27 | $loader->load('swoole-bridge.yaml'); 28 | 29 | $container->setParameter('swoole_bridge.server.host', $configs['server']['host']); 30 | $container->setParameter('swoole_bridge.server.port', $configs['server']['port']); 31 | $container->setParameter('swoole_bridge.server.configs', $configs['server']['configs'] ?? []); 32 | 33 | if (!empty($configs['server']['origins'])) { 34 | //@todo add origin check 35 | } 36 | } 37 | } 38 | 39 | /** 40 | * Register handler instance 41 | * 42 | * @param string $handlerKey 43 | * @param array $handlerInfo 44 | * @param ContainerBuilder $container 45 | * 46 | * @return string The handler service id 47 | */ 48 | private function registerHandler($handlerKey, array $handlerInfo, ContainerBuilder $container) 49 | { 50 | $handlerId = 'swoole_bridge.handler.' . $handlerKey; 51 | 52 | return $handlerId; 53 | } 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | public function getAlias(): string 59 | { 60 | return self::ALIAS; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /bumpversion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # works with a file called VERSION in the current directory, 4 | # the contents of which should be a semantic version number 5 | # such as "1.2.3" 6 | 7 | # this script will display the current version, automatically 8 | # suggest a "minor" version update, and ask for input to use 9 | # the suggestion, or a newly entered value. 10 | 11 | # once the new version number is determined, the script will 12 | # pull a list of changes from git history, prepend this to 13 | # a file called CHANGES (under the title of the new version 14 | # number) and create a GIT tag. 15 | 16 | if [ -f VERSION ]; then 17 | BASE_STRING=`cat VERSION` 18 | BASE_LIST=(`echo $BASE_STRING | tr '.' ' '`) 19 | V_MAJOR=${BASE_LIST[0]} 20 | V_MINOR=${BASE_LIST[1]} 21 | V_PATCH=${BASE_LIST[2]} 22 | echo "Current version : $BASE_STRING" 23 | V_MINOR=$((V_MINOR + 1)) 24 | V_PATCH=0 25 | SUGGESTED_VERSION="$V_MAJOR.$V_MINOR.$V_PATCH" 26 | read -p "Enter a version number [$SUGGESTED_VERSION]: " INPUT_STRING 27 | if [ "$INPUT_STRING" = "" ]; then 28 | INPUT_STRING=$SUGGESTED_VERSION 29 | fi 30 | echo "Will set new version to be $INPUT_STRING" 31 | echo $INPUT_STRING > VERSION 32 | echo "Version $INPUT_STRING:" > tmpfile 33 | git log --pretty=format:" - %s" "v$BASE_STRING"...HEAD >> tmpfile 34 | echo "" >> tmpfile 35 | echo "" >> tmpfile 36 | cat CHANGES >> tmpfile 37 | mv tmpfile CHANGES 38 | git add CHANGES VERSION 39 | git commit -m "Version bump to $INPUT_STRING" 40 | git tag -a -m "Tagging version $INPUT_STRING" "v$INPUT_STRING" 41 | git push origin --tags 42 | else 43 | echo "Could not find a VERSION file" 44 | read -p "Do you want to create a version file and start from scratch? [y]" RESPONSE 45 | if [ "$RESPONSE" = "" ]; then RESPONSE="y"; fi 46 | if [ "$RESPONSE" = "Y" ]; then RESPONSE="y"; fi 47 | if [ "$RESPONSE" = "Yes" ]; then RESPONSE="y"; fi 48 | if [ "$RESPONSE" = "yes" ]; then RESPONSE="y"; fi 49 | if [ "$RESPONSE" = "YES" ]; then RESPONSE="y"; fi 50 | if [ "$RESPONSE" = "y" ]; then 51 | echo "0.1.0" > VERSION 52 | echo "Version 0.1.0" > CHANGES 53 | git log --pretty=format:" - %s" >> CHANGES 54 | echo "" >> CHANGES 55 | echo "" >> CHANGES 56 | git add VERSION CHANGES 57 | git commit -m "Added VERSION and CHANGES files, Version bump to v0.1.0" 58 | git tag -a -m "Tagging version 0.1.0" "v0.1.0" 59 | git push origin --tags 60 | fi 61 | 62 | fi -------------------------------------------------------------------------------- /Resources/config/swoole-bridge.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | 3 | swoole_bridge.handler.class: Insidestyles\SwooleBridge\Handler 4 | swoole_bridge.adapter.symfony.class: Insidestyles\SwooleBridge\Adapter\SymfonyAdapter 5 | swoole_bridge.factory.request_builder.class: Insidestyles\SwooleBridge\Builder\RequestBuilderFactory 6 | swoole_bridge.emitter.class: Insidestyles\SwooleBridge\Emiter\SwooleResponseEmitter 7 | swoole_bridge.psr15_symfony_kernel.class: Insidestyles\SwooleBridge\Adapter\Kernel\Psr15SymfonyKernel 8 | 9 | services: 10 | 11 | swoole_bridge.emitter: 12 | class: "%swoole_bridge.emitter.class%" 13 | 14 | swoole_bridge.factory.request_builder: 15 | class: "%swoole_bridge.factory.request_builder.class%" 16 | 17 | Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory: ~ 18 | Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface: '@Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory' 19 | 20 | swoole_bridge.factory.psr17: 21 | class: Nyholm\Psr7\Factory\Psr17Factory 22 | Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory: 23 | class: Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory 24 | arguments: 25 | - "@swoole_bridge.factory.psr17" 26 | - "@swoole_bridge.factory.psr17" 27 | - "@swoole_bridge.factory.psr17" 28 | - "@swoole_bridge.factory.psr17" 29 | Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface: '@Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory' 30 | swoole_bridge.psr15_symfony_kernel: 31 | class: "%swoole_bridge.psr15_symfony_kernel.class%" 32 | autowire: true 33 | 34 | swoole_bridge.adapter.symfony: 35 | class: "%swoole_bridge.adapter.symfony.class%" 36 | arguments: 37 | - "@swoole_bridge.emitter" 38 | - "@swoole_bridge.psr15_symfony_kernel" 39 | - "@swoole_bridge.factory.request_builder" 40 | Insidestyles\SwooleBridge\Adapter\SymfonyAdapter: '@swoole_bridge.adapter.symfony' 41 | 42 | swoole_bridge.handler: 43 | class: "%swoole_bridge.handler.class%" 44 | arguments: 45 | - "@swoole_bridge.adapter.symfony" 46 | - "@?logger" 47 | Insidestyles\SwooleBridge\Handler: '@swoole_bridge.handler' 48 | 49 | swoole_bridge.server_command: 50 | class: Insidestyles\SwooleBridgeBundle\Command\SwooleBridgeServerCommand 51 | arguments: 52 | - "@swoole_bridge.handler" 53 | - "%swoole_bridge.server.host%" 54 | - "%swoole_bridge.server.port%" 55 | - "%swoole_bridge.server.configs%" 56 | tags: 57 | - { name: console.command } 58 | Insidestyles\SwooleBridgeBundle\Command\SwooleBridgeServerCommand: '@swoole_bridge.server_command' 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swoole-bridge-bundle 2 | Symfony Swoole Bridge Bundle 3 | 4 | ## Requirements 5 | 6 | * PHP >= 7.1 7 | * symfony/framework-bundle: "^5.0|^6.0" 8 | * insidestyles/swoole-bridge: "^1.0" 9 | * symfony/console: "^5.0|^6.0", 10 | * "laminas/laminas-diactoros": "^2.0" 11 | 12 | 13 | ## Installation 14 | 15 | This package is installable and autoloadable via Composer 16 | 17 | ```sh 18 | composer require insidestyles/swoole-bridge-bundle 19 | ``` 20 | Update config.yml 21 | ```yaml 22 | swoole_bridge: 23 | server: 24 | port: "%web_server_port%" #The port the socket server will listen on 25 | host: "%web_server_host%" 26 | configs: 27 | document_root: '/app/public' 28 | enable_static_handler: true 29 | http_compression: true 30 | http_compression_level: 1 31 | ``` 32 | Update bundles 33 | ```php 34 | Insidestyles\SwooleBridgeBundle\SwooleBridgeBundle::class => ['all' => true], 35 | ``` 36 | 37 | ## Usage 38 | 39 | ```sh 40 | php bin/console swoole:bridge:server 41 | ``` 42 | 43 | ## Create custom handler 44 | One of common problems while running long lived program in php is "MySQL server has gone away" (Long lived php 45 | daemon running using reactphp, websocket, swoole, ..etc..). 46 | In this case we can create a custom handler to handle this error. For example: 47 | 48 | ```php 49 | class CustomHandler implements SwooleBridgeInterface 50 | { 51 | /** 52 | * @var SwooleBridgeInterface 53 | */ 54 | private $swooleBridge; 55 | 56 | /** 57 | * @var RegistryInterface 58 | */ 59 | private $doctrineRegistry; 60 | 61 | /** 62 | * @var LoggerInterface 63 | */ 64 | private $logger; 65 | 66 | /** 67 | * Handler constructor. 68 | * @param SwooleBridgeInterface $swooleBridge 69 | * @param null|LoggerInterface $logger 70 | */ 71 | public function __construct( 72 | SwooleBridgeInterface $swooleBridge, 73 | RegistryInterface $doctrineRegistry, 74 | ?LoggerInterface $logger = null 75 | ) { 76 | $this->swooleBridge = $swooleBridge; 77 | $this->doctrineRegistry = $doctrineRegistry; 78 | $this->logger = $logger ?? new NullLogger(); 79 | } 80 | 81 | /** 82 | * @inheritdoc 83 | */ 84 | public function handle( 85 | SwooleRequest $swooleRequest, 86 | SwooleResponse $swooleResponse 87 | ): void { 88 | try { 89 | $this->swooleBridge->handle($swooleRequest, $swooleResponse); 90 | } catch (PDOException $e) { 91 | $this->logger->error($e->getMessage()); 92 | /** @var Connection $connection */ 93 | $connection = $this->doctrineRegistry->getConnection(); 94 | if (!$connection->ping()) { 95 | $connection->close(); 96 | $connection->connect(); 97 | } 98 | $em = $this->doctrineRegistry->getEntityManager(); 99 | if (!$em->isOpen()){ 100 | $this->doctrineRegistry->resetManager(); 101 | } 102 | } catch (\Throwable $e) { 103 | $this->logger->error($e->getMessage()); 104 | } 105 | } 106 | } 107 | 108 | ``` 109 | 110 | Override default handler using symfony service decorator: 111 | 112 | ```yml 113 | #config/services.yaml 114 | services: 115 | swoole_bridge.custom_handler: 116 | class: App\CustomHandler 117 | decorates: swoole_bridge.handler 118 | arguments: 119 | - '@swoole_bridge.custom_handler.inner' 120 | - '@doctrine' 121 | - '@logger' 122 | --------------------------------------------------------------------------------