├── examples ├── servers │ ├── http1-static-content.moc │ ├── websocket.php │ ├── tcp1.php │ ├── http2.php │ ├── udp.php │ ├── tcp2.php │ ├── mixed-protocols-1.php │ ├── redis.php │ ├── keepalive.php │ ├── websocket-integrated.php │ ├── ddos-protection.php │ ├── apcu-caching.php │ ├── interruptible-sleep.php │ ├── rock-paper-scissors.php │ ├── http1.php │ ├── heartbeat.php │ ├── http1-integrated.php │ └── server-events.php ├── io │ ├── blocking-io.php │ ├── non-blocking-io.php │ ├── non-blocking-io-debug.php │ ├── blocking-vs-non-blocking.php │ ├── block-a-coroutine.php │ ├── block-a-process-using-swoole-lock.php │ ├── block-processes-using-swoole-lock.php │ └── block-processes-using-swoole-atomic.php ├── clients │ ├── websocket.php │ ├── udp.php │ ├── tcp.php │ └── http1.php ├── csp │ ├── defer.php │ ├── scheduling │ │ ├── non-preemptive.php │ │ ├── preemptive.php │ │ └── mixed.php │ ├── coroutines │ │ ├── yield-and-resume.php │ │ ├── nested-debug.php │ │ ├── nested.php │ │ ├── for.php │ │ ├── exit.php │ │ ├── creation-1.php │ │ ├── creation-2.php │ │ └── benchmark.php │ ├── deadlocks │ │ ├── coroutine-yielded-1.php │ │ ├── coroutine-yielded-2.php │ │ ├── an-empty-channel.php │ │ ├── channel-is-full.php │ │ ├── coroutine-yielded-3.php │ │ ├── swoole-lock.php │ │ ├── server-shutdown.php │ │ └── file-locking.php │ ├── barrier.php │ ├── waitgroup.php │ ├── channel.php │ └── context.php ├── pool │ ├── process-pool │ │ ├── pool-tcp-socket.php │ │ ├── pool-unix-socket.php │ │ ├── pool-msgqueue.php │ │ ├── pool-standalone.php │ │ └── client.php │ └── database-pool │ │ ├── redis.php │ │ ├── mysqli.php │ │ └── pdo_pgsql.php ├── timer │ ├── coroutine-style.php │ └── timer.php ├── hooks │ ├── pdo_mysql.php │ ├── mysqli.php │ ├── pdo_sqlite.php │ ├── curl.php │ ├── pdo_pgsql.php │ ├── redis │ │ ├── phpredis.php │ │ └── predis.php │ ├── native-curl.php │ └── hook-flags.php ├── locks │ ├── lock-across-processes.php │ ├── lock-across-coroutines.php │ └── lock-across-threads.php └── misc │ ├── atomic-counter-signed-64-bit.php │ └── atomic-counter-unsigned-32-bit.php ├── .github ├── dependabot.yml └── workflows │ ├── coding_style_checks.yml │ ├── static_analysis.yml │ └── build_docker_images.yml ├── .gitignore ├── dockerfiles ├── server │ ├── rootfilesystem │ │ └── etc │ │ │ └── supervisor │ │ │ └── service.d │ │ │ ├── udp.conf │ │ │ ├── http1.conf │ │ │ ├── http2.conf │ │ │ ├── redis.conf │ │ │ ├── tcp1.conf │ │ │ ├── tcp2.conf │ │ │ ├── keepalive.conf │ │ │ ├── websocket.conf │ │ │ ├── apcu-caching.conf │ │ │ ├── ddos-protection.conf │ │ │ ├── http1-integrated.conf │ │ │ ├── mixed-protocols-1.conf │ │ │ ├── pool-msgqueue.conf │ │ │ ├── pool-tcp-socket.conf │ │ │ ├── interruptible-sleep.conf │ │ │ ├── pool-unix-socket.conf │ │ │ ├── rock-paper-scissors.conf │ │ │ └── websocket-integrated.conf │ └── Dockerfile └── client │ └── Dockerfile ├── composer.json ├── docker-compose.yml ├── phpstan.neon.dist ├── .php-cs-fixer.dist.php ├── README.md └── LICENSE.txt /examples/servers/http1-static-content.moc: -------------------------------------------------------------------------------- 1 | This text file is for testing serving static content under HTTP/1. Please check script http1.php for details. 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Composer files 2 | /composer.lock 3 | /vendor/ 4 | 5 | # Ignore PHPStan files. 6 | /phpstan.neon 7 | 8 | # Some examples will download files from Internet. This is to ignore those downloaded files. 9 | /examples/clients/*.png 10 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/udp.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:udp] 5 | command = /var/www/servers/udp.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/http1.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:http1] 5 | command = /var/www/servers/http1.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/http2.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:http2] 5 | command = /var/www/servers/http2.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/redis.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:redis] 5 | command = /var/www/servers/redis.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/tcp1.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:tcp1] 5 | command = /var/www/servers/tcp1.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/tcp2.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:tcp2] 5 | command = /var/www/servers/tcp2.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/keepalive.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:keepalive] 5 | command = /var/www/servers/keepalive.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/websocket.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:websocket] 5 | command = /var/www/servers/websocket.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/apcu-caching.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:apcu-caching] 5 | command = /var/www/servers/apcu-caching.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/ddos-protection.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:ddos-protection] 5 | command = /var/www/servers/ddos-protection.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/http1-integrated.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:http1-integrated] 5 | command = /var/www/servers/http1-integrated.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/mixed-protocols-1.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:mixed-protocols-1] 5 | command = /var/www/servers/mixed-protocols-1.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/pool-msgqueue.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:pool-msgqueue] 5 | command = /var/www/pool/process-pool/pool-msgqueue.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/pool-tcp-socket.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:pool-tcp-socket] 5 | command = /var/www/pool/process-pool/pool-tcp-socket.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/interruptible-sleep.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:interruptible-sleep] 5 | command = /var/www/servers/interruptible-sleep.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/pool-unix-socket.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:pool-unix-socket] 5 | command = /var/www/pool/process-pool/pool-unix-socket.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/rock-paper-scissors.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:rock-paper-scissors] 5 | command = /var/www/servers/rock-paper-scissors.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /dockerfiles/server/rootfilesystem/etc/supervisor/service.d/websocket-integrated.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user = root 3 | 4 | [program:websocket-integrated] 5 | command = /var/www/servers/websocket-integrated.php 6 | user = root 7 | autostart = true 8 | autorestart = true 9 | stdout_logfile=/proc/self/fd/1 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/proc/self/fd/1 12 | stderr_logfile_maxbytes=0 13 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deminy/swoole-by-examples", 3 | "description": "Go through different features in Swoole by examples.", 4 | "type": "project", 5 | "homepage": "https://github.com/deminy/swoole-by-examples", 6 | "license": "CC-BY-NC-ND-4.0", 7 | "require": { 8 | "php": ">=8.2" 9 | }, 10 | "require-dev": { 11 | "predis/predis": "~3.0", 12 | "swoole/ide-helper": ">=5.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/coding_style_checks.yml: -------------------------------------------------------------------------------- 1 | name: Coding Style Checks 2 | 3 | on: [ push, pull_request, workflow_dispatch ] 4 | 5 | jobs: 6 | coding-style-checks: 7 | runs-on: ubuntu-22.04 8 | 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | 13 | - name: Run Coding Style Checks 14 | run: docker run -q --rm -v "$(pwd):/project" -w /project -i jakzal/phpqa:php8.3-alpine php-cs-fixer fix -q --dry-run 15 | -------------------------------------------------------------------------------- /examples/io/blocking-io.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on( 14 | 'message', 15 | function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame): void { 16 | $server->push($frame->fd, "Hello, {$frame->data}!"); 17 | } 18 | ); 19 | $server->start(); 20 | -------------------------------------------------------------------------------- /dockerfiles/client/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile is to build client-side images, e.g., "deminy/swoole-by-examples:client-5.1". 2 | # @see https://hub.docker.com/r/deminy/swoole-by-examples 3 | 4 | ARG SWOOLE_VERSION=6.0 5 | ARG PHP_VERSION=8.3 6 | 7 | FROM phpswoole/swoole:${SWOOLE_VERSION}-php${PHP_VERSION} 8 | 9 | RUN \ 10 | set -ex && \ 11 | docker-php-ext-install mysqli && \ 12 | apt-get update && \ 13 | apt-get install -y apache2-utils netcat-traditional xxd --no-install-recommends && \ 14 | curl -sfL -o /usr/bin/websocat "https://github.com/vi/websocat/releases/download/v1.12.0/websocat.$(uname -m)-unknown-linux-musl" && \ 15 | chmod 755 /usr/bin/websocat && \ 16 | rm -rf /var/lib/apt/lists/* 17 | -------------------------------------------------------------------------------- /examples/clients/websocket.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | upgrade('/'); 22 | $client->push('Swoole'); 23 | echo $client->recv()->data, PHP_EOL; // @phpstan-ignore property.nonObject 24 | $client->close(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/clients/udp.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | connect('server', 9506); 23 | $client->send('Hello Swoole!'); 24 | echo $client->recv(), PHP_EOL; // @phpstan-ignore echo.nonString 25 | $client->close(); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/servers/tcp1.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on( 22 | 'receive', 23 | function (Server $server, int $fd, int $reactorId, string $data): void { 24 | $server->send($fd, $data); 25 | } 26 | ); 27 | $server->start(); 28 | -------------------------------------------------------------------------------- /examples/csp/defer.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 19 | [ 20 | 'open_http2_protocol' => true, 21 | ] 22 | ); 23 | $server->on( 24 | 'request', 25 | function (Request $request, Response $response): void { 26 | $response->end( 27 | <<<'EOT' 28 | In this example we start an HTTP/2 server. 29 | 30 | EOT 31 | ); 32 | } 33 | ); 34 | $server->start(); 35 | -------------------------------------------------------------------------------- /examples/servers/udp.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on( 22 | 'packet', 23 | function (Server $server, string $data, array $clientInfo): void { 24 | $server->sendto($clientInfo['address'], $clientInfo['port'], $data); 25 | } 26 | ); 27 | $server->start(); 28 | -------------------------------------------------------------------------------- /examples/pool/process-pool/pool-tcp-socket.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on('message', function (Pool $pool, string $message): void { 16 | $pool->write("Hello, {$message}!"); 17 | }); 18 | $pool->on('workerStart', function (Pool $pool, int $workerId): void { 19 | echo "Process #{$workerId} started. (TCP SOCKET)", PHP_EOL; 20 | }); 21 | $pool->on('workerStop', function (Pool $pool, int $workerId): void { 22 | echo "Process #{$workerId} stopped. (TCP SOCKET)", PHP_EOL; 23 | }); 24 | 25 | $pool->listen('0.0.0.0', 9701); 26 | $pool->start(); 27 | -------------------------------------------------------------------------------- /examples/csp/coroutines/yield-and-resume.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on('message', function (Pool $pool, string $message): void { 16 | $pool->write("Hello, {$message}!"); 17 | }); 18 | $pool->on('workerStart', function (Pool $pool, int $workerId): void { 19 | echo "Process #{$workerId} started. (UNIX SOCKET)", PHP_EOL; 20 | }); 21 | $pool->on('workerStop', function (Pool $pool, int $workerId): void { 22 | echo "Process #{$workerId} stopped. (UNIX SOCKET)", PHP_EOL; 23 | }); 24 | 25 | $pool->listen('unix:/var/run/pool-unix-socket.sock'); 26 | $pool->start(); 27 | -------------------------------------------------------------------------------- /examples/timer/coroutine-style.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | > /usr/local/etc/php/conf.d/docker-php-ext-swoole.ini 24 | composer global require -n -q --no-progress -- \ 25 | phpstan/phpstan=~2.0 \ 26 | predis/predis=~3.0 \ 27 | swoole/library=~${{ matrix.version }} 28 | 29 | - name: Run Static Analysis 30 | run: ~/.composer/vendor/bin/phpstan analyse --no-progress --memory-limit 2G 31 | -------------------------------------------------------------------------------- /dockerfiles/server/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile is to build server-side images, e.g., "deminy/swoole-by-examples:server-5.1". 2 | # @see https://hub.docker.com/r/deminy/swoole-by-examples 3 | 4 | ARG SWOOLE_VERSION=6.0 5 | ARG PHP_VERSION=8.3 6 | 7 | FROM phpswoole/swoole:${SWOOLE_VERSION}-php${PHP_VERSION} 8 | 9 | COPY ./rootfilesystem / 10 | 11 | # 1. The System V messages support is to run the example from script "./examples/pool/process-pool/client.php". 12 | # 2. The APCu extension is to run the example from script "./examples/servers/apcu-caching.php". 13 | RUN \ 14 | set -ex && \ 15 | docker-php-ext-install sysvmsg && \ 16 | pecl channel-update pecl && \ 17 | pecl install apcu-stable && \ 18 | docker-php-ext-enable apcu && \ 19 | echo "apc.enable_cli=1" >> $(php-config --ini-dir)/docker-php-ext-apcu.ini && \ 20 | apt-get update && \ 21 | apt-get install -y net-tools watch --no-install-recommends && \ 22 | rm -rf /var/lib/apt/lists/* 23 | -------------------------------------------------------------------------------- /examples/csp/scheduling/preemptive.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on('message', function (Pool $pool, string $message): void { 17 | /** @var Process $process */ 18 | $process = $pool->getProcess(); 19 | echo "Process #{$process->id} received message \"{$message}\". (MSGQUEUE)", PHP_EOL; 20 | }); 21 | $pool->on('workerStart', function (Pool $pool, int $workerId): void { 22 | echo "Process #{$workerId} started. (MSGQUEUE)", PHP_EOL; 23 | }); 24 | $pool->on('workerStop', function (Pool $pool, int $workerId): void { 25 | echo "Process #{$workerId} stopped. (MSGQUEUE)", PHP_EOL; 26 | }); 27 | 28 | $pool->start(); 29 | -------------------------------------------------------------------------------- /examples/csp/coroutines/nested-debug.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | handle(function (Connection $conn): void { 26 | while (true) { 27 | if ($data = $conn->recv()) { 28 | $conn->send($data); 29 | } else { 30 | $conn->close(); 31 | break; 32 | } 33 | } 34 | }); 35 | $server->start(); 36 | }); 37 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | server: 3 | image: deminy/swoole-by-examples:server-6.0 4 | environment: 5 | AUTORELOAD_PROGRAMS: "apcu-caching ddos-protection http1 http1-integrated http2 interruptible-sleep keepalive mixed-protocols-1 pool-msgqueue pool-tcp-socket pool-unix-socket redis rock-paper-scissors tcp1 tcp2 udp websocket websocket-integrated" 6 | DISABLE_DEFAULT_SERVER: 1 7 | ports: 8 | - 9801:9801 9 | volumes: 10 | - ./examples:/var/www 11 | client: 12 | image: deminy/swoole-by-examples:client-6.0 13 | environment: 14 | DISABLE_DEFAULT_SERVER: 1 15 | links: 16 | - mysql 17 | - postgresql 18 | - redis 19 | - server 20 | volumes: 21 | - ./examples:/var/www 22 | redis: 23 | image: redis:8-alpine 24 | mysql: 25 | image: mysql:8 26 | environment: 27 | MYSQL_DATABASE: test 28 | MYSQL_PASSWORD: password 29 | MYSQL_ROOT_PASSWORD: password 30 | MYSQL_USER: username 31 | postgresql: 32 | image: postgres:17-alpine 33 | environment: 34 | POSTGRES_DB: test 35 | POSTGRES_USER: username 36 | POSTGRES_PASSWORD: password 37 | -------------------------------------------------------------------------------- /examples/csp/coroutines/for.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | false]); 21 | Coroutine::create(function (): void { 22 | echo '1', PHP_EOL; // This will be printed out. 23 | Coroutine::yield(); 24 | echo '3', PHP_EOL; // This will never be printed out. 25 | }); 26 | echo '2', PHP_EOL; // This will be printed out. 27 | 28 | // NOTE: In most cases it's not necessary nor recommended to use method `Swoole\Event::wait()` directly in your code. 29 | // The example in this file is just for demonstration purpose. 30 | Swoole\Event::wait(); 31 | -------------------------------------------------------------------------------- /examples/csp/deadlocks/an-empty-channel.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | (bool) ($argv[1] ?? true), 27 | ] 28 | ); 29 | 30 | run(function (): void { 31 | Coroutine::create(function (): void { 32 | echo '1', PHP_EOL; // This will be printed out. 33 | (new Channel(1))->pop(); 34 | echo '3', PHP_EOL; // This will never be printed out. 35 | }); 36 | echo '2', PHP_EOL; // This will be printed out. 37 | }); 38 | -------------------------------------------------------------------------------- /examples/csp/scheduling/mixed.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 19 | [ 20 | 'open_http2_protocol' => true, 21 | ] 22 | ); 23 | 24 | // HTTP/1 and HTTP/2 25 | $server->on( 26 | 'request', 27 | function (Swoole\Http\Request $request, Swoole\Http\Response $response): void { 28 | $response->end("Hello, {$request->rawContent()}" . PHP_EOL); 29 | } 30 | ); 31 | 32 | // WebSocket 33 | $server->on( 34 | 'message', 35 | function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame): void { 36 | $server->push($frame->fd, "Hello, {$frame->data}"); 37 | } 38 | ); 39 | 40 | $server->start(); 41 | -------------------------------------------------------------------------------- /examples/clients/tcp.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | connect('server', $port); 28 | $client->send("client side message to port {$port}"); 29 | echo $client->recv(), PHP_EOL; // @phpstan-ignore echo.nonString 30 | $client->close(); 31 | }); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /examples/pool/database-pool/redis.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | withHost('redis'), 32); 23 | for ($n = 1024; $n--;) { 24 | go(function () use ($pool, $n): void { 25 | $redis = $pool->get(); 26 | 27 | $key = uniqid(sprintf('test-%d-', $n)); // A unique key to avoid conflicts. 28 | $redis->set($key, 'dummy', ['EX' => 10]); // Set a key with an expiration time of 10 seconds. 29 | assert($redis->get($key) === 'dummy', 'The value stored in Redis should be "dummy".'); 30 | 31 | $pool->put($redis); 32 | }); 33 | } 34 | }); 35 | 36 | echo 'Done', PHP_EOL; 37 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 9 3 | paths: 4 | - ./examples 5 | reportUnmatchedIgnoredErrors: false 6 | ignoreErrors: 7 | - 8 | message: '#Instantiated class Swoole\\Coroutine\\Lock not found\.#' 9 | path: ./examples/locks/lock-across-coroutines.php 10 | - 11 | message: '#Call to method \w+\(\) on an unknown class Swoole\\Coroutine\\Lock\.#' 12 | path: ./examples/locks/lock-across-coroutines.php 13 | - 14 | identifier: if.alwaysTrue 15 | path: ./examples/locks/lock-across-coroutines.php 16 | - 17 | identifier: if.alwaysFalse 18 | path: ./examples/locks/lock-across-coroutines.php 19 | - 20 | identifier: deadCode.unreachable 21 | path: ./examples/locks/lock-across-coroutines.php 22 | - 23 | message: '#Instantiated class Swoole\\Thread(\\.+)? not found\.#' 24 | path: ./examples/locks/lock-across-threads.php 25 | - 26 | message: '#Call to( static)? method \w+\(\) on an unknown class Swoole\\Thread\.#' 27 | path: ./examples/locks/lock-across-threads.php 28 | - 29 | identifier: if.alwaysTrue 30 | path: ./examples/locks/lock-across-threads.php 31 | - 32 | identifier: if.alwaysFalse 33 | path: ./examples/locks/lock-across-threads.php 34 | - 35 | identifier: deadCode.unreachable 36 | path: ./examples/locks/lock-across-threads.php 37 | -------------------------------------------------------------------------------- /examples/csp/deadlocks/channel-is-full.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | (bool) ($argv[1] ?? true), 27 | ] 28 | ); 29 | 30 | run(function (): void { 31 | Coroutine::create(function (): void { 32 | $channel = new Channel(1); // Size of the channel is 1. 33 | echo '1', PHP_EOL; // This will be printed out. 34 | $channel->push('foo'); 35 | echo '2', PHP_EOL; // This will be printed out. 36 | $channel->push('bar'); // No room to store it in the channel. 37 | echo '4', PHP_EOL; // This will never be printed out. 38 | }); 39 | echo '3', PHP_EOL; // This will be printed out. 40 | }); 41 | -------------------------------------------------------------------------------- /.github/workflows/build_docker_images.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker Images 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build-docker-images: 11 | runs-on: ubuntu-22.04 12 | if: ${{ github.repository == 'deminy/swoole-by-examples' }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | type: ["server", "client"] 17 | version: ["5.1", "6.0"] 18 | 19 | steps: 20 | - 21 | name: Checkout 22 | uses: actions/checkout@v4 23 | 24 | - 25 | name: Set up QEMU 26 | uses: docker/setup-qemu-action@v3 27 | 28 | - 29 | name: Set up Docker Buildx 30 | uses: docker/setup-buildx-action@v3 31 | 32 | - 33 | name: Login to DockerHub 34 | uses: docker/login-action@v3 35 | with: 36 | username: ${{ secrets.DOCKERHUB_USERNAME }} 37 | password: ${{ secrets.DOCKERHUB_TOKEN }} 38 | 39 | - 40 | name: Build and Publish Docker Image 41 | id: docker_build 42 | uses: docker/build-push-action@v6 43 | with: 44 | context: ./dockerfiles/${{ matrix.type }}/ 45 | build-args: | 46 | SWOOLE_VERSION=${{ matrix.version }} 47 | PHP_VERSION=8.3 48 | platforms: linux/amd64,linux/arm64/v8 49 | push: true 50 | tags: deminy/swoole-by-examples:${{ matrix.type }}-${{ matrix.version }} 51 | -------------------------------------------------------------------------------- /examples/csp/coroutines/exit.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getFlags()} (SWOOLE_EXIT_IN_COROUTINE). 29 | 2. \\Swoole\\ExitException::getStatus(): The status as defined in PHP function exit(). In this example, the status is {$e->getStatus()}. 30 | 31 | EOT; 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /examples/pool/process-pool/pool-standalone.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on('workerStart', function (Pool $pool, int $workerId) use ($counter): void { 24 | // For standalone process pool, business logic should be implemented inside this "workerStart" callback. 25 | echo "Process #{$workerId} (process ID in the OS: {$pool->getProcess()->pid}) started.", PHP_EOL; // @phpstan-ignore property.nonObject 26 | $counter->add(1); 27 | }); 28 | $pool->on('workerStop', function (Pool $pool, int $workerId) use ($counter): void { 29 | echo "Process #{$workerId} (process ID in the OS: {$pool->getProcess()->pid}) stopped.", PHP_EOL; // @phpstan-ignore property.nonObject 30 | if ($counter->get() >= 3) { 31 | $pool->shutdown(); 32 | } 33 | }); 34 | 35 | $pool->start(); 36 | -------------------------------------------------------------------------------- /examples/hooks/pdo_mysql.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_TCP]); 28 | 29 | run(function (): void { 30 | for ($i = 0; $i < 5; $i++) { 31 | go(function (): void { 32 | $pdo = new PDO('mysql:host=mysql;dbname=test', 'username', 'password'); 33 | $stmt = $pdo->prepare('SELECT SLEEP(3)'); 34 | $stmt->execute(); 35 | $stmt = null; 36 | $pdo = null; 37 | }); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /examples/csp/deadlocks/coroutine-yielded-3.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | function () { 24 | return Coroutine::stats()['coroutine_num'] === 0; // @phpstan-ignore offsetAccess.nonOffsetAccessible 25 | }, 26 | ] 27 | ); 28 | Coroutine::create(function (): void { 29 | echo '1', PHP_EOL; // This will be printed out. 30 | Coroutine::yield(); 31 | echo '3', PHP_EOL; // This will never be printed out. 32 | }); 33 | echo '2', PHP_EOL; // This will be printed out. 34 | 35 | // NOTE: In most cases it's not necessary nor recommended to use method `Swoole\Event::wait()` directly in your code. 36 | // The example in this file is just for demonstration purpose. 37 | Swoole\Event::wait(); 38 | -------------------------------------------------------------------------------- /examples/locks/lock-across-processes.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | lock() === true, 'Lock the lock for the first time successfully.'); 26 | usleep(5000); // Sleep for 5 milliseconds. 27 | echo '4'; 28 | assert($lock->unlock() === true, 'Unlock the lock successfully.'); 29 | echo '5'; 30 | }); 31 | 32 | $process2 = new Process(function () use ($lock) { 33 | echo '2'; 34 | assert($lock->trylock() === false, 'Failed to lock a locked lock.'); 35 | echo '3'; 36 | assert($lock->lock() === true, 'Lock the lock for the second time successfully.'); 37 | echo '6'; 38 | assert($lock->unlock() === true, 'Unlock the lock successfully.'); 39 | echo '7'; 40 | }); 41 | 42 | $process1->start(); // Start the first child process. 43 | $process2->start(); // Start the second child process. 44 | 45 | Process::wait(); 46 | Process::wait(); 47 | 48 | echo '8', PHP_EOL; 49 | -------------------------------------------------------------------------------- /examples/hooks/mysqli.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_TCP]); 28 | 29 | run(function (): void { 30 | for ($i = 0; $i < 5; $i++) { 31 | go(function (): void { 32 | $mysqli = new mysqli('mysql', 'username', 'password', 'test'); 33 | $stmt = $mysqli->prepare('SELECT SLEEP(3)'); 34 | if ($stmt === false) { 35 | echo 'Failed to prepare the statement.', PHP_EOL; 36 | return; 37 | } 38 | 39 | $stmt->execute(); 40 | $stmt->close(); 41 | $mysqli->close(); 42 | }); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /examples/csp/barrier.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 16 | * $client = new Redis(); 17 | * $client->connect('server'); 18 | * echo $client->set('foo', 'bar'), PHP_EOL; 19 | * echo $client->get('foo'), PHP_EOL; 20 | * 21 | */ 22 | 23 | use Swoole\Redis\Server; 24 | use Swoole\Table; 25 | 26 | // We use a Swoole table as the data storage for the Redis server. 27 | $table = new Table(1024); 28 | $table->column('value', Table::TYPE_STRING, 64); 29 | $table->create(); 30 | 31 | $server = new Server('0.0.0.0', 6379); 32 | 33 | $server->setHandler('SET', function (int $fd, array $data) use ($server, $table): void { 34 | $table->set($data[0], ['value' => $data[1]]); 35 | $server->send($fd, Server::format(Server::STATUS, 'OK')); 36 | }); 37 | 38 | $server->setHandler('GET', function (int $fd, array $data) use ($server, $table): void { 39 | $key = $data[0]; 40 | if ($table->exist($key)) { 41 | $server->send($fd, Server::format(Server::STRING, $table->get($key)['value'])); // @phpstan-ignore offsetAccess.nonOffsetAccessible 42 | } else { 43 | $server->send($fd, Server::format(Server::NIL)); 44 | } 45 | }); 46 | 47 | $server->start(); 48 | -------------------------------------------------------------------------------- /examples/csp/waitgroup.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | add(); 32 | Coroutine::sleep(1); 33 | $wg->done(); 34 | }); 35 | 36 | $wg->add(2); // You don't have to increase the counter one by one. 37 | go(function () use ($wg): void { 38 | Coroutine::sleep(2); 39 | $wg->done(); 40 | }); 41 | go(function () use ($wg): void { 42 | Coroutine::sleep(3); 43 | $wg->done(); 44 | }); 45 | 46 | $wg->wait(); // Wait those 3 coroutines to finish. 47 | 48 | // Any code here won't be executed until all 3 coroutines created in this function finish execution. 49 | }); 50 | -------------------------------------------------------------------------------- /examples/hooks/pdo_sqlite.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_PDO_SQLITE]); 30 | 31 | run(function (): void { 32 | for ($i = 0; $i < 5; $i++) { 33 | go(function (): void { 34 | // Here we use a temporary SQLite database for demonstration purpose. It will be deleted automatically when 35 | // the connection is closed. 36 | $pdo = new PDO('sqlite:'); 37 | 38 | // Register a custom SQLite function to sleep for 3 seconds. 39 | $pdo->sqliteCreateFunction('sleepFor3Seconds', function (): void { 40 | sleep(3); 41 | }); 42 | 43 | $pdo->exec('SELECT sleepFor3Seconds()'); 44 | }); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /examples/csp/channel.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | pop(); 34 | } 35 | var_dump($result); 36 | }); 37 | 38 | go(function () use ($channel): void { 39 | $cli = new Client('php.net'); 40 | $cli->get('/'); 41 | $channel->push("{$cli->statusCode}"); 42 | }); 43 | 44 | go(function () use ($channel): void { 45 | $cli = new Client('swoole.com'); 46 | $cli->get('/'); 47 | $channel->push((int) $cli->statusCode); 48 | }); 49 | 50 | // At this point, all three coroutines are paused. 51 | }); 52 | -------------------------------------------------------------------------------- /examples/csp/deadlocks/swoole-lock.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | lock(); // 1. The lock is acquired. 33 | Coroutine::sleep(1); // 2. The sleep() method call will switch execution to another coroutine. 34 | $lock->unlock(); 35 | }); 36 | 37 | go(function () use ($lock): void { 38 | $lock->lock(); // 3. Trying to acquire the lock again? This will cause deadlock. 39 | Coroutine::sleep(1); 40 | $lock->unlock(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /examples/io/blocking-vs-non-blocking.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | lock() === true, 'Lock the lock for the first time successfully.'); 34 | Coroutine::sleep(0.005); // Sleep for 5 milliseconds. 35 | echo '4'; 36 | assert($lock->unlock() === true, 'Unlock the lock successfully.'); 37 | echo '5'; 38 | }); 39 | 40 | go(function () use ($lock) { // Start the second coroutine inside the main coroutine. 41 | echo '2'; 42 | assert($lock->trylock() === false, 'Failed to lock a locked lock.'); 43 | echo '3'; 44 | assert($lock->lock() === true, 'Lock the lock for the second time successfully.'); 45 | echo '6'; 46 | assert($lock->unlock() === true, 'Unlock the lock successfully.'); 47 | echo '7'; 48 | }); 49 | }); 50 | 51 | echo '8', PHP_EOL; 52 | -------------------------------------------------------------------------------- /examples/hooks/curl.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_CURL]); 32 | 33 | run(function (): void { 34 | for ($i = 0; $i < 6; $i++) { 35 | go(function (): void { 36 | $ch = curl_init(); 37 | curl_setopt($ch, CURLOPT_URL, 'http://server:9501?sleep=2'); 38 | curl_setopt($ch, CURLOPT_HEADER, false); 39 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 40 | curl_exec($ch); 41 | $statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); 42 | curl_close($ch); 43 | 44 | if ($statusCode !== 234) { 45 | throw new Exception('Status code returned from the built-in HTTP/1 server should be 234.'); 46 | } 47 | }); 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /examples/locks/lock-across-threads.php: -------------------------------------------------------------------------------- 1 | join(); 33 | } 34 | 35 | echo '8', PHP_EOL; 36 | } else { 37 | $i = $args[0]; 38 | $lock = $args[1]; 39 | 40 | if ($i === 1) { // First child thread. 41 | echo '1'; 42 | assert($lock->lock() === true, 'Lock the lock for the first time successfully.'); 43 | usleep(5000); // Sleep for 5 milliseconds. 44 | echo '4'; 45 | assert($lock->unlock() === true, 'Unlock the lock successfully.'); 46 | echo '5'; 47 | } else { // Second child thread. 48 | echo '2'; 49 | assert($lock->trylock() === false, 'Failed to lock a locked lock.'); 50 | echo '3'; 51 | assert($lock->lock() === true, 'Lock the lock for the second time successfully.'); 52 | echo '6'; 53 | assert($lock->unlock() === true, 'Unlock the lock successfully.'); 54 | echo '7'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/io/block-a-coroutine.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | errCode. 39 | // 40 | // Please note that the pop() method blocks the current coroutine only, not the whole process. 41 | $channel->pop(2.0); 42 | 43 | echo date('H:i:s'), " (round {$i})", PHP_EOL; 44 | } 45 | }); 46 | -------------------------------------------------------------------------------- /examples/clients/http1.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 27 | [ 28 | 'timeout' => -1, 29 | ] 30 | ); 31 | $client->setHeaders( 32 | [ 33 | 'Accept-Encoding' => 'gzip', 34 | ] 35 | ); 36 | $client->download('/swoole/swoole-src/master/mascot.png', __DIR__ . '/mascot.png'); 37 | 38 | echo 'Done executing the first HTTP/1 request.', PHP_EOL; 39 | }); 40 | 41 | // For the 2nd one, we make an HTTP request to the HTTP/1 server started in the "server" container. 42 | go(function (): void { 43 | $client = new Client('server', 9501); 44 | $client->get('/'); 45 | 46 | // You can uncomment following code to print out the HTTP response headers and body. 47 | // foreach ($client->getHeaders() as $key => $value) { 48 | // echo "{$key}: $value", PHP_EOL; 49 | // } 50 | // echo PHP_EOL, $client->body; 51 | 52 | echo 'Done executing the second HTTP/1 request.', PHP_EOL; 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /examples/servers/keepalive.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 28 | [ 29 | Constant::OPTION_OPEN_TCP_KEEPALIVE => 1, 30 | 31 | // Following 3 parameters are to adjust timeout. Run this command to check default values in the container: 32 | // docker compose exec -t server bash -c "tail -n +1 /proc/sys/net/ipv4/tcp_keepalive_*" 33 | Constant::OPTION_TCP_KEEPIDLE => 7200, 34 | Constant::OPTION_TCP_KEEPCOUNT => 9, 35 | Constant::OPTION_TCP_KEEPINTERVAL => 75, 36 | ] 37 | ); 38 | $server->on( 39 | 'receive', 40 | function (Server $server, int $fd, int $reactorId, string $data): void { 41 | $server->send($fd, $data); 42 | } 43 | ); 44 | $server->start(); 45 | -------------------------------------------------------------------------------- /examples/csp/coroutines/creation-1.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on( 25 | 'message', 26 | function (Server $server, Frame $frame): void { 27 | $server->push($frame->fd, "Hello, {$frame->data}"); 28 | } 29 | ); 30 | 31 | $process = new Process( 32 | function (): void { 33 | // To simulate task processing. Here we simply print out a message. 34 | // In reality, a task queue system works like following: 35 | // 1. Use some storage system (e.g., Redis) to store tasks dispatched from worker processes, cron jobs or 36 | // another source; 37 | // 2. In the task processing processes, get tasks from the storage system, process them, then remove them once 38 | // done. 39 | echo 'Task processed (in file ', __FILE__, ').', PHP_EOL; 40 | sleep(29); 41 | } 42 | ); 43 | $server->addProcess($process); 44 | 45 | $process = new Process( 46 | function (): void { 47 | while (true) { // @phpstan-ignore while.alwaysTrue 48 | sleep(31); 49 | echo 'Cron executed (in file ', __FILE__, ').', PHP_EOL; // To simulate cron executions. 50 | } 51 | } 52 | ); 53 | $server->addProcess($process); 54 | 55 | $server->start(); 56 | -------------------------------------------------------------------------------- /examples/hooks/pdo_pgsql.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_PDO_PGSQL]); 32 | 33 | run(function (): void { 34 | for ($i = 0; $i < 5; $i++) { 35 | go(function (): void { 36 | $pdo = new PDO('pgsql:host=postgresql;port=5432;dbname=test', 'username', 'password'); 37 | $stmt = $pdo->prepare('SELECT pg_sleep(3)'); 38 | if ($stmt === false) { 39 | echo 'Failed to prepare the statement.', PHP_EOL; 40 | return; 41 | } 42 | 43 | $stmt->execute(); // The query finishes in 3 seconds. 44 | $stmt->fetchAll(); 45 | // The result array returned is: 46 | // [ 47 | // [ 48 | // 'pg_sleep' => '', 49 | // 0 => '', 50 | // ] 51 | // ]; 52 | $stmt = null; 53 | $pdo = null; 54 | }); 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /examples/hooks/redis/phpredis.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_TCP]); 30 | 31 | run(function (): void { 32 | for ($i = 0; $i < 5; $i++) { 33 | go(function (): void { 34 | $client = new Redis(); 35 | $client->connect('redis'); 36 | 37 | // Use unique keys for each coroutine to avoid conflicts. 38 | $key = uniqid(sprintf('test-%d-', Coroutine::getCid())); // @phpstan-ignore argument.type 39 | $client->set($key, 'dummy', ['EX' => 10]); // Set a key with an expiration time of 10 seconds. 40 | assert($client->get($key) === 'dummy', 'The value stored in Redis should be "dummy".'); 41 | 42 | // This will block the coroutine for 3 seconds since the list does not exist. 43 | $client->blPop('non-existing-list', 3.0); 44 | 45 | $client->close(); 46 | }); 47 | } 48 | }); 49 | 50 | echo PHP_EOL; 51 | -------------------------------------------------------------------------------- /examples/pool/database-pool/mysqli.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | withHost('mysql') 38 | ->withUsername('username') 39 | ->withPassword('password') 40 | ->withDbname('test') 41 | ; 42 | $pool = new MysqliPool($config, 128); 43 | 44 | for ($n = 1024; $n--;) { 45 | go(function () use ($pool): void { 46 | /** @var mysqli $mysqli */ 47 | $mysqli = $pool->get(); 48 | 49 | $stmt = $mysqli->prepare('SELECT SLEEP(1)'); 50 | if ($stmt === false) { 51 | echo 'Failed to prepare the statement.', PHP_EOL; 52 | return; 53 | } 54 | $stmt->execute(); 55 | $stmt->close(); 56 | 57 | $pool->put($mysqli); 58 | }); 59 | } 60 | }); 61 | 62 | echo 'Done', PHP_EOL; 63 | -------------------------------------------------------------------------------- /examples/csp/deadlocks/server-shutdown.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on('workerStart', function (Server $server, int $workerId): void { 25 | if ($workerId === 0) { // The first event worker process is used in this example. 26 | // Create a coroutine that sleeps forever. 27 | $cid = Coroutine::create(function (): void { 28 | while (true) { // @phpstan-ignore while.alwaysTrue 29 | if (Coroutine::isCanceled()) { 30 | // The deadlock can be resolved by uncommenting line 40 and line 31. 31 | // break; #2: Quit the infinite loop after the coroutine is canceled. 32 | } 33 | Coroutine::sleep(0.01); 34 | } 35 | }); 36 | 37 | // Shutdown the server after 2 seconds. 38 | Timer::after(2_000, function () use ($server, $cid): void { // @phpstan-ignore closure.unusedUse 39 | // The deadlock can be resolved by uncommenting line 40 and line 31. 40 | // Coroutine::cancel($cid); #1: Cancel the coroutine before shutting down the server. 41 | 42 | echo 'The server is shutting down.', PHP_EOL; 43 | $server->shutdown(); 44 | }); 45 | } 46 | }); 47 | 48 | // A dummy callback for the "request" event is required for the HTTP server. It has nothing to do with this example. 49 | $server->on('request', function (Request $request, Response $response): void { 50 | $response->end('OK' . PHP_EOL); 51 | }); 52 | 53 | $server->start(); 54 | -------------------------------------------------------------------------------- /examples/csp/deadlocks/file-locking.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | lock(); 36 | if ($lock->lockwait(2.0) !== true) { // The lock is released after 2 seconds due to timeout. 37 | $lock->unlock(); 38 | } 39 | 40 | echo date('H:i:s'), '(coroutine ID: 2)', PHP_EOL; 41 | }); 42 | 43 | // Everything blow is blocked due to the lock acquired by the second coroutine. 44 | 45 | go(function (): void { // A third coroutine is created, which has to wait until the second coroutine releases the lock. 46 | echo date('H:i:s'), '(coroutine ID: 3)', PHP_EOL; 47 | }); 48 | 49 | // This line is printed out in the main coroutine (created by function call run()). 50 | echo date('H:i:s'), '(coroutine ID: 1)', PHP_EOL; 51 | }); 52 | -------------------------------------------------------------------------------- /examples/servers/ddos-protection.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 35 | [ 36 | // For testing purpose, there is only one worker process to handle HTTP requests. 37 | Constant::OPTION_WORKER_NUM => 1, 38 | Constant::OPTION_ENABLE_DELAY_RECEIVE => true, 39 | ] 40 | ); 41 | 42 | // In this example, 1/3 of the traffic is processed at a later time, but not in real time. In reality, 43 | // there are different ways to enable DDoS protection, like rate limiting, blocking IP address, etc. 44 | $server->on('connect', function (Server $server, int $fd, int $reactorId): void { 45 | if (($fd % 3) === 0) { 46 | // 1/3 of all HTTP requests have to wait for two seconds before being processed. 47 | Timer::after(2000, function () use ($server, $fd): void { 48 | $server->confirm($fd); 49 | }); 50 | } else { 51 | // 2/3 of all HTTP requests are processed immediately by the server. 52 | $server->confirm($fd); 53 | } 54 | }); 55 | $server->on('request', function (Request $request, Response $response): void { 56 | $response->end('OK' . PHP_EOL); 57 | }); 58 | 59 | $server->start(); 60 | -------------------------------------------------------------------------------- /examples/pool/database-pool/pdo_pgsql.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | withDriver('pgsql') 35 | ->withHost('postgresql') 36 | ->withPort(5432) 37 | ->withUsername('username') 38 | ->withPassword('password') 39 | ->withDbName('test') 40 | ; 41 | $pool = new PDOPool($config, 100); 42 | 43 | for ($n = 1000; $n--;) { 44 | go(function () use ($pool): void { 45 | /** @var PDO $pdo */ 46 | $pdo = $pool->get(); 47 | 48 | $stmt = $pdo->prepare('SELECT pg_sleep(1)'); 49 | if ($stmt === false) { 50 | echo 'Failed to prepare the statement.', PHP_EOL; 51 | return; 52 | } 53 | $stmt->execute(); // The query finishes in 1 second. 54 | $stmt->fetchAll(); 55 | // The result array returned is: 56 | // [ 57 | // [ 58 | // 'pg_sleep' => '', 59 | // 0 => '', 60 | // ] 61 | // ]; 62 | $stmt = null; 63 | 64 | $pool->put($pdo); 65 | }); 66 | } 67 | }); 68 | 69 | echo 'Done', PHP_EOL; 70 | -------------------------------------------------------------------------------- /examples/misc/atomic-counter-signed-64-bit.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | add(5); 29 | echo $atomic->get(), PHP_EOL; // 5 30 | 31 | $atomic->add(0); 32 | echo $atomic->get(), PHP_EOL; // 5 33 | 34 | $atomic->add(-2); 35 | echo $atomic->get(), PHP_EOL; // 3 36 | 37 | $atomic->sub(2); 38 | echo $atomic->get(), PHP_EOL; // 1 39 | 40 | $atomic->sub(0); 41 | echo $atomic->get(), PHP_EOL; // 1 42 | 43 | $atomic->sub(-1); 44 | echo $atomic->get(), PHP_EOL; // 2 45 | 46 | $atomic->set(5); 47 | echo $atomic->get(), PHP_EOL; // 5 48 | 49 | // The counter won't be changed to 7 since current value is not 4. 50 | $atomic->cmpset(4, 7); 51 | echo $atomic->get(), PHP_EOL; // 5 52 | 53 | // The counter will be changed to 7 since current value is 5 (same as the one expected). 54 | $atomic->cmpset(5, 7); 55 | echo $atomic->get(), PHP_EOL, PHP_EOL; // 7 56 | 57 | // At this point, class \Swoole\Atomic\Long works exactly the same as class \Swoole\Atomic. 58 | // Now let's show the difference between the two classes. 59 | 60 | $atomic = new Swoole\Atomic\Long(-1); 61 | echo $atomic->get(), PHP_EOL; // -1 62 | 63 | $atomic = new Swoole\Atomic\Long(); 64 | 65 | $atomic->sub(11); 66 | echo $atomic->get(), PHP_EOL; // -11 67 | 68 | $atomic->set(-1999); 69 | echo $atomic->get(), PHP_EOL; // -1999 70 | -------------------------------------------------------------------------------- /examples/hooks/redis/predis.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_TCP]); 36 | 37 | run(function (): void { 38 | for ($i = 0; $i < 5; $i++) { 39 | go(function (): void { 40 | $client = new Client(['scheme' => 'tcp', 'host' => 'redis', 'port' => 6379]); 41 | 42 | // Use unique keys for each coroutine to avoid conflicts. 43 | $key = uniqid(sprintf('test-%d-', Coroutine::getCid())); // @phpstan-ignore argument.type 44 | $client->set($key, 'dummy', 'EX', 10); // Set a key with an expiration time of 10 seconds. 45 | assert($client->get($key) === 'dummy', 'The value stored in Redis should be "dummy".'); 46 | 47 | // This will block the coroutine for 3 seconds since the list does not exist. 48 | $client->blpop('non-existing-list', 3.0); 49 | 50 | $client->disconnect(); 51 | }); 52 | } 53 | }); 54 | 55 | echo PHP_EOL; 56 | -------------------------------------------------------------------------------- /examples/misc/atomic-counter-unsigned-32-bit.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | add(5); 30 | echo $atomic->get(), PHP_EOL; // 5 31 | 32 | $atomic->add(0); 33 | echo $atomic->get(), PHP_EOL; // 5 34 | 35 | $atomic->add(-2); 36 | echo $atomic->get(), PHP_EOL; // 3 37 | 38 | $atomic->sub(2); 39 | echo $atomic->get(), PHP_EOL; // 1 40 | 41 | $atomic->sub(0); 42 | echo $atomic->get(), PHP_EOL; // 1 43 | 44 | $atomic->sub(-1); 45 | echo $atomic->get(), PHP_EOL; // 2 46 | 47 | $atomic->set(5); 48 | echo $atomic->get(), PHP_EOL; // 5 49 | 50 | // The counter won't be changed to 7 since current value is not 4. 51 | $atomic->cmpset(4, 7); 52 | echo $atomic->get(), PHP_EOL; // 5 53 | 54 | // The counter will be changed to 7 since current value is 5 (same as the one expected). 55 | $atomic->cmpset(5, 7); 56 | echo $atomic->get(), PHP_EOL, PHP_EOL; // 7 57 | 58 | // At this point, class \Swoole\Atomic works exactly the same as class \Swoole\Atomic\Long. 59 | // Now let's show the difference between the two classes. 60 | 61 | $atomic = new Swoole\Atomic(-1); 62 | echo $atomic->get(), PHP_EOL; // 4294967295 63 | 64 | $atomic = new Swoole\Atomic(); 65 | 66 | $atomic->sub(11); 67 | echo $atomic->get(), PHP_EOL; // 4294967285 68 | 69 | $atomic->set(-1999); 70 | echo $atomic->get(), PHP_EOL; // 4294965297 71 | -------------------------------------------------------------------------------- /examples/hooks/native-curl.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_NATIVE_CURL]); 33 | 34 | run(function (): void { 35 | for ($i = 0; $i < 3; $i++) { 36 | go(function (): void { 37 | $mh = curl_multi_init(); 38 | 39 | $handlers = []; 40 | for ($j = 0; $j < 2; $j++) { 41 | $ch = curl_init(); 42 | curl_setopt($ch, CURLOPT_URL, 'http://server:9501?sleep=2'); 43 | curl_setopt($ch, CURLOPT_HEADER, false); 44 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 45 | 46 | curl_multi_add_handle($mh, $ch); 47 | $handlers[] = $ch; 48 | } 49 | 50 | do { 51 | $status = curl_multi_exec($mh, $active); 52 | if ($active) { 53 | curl_multi_select($mh); 54 | } 55 | } while ($active && $status == CURLM_OK); 56 | 57 | foreach ($handlers as $ch) { 58 | $statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); 59 | curl_multi_remove_handle($mh, $ch); 60 | if ($statusCode !== 234) { 61 | throw new Exception('Status code returned from the built-in HTTP/1 server should be 234.'); 62 | } 63 | } 64 | curl_multi_close($mh); 65 | }); 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /examples/csp/coroutines/creation-2.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 33 | [ 34 | Constant::OPTION_WORKER_NUM => 3, // The number of worker processes to process HTTP requests. 35 | ] 36 | ); 37 | 38 | $server->on( 39 | 'request', 40 | function (Request $request, Response $response) use ($server): void { 41 | if ($request->server['request_uri'] === '/summary') { // Show summary of all counters. 42 | $output = ''; 43 | foreach (apcu_cache_info()['cache_list'] as $item) { // @phpstan-ignore foreach.nonIterable 44 | $output .= "{$item['info']}: " . apcu_fetch($item['info']) . PHP_EOL; // @phpstan-ignore-line 45 | } 46 | 47 | // The output will be like: 48 | // counter_0: 46 49 | // counter_1: 16 50 | // counter_2: 437 51 | $response->end($output); 52 | } else { // Increase a counter. 53 | apcu_inc("counter_{$server->worker_id}"); 54 | $response->end('OK' . PHP_EOL); 55 | } 56 | } 57 | ); 58 | 59 | $server->start(); 60 | -------------------------------------------------------------------------------- /examples/servers/interruptible-sleep.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | on('workerStart', function (Server $server, int $workerId) use ($exited): void { 32 | if ($workerId === 0) { 33 | Coroutine::create(function () use ($exited): void { 34 | // Here we start the second cron job that makes an HTTP request every 19 seconds. 35 | while (true) { 36 | echo '[INTERRUPTIBLE-SLEEP] Simulating cronjob execution. (case 1)', PHP_EOL; 37 | $exited->pop(19); 38 | if ($exited->errCode === SWOOLE_CHANNEL_CLOSED) { 39 | echo '[INTERRUPTIBLE-SLEEP] Simulating cronjob execution. (case 2)', PHP_EOL; 40 | break; 41 | } 42 | } 43 | echo '[INTERRUPTIBLE-SLEEP] The cronjob has exited.', PHP_EOL; 44 | }); 45 | } 46 | }); 47 | $server->on('workerExit', function (Server $server, int $workerId) use ($exited): void { 48 | echo "[INTERRUPTIBLE-SLEEP] Worker #{$workerId} is exiting.", PHP_EOL; 49 | if ($workerId === 0) { 50 | Coroutine::create(function () use ($exited): void { 51 | $exited->close(); 52 | }); 53 | } 54 | echo "[INTERRUPTIBLE-SLEEP] Worker #{$workerId} has exited.", PHP_EOL; 55 | }); 56 | $server->on('request', function (Request $request, Response $response): void { 57 | $response->end('OK' . PHP_EOL); 58 | }); 59 | 60 | $server->start(); 61 | -------------------------------------------------------------------------------- /examples/servers/rock-paper-scissors.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 35 | Rock 36 | Paper 37 | Scissors 38 | 39 | 40 | EOT; 41 | $exchanges = []; 42 | 43 | $server = new Server('0.0.0.0', 9801, SWOOLE_BASE); 44 | 45 | $server->on( 46 | 'request', 47 | function (Request $request, Response $response) use ($form, &$exchanges): void { 48 | $response->header('Content-Type', 'text/html; charset=UTF-8'); 49 | if ($request->server['request_method'] == 'GET') { 50 | $response->end($form); // Show the web form. 51 | } else { 52 | $exchanges[$request->get['name']] = [$request, $response]; 53 | 54 | if (count($exchanges) == 3) { 55 | $result = ''; 56 | foreach ($exchanges as [$request]) { 57 | $result .= "{$request->get['name']}: {$request->post['shape']}; "; 58 | } 59 | $result = substr($result, 0, -2) . '.' . PHP_EOL; 60 | 61 | foreach ($exchanges as [, $response]) { // Send responses back. 62 | $response->end($result); 63 | } 64 | 65 | $exchanges = []; 66 | } 67 | } 68 | } 69 | ); 70 | 71 | $server->start(); 72 | -------------------------------------------------------------------------------- /examples/io/block-processes-using-swoole-lock.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 23 | [ 24 | Constant::OPTION_ENABLE_COROUTINE => true, 25 | ] 26 | ); 27 | 28 | // WARNING: 29 | // 1. Don't keep creating new locks in callback functions of server events (e.g., onReceive, onConnect, 30 | // onRequest, etc.). It could lead to memory leak. 31 | // 2. Avoid using the same lock across different coroutines. It could lead to deadlock. e.g., 32 | // https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/swoole-lock.php 33 | $lock = new Lock(); // The lock created is available to all forked processes within the pool. 34 | $lock->lock(); 35 | 36 | // In this example, we will use a pool of 3 processes: 37 | // - Process #0 and #1 are blocked in sequence and waiting another process (process #3) to wake them up. 38 | // - Process #2 will wake up process #1 and #2 in sequence; afterwords it will shutdown the pool and exit the program. 39 | $pool->on('workerStart', function (Pool $pool, int $workerId) use ($lock): void { 40 | Coroutine::sleep(max(0.001, $workerId * 0.1)); // Used only to better order the output from different processes. 41 | 42 | switch ($workerId) { 43 | case 0: // Process #0. 44 | case 1: // Process #1. 45 | echo "Process #{$workerId} is blocked and waiting another process (process #2) to wake it up.", PHP_EOL; 46 | // Since the lock is already acquired by the main process of the pool at line 29, here the two child 47 | // processes are blocked until the lock is released and then acquired. 48 | $lock->lock(); 49 | echo "Process #{$workerId} is waken up.", PHP_EOL; 50 | break; 51 | case 2: // Process #2. 52 | echo 'Process #2 is waking up process #0 and #1.', PHP_EOL; 53 | for ($i = 0; $i < 2; $i++) { 54 | $lock->unlock(); // To wake up process #0 and #1 in sequence. 55 | Coroutine::sleep(0.1); // Used only to better order the output. 56 | } 57 | 58 | $pool->shutdown(); // Done with the example. Now lets shutdown the pool and exit the program. 59 | break; 60 | default: 61 | echo "Error: process #{$workerId} not handled properly.", PHP_EOL; 62 | break; 63 | } 64 | 65 | // Blocks current process for a minute. This is to prevent recreating processes in the pool before the program exits. 66 | Coroutine::sleep(60); 67 | }); 68 | 69 | $pool->start(); 70 | -------------------------------------------------------------------------------- /examples/csp/coroutines/benchmark.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | $totalCoroutines, 54 | ] 55 | ); 56 | 57 | run(function () use ($totalCoroutines, $startTime): void { 58 | for ($i = $totalCoroutines; $i--;) { 59 | go(function (): void { 60 | sleep(5); 61 | }); 62 | 63 | if (($i % 100_000) === 0) { 64 | printf( 65 | '%07d active coroutines; total time: %f seconds; memory usage: %d.' . PHP_EOL, 66 | count(Coroutine::listCoroutines()), // @phpstan-ignore argument.type 67 | microtime(true) - $startTime, 68 | memory_get_usage() 69 | ); 70 | } 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /examples/io/block-processes-using-swoole-atomic.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 26 | [ 27 | Constant::OPTION_ENABLE_COROUTINE => true, 28 | ] 29 | ); 30 | 31 | // In this example, we will use a pool of 4 processes: 32 | // - Process #0 will be blocked for 10 milliseconds. 33 | // - Process #1 and #2 are blocked forever and waiting another process (process #3) to wake them up. 34 | // - Process #3 will wake up process #1 and #2; afterwords it will shutdown the pool and exit the program. 35 | $pool->on('workerStart', function (Pool $pool, int $workerId) use ($atomic): void { 36 | Coroutine::sleep(max(0.001, $workerId * 0.1)); // Used only to better order the output from different processes. 37 | 38 | switch ($workerId) { 39 | case 0: // Process #0 (first process). 40 | // Method wait() will block the process until another process wakes it up or the timeout expires. In our 41 | // case, the timeout expires and the method call to wait() returns false. 42 | (new Atomic())->wait(0.01); 43 | echo 'Process #0 is blocked for 10 milliseconds (0.01 second).', PHP_EOL; 44 | break; 45 | case 1: // Process #1. 46 | case 2: // Process #2. 47 | echo "Process #{$workerId} is blocked and waiting another process (process #3) to wake it up.", PHP_EOL; 48 | // Method wait() will block current process until another process (process #3) wakes it up. The timeout 49 | // never expires because we set it to -1. 50 | $atomic->wait(-1); 51 | 52 | Coroutine::sleep($workerId * 0.2); // Used only to better order the output. 53 | echo "Process #{$workerId} is waken up.", PHP_EOL; 54 | break; 55 | case 3: // Process #3. 56 | echo 'Process #3 is waking up process #1 and #2.', PHP_EOL; 57 | $atomic->wakeup(2); // To wake up process #1 and #2. 58 | 59 | Coroutine::sleep(1); 60 | $pool->shutdown(); // Done with the example. Now lets shutdown the pool and exit the program. 61 | break; 62 | default: 63 | echo "Error: process #{$workerId} not handled properly.", PHP_EOL; 64 | break; 65 | } 66 | 67 | // Blocks current process forever. This is to prevent recreating processes in the pool. 68 | (new Atomic())->wait(-1); 69 | }); 70 | 71 | $pool->start(); 72 | -------------------------------------------------------------------------------- /examples/hooks/hook-flags.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | SWOOLE_HOOK_ALL]); 43 | 44 | run(function (): void { 45 | // Enable the hook for sleep-related functions, allowing them to run non-blockingly. 46 | Runtime::setHookFlags(SWOOLE_HOOK_SLEEP); 47 | go(function (): void { 48 | echo '0'; 49 | sleep(1); 50 | echo '7'; 51 | }); 52 | 53 | // Enable hooks for sleep, file, and process-related functions, making them coroutine-friendly. 54 | Runtime::setHookFlags(SWOOLE_HOOK_SLEEP | SWOOLE_HOOK_FILE | SWOOLE_HOOK_PROC); 55 | go(function (): void { 56 | echo '1'; 57 | sleep(1); 58 | echo '7'; 59 | }); 60 | 61 | // Enable all hooks, making all supported blocking I/O operations coroutine-friendly. 62 | Runtime::setHookFlags(SWOOLE_HOOK_ALL); 63 | go(function (): void { 64 | echo '2'; 65 | sleep(1); 66 | echo '7'; 67 | }); 68 | 69 | // Enable all hooks except for sleep-related ones. Sleep operations will now block. 70 | Runtime::setHookFlags(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_SLEEP); 71 | go(function (): void { 72 | echo '3'; 73 | sleep(1); 74 | echo '4'; 75 | }); 76 | 77 | // Disable all hook flags, causing all I/O operations to run in blocking mode. 78 | Runtime::setHookFlags(0); 79 | go(function (): void { 80 | echo '5'; 81 | sleep(1); 82 | echo '6'; 83 | }); 84 | }); 85 | 86 | echo PHP_EOL; 87 | -------------------------------------------------------------------------------- /examples/csp/context.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | name)) { 44 | echo 'Object "', $this->name, '" is destroyed.' . PHP_EOL; 45 | } 46 | } 47 | }; 48 | 49 | // Prior to PHP 8.2, items in the Context object of a coroutine could be set or accessed using dynamic properties, 50 | // e.g., 51 | // Coroutine::getContext($cid)->foo = 'bar'; 52 | // Starting with PHP 8.2, dynamic properties are deprecated, so we use array-style access instead. 53 | 54 | // The Context object of a coroutine works the same as an \ArrayObject object. 55 | Coroutine::getContext()['co1_obj'] = new $class('co1_obj'); // @phpstan-ignore offsetAccess.nonOffsetAccessible 56 | $cid = Coroutine::getCid(); // Coroutine::getCid() returns 1. 57 | 58 | Coroutine::create(function () use ($class): void { 59 | // The Context object of a coroutine works the same as an \ArrayObject object. 60 | Coroutine::getContext()['co2_obj'] = new $class('co2_obj'); // @phpstan-ignore offsetAccess.nonOffsetAccessible 61 | $cid = Coroutine::getCid(); // Coroutine::getCid() returns 2. 62 | 63 | echo 'Name of the object in the Context object of coroutine #1: ', Coroutine::getContext(1)['co1_obj']->name, PHP_EOL; // @phpstan-ignore offsetAccess.nonOffsetAccessible,property.nonObject 64 | echo 'Name of the object in the Context object of coroutine #2: ', Coroutine::getContext(2)['co2_obj']->name, PHP_EOL; // @phpstan-ignore offsetAccess.nonOffsetAccessible,property.nonObject 65 | 66 | echo PHP_EOL, 'Coroutine #2 is exiting. The context object of it will be destroyed.', PHP_EOL; 67 | }); 68 | 69 | echo PHP_EOL, 'Coroutine #1 is exiting. The context object of it will be destroyed.', PHP_EOL; 70 | }); 71 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | setRiskyAllowed(true) 7 | ->setRules([ 8 | '@DoctrineAnnotation' => true, 9 | '@PhpCsFixer' => true, 10 | '@PSR2' => true, 11 | '@Symfony' => true, 12 | 'align_multiline_comment' => ['comment_type' => 'all_multiline'], 13 | 'array_syntax' => ['syntax' => 'short'], 14 | 'binary_operator_spaces' => ['operators' => ['=' => 'align', '=>' => 'align', ]], 15 | 'blank_line_after_namespace' => true, 16 | 'blank_line_before_statement' => ['statements' => ['declare']], 17 | 'class_attributes_separation' => true, 18 | 'concat_space' => ['spacing' => 'one'], 19 | 'constant_case' => ['case' => 'lower'], 20 | 'combine_consecutive_unsets' => true, 21 | 'declare_strict_types' => true, 22 | 'general_phpdoc_annotation_remove' => ['annotations' => ['author']], 23 | 'header_comment' => ['comment_type' => 'PHPDoc', 'header' => '', 'separate' => 'bottom', 'location' => 'after_open'], 24 | 'increment_style' => ['style' => 'post'], 25 | 'lambda_not_used_import' => false, 26 | 'linebreak_after_opening_tag' => true, 27 | 'list_syntax' => ['syntax' => 'short'], 28 | 'lowercase_static_reference' => true, 29 | 'multiline_comment_opening_closing' => true, 30 | 'multiline_whitespace_before_semicolons' => ['strategy' => 'new_line_for_chained_calls'], 31 | 'no_unused_imports' => true, 32 | 'no_useless_else' => true, 33 | 'no_useless_return' => true, 34 | 'not_operator_with_space' => false, 35 | 'not_operator_with_successor_space' => false, 36 | 'php_unit_strict' => false, 37 | 'phpdoc_align' => ['align' => 'left'], 38 | 'phpdoc_no_empty_return' => false, 39 | 'phpdoc_types_order' => ['sort_algorithm' => 'none', 'null_adjustment' => 'always_last'], 40 | 'phpdoc_separation' => false, 41 | 'phpdoc_summary' => false, 42 | 'ordered_class_elements' => true, 43 | 'ordered_imports' => ['imports_order' => ['class', 'function', 'const'], 'sort_algorithm' => 'alpha'], 44 | 'single_line_comment_style' => ['comment_types' => []], 45 | 'single_line_empty_body' => false, 46 | 'single_quote' => true, 47 | 'standardize_increment' => false, 48 | 'standardize_not_equals' => true, 49 | 'yoda_style' => ['always_move_variable' => false, 'equal' => false, 'identical' => false], 50 | ]) 51 | ->setFinder(PhpCsFixer\Finder::create()->in(__DIR__ . '/examples')) 52 | ->setUsingCache(false); 53 | -------------------------------------------------------------------------------- /examples/servers/http1.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 41 | [ 42 | Constant::OPTION_DOCUMENT_ROOT => dirname(__DIR__), 43 | Constant::OPTION_ENABLE_STATIC_HANDLER => true, 44 | Constant::OPTION_STATIC_HANDLER_LOCATIONS => [ 45 | '/clients', 46 | '/servers', 47 | ], 48 | 49 | Constant::OPTION_HTTP_COMPRESSION => true, 50 | Constant::OPTION_HTTP_COMPRESSION_LEVEL => 5, 51 | ] 52 | ); 53 | 54 | $server->on( 55 | 'request', 56 | function (Request $request, Response $response): void { 57 | if (!empty($request->get['sleep'])) { 58 | Coroutine::sleep((float) $request->get['sleep']); // Sleep for a while if HTTP query parameter "sleep" presents. 59 | } 60 | 61 | // Next method call is to show how to change HTTP status code from the default one (200) to something else. 62 | $response->status(234, 'Test'); 63 | 64 | $response->end( 65 | <<<'EOT' 66 |
67 | In this example we start an HTTP/1 server. 68 | 69 | NOTE: The autoreloading feature is enabled. If you update this PHP script and 70 | then refresh URL http://127.0.0.1:9501, you should see the changes made. 71 |72 | 73 | EOT 74 | ); 75 | } 76 | ); 77 | 78 | // By default, Swoole sets MIME type of static content based on file extension. For example, for file "foo.txt", Swoole 79 | // sets HTTP header "Content-Type" to "text/plain". For a list of file types that can be recognized by Swoole 80 | // (as of 5.1.0), please check 81 | // https://github.com/swoole/swoole-src/blob/v5.1.0/src/protocol/mime_type.cc#L27-L389 82 | // 83 | // For unknown file type, Swoole set HTTP header "Content-Type" to "application/octet-stream". The following code shows 84 | // how to customize MIME type for "*.moc" files. 85 | swoole_mime_type_add('moc', 'text/plain'); 86 | 87 | $server->start(); 88 | -------------------------------------------------------------------------------- /examples/servers/heartbeat.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 27 | [ 28 | Constant::OPTION_HEARTBEAT_CHECK_INTERVAL => 1, 29 | Constant::OPTION_HEARTBEAT_IDLE_TIME => 3, 30 | ] 31 | ); 32 | $server->on( 33 | 'start', 34 | function (Server $server): void { 35 | file_put_contents('/var/run/sw-heartbeat.pid', $server->master_pid); 36 | } 37 | ); 38 | $server->on( 39 | 'receive', 40 | function (Server $server, int $fd, int $reactorId, string $data): void { 41 | $server->send($fd, 'pong'); 42 | } 43 | ); 44 | $server->on( 45 | 'close', 46 | function (Server $server, int $fd): void { 47 | // In this example we only have one client created, and this client will be closed by the server due to 48 | // timeout. 49 | echo PHP_EOL, "TCP client #{$fd} is closed due to timeout.", PHP_EOL; 50 | } 51 | ); 52 | $server->start(); 53 | 54 | exit; 55 | }, 56 | false 57 | ); 58 | $serverProcess->start(); 59 | 60 | go(function (): void { 61 | // Sleep for 1 second waiting for the TCP server to start. 62 | sleep(1); 63 | 64 | // After the TCP server is started, we make a connection to the server, and send messages to the server every 2 65 | // seconds for 2 times. Both should receive a valid response from the server. 66 | $client = new Client(SWOOLE_SOCK_TCP); 67 | $client->connect('127.0.0.1', 9601); 68 | for ($i = 0; $i < 2; $i++) { 69 | $client->send('ping'); 70 | $data = $client->recv(); 71 | if ($data == 'pong') { 72 | echo 'INFO: Successfully received message "pong" from server side.', PHP_EOL; 73 | } else { 74 | echo 'ERROR: Server side should have sent message "pong" back.', PHP_EOL; 75 | } 76 | sleep(2); 77 | } 78 | 79 | // Then we wait 2 second and send a last message to the server. This message is sent 4 seconds after last message, 80 | // thus the server has closed the connection due to inactivity and we should receive nothing from the server. 81 | sleep(2); 82 | $client->send('ping'); 83 | $data = $client->recv(); 84 | if ($data == 'pong') { 85 | echo PHP_EOL, 'ERROR: Server side should have closed the connection and no message received.', PHP_EOL; 86 | } else { 87 | echo PHP_EOL, 'INFO: Server side has successfully closed the connection and no message received.', PHP_EOL; 88 | } 89 | }); 90 | 91 | // Stop the TCP server and the process created once PHP code finishes execution. 92 | register_shutdown_function(function () use ($serverProcess): void { 93 | Process::kill(intval(shell_exec('cat /var/run/sw-heartbeat.pid')), SIGTERM); 94 | sleep(1); 95 | Process::kill($serverProcess->pid); 96 | }); 97 | 98 | // NOTE: In most cases it's not necessary nor recommended to use method `Swoole\Event::wait()` directly in your code. 99 | // The example in this file is just for demonstration purpose. 100 | Event::wait(); 101 | -------------------------------------------------------------------------------- /examples/pool/process-pool/client.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 1, 26 | Constant::OPTION_PACKAGE_LENGTH_TYPE => 'N', 27 | Constant::OPTION_PACKAGE_LENGTH_OFFSET => 0, 28 | Constant::OPTION_PACKAGE_BODY_OFFSET => 4, 29 | ]; 30 | 31 | // This first example shows how to send messages (and deploy tasks) to a process pool through message queue. 32 | // Swoole extension "async" (https://github.com/swoole/ext-async) is needed to run this example. 33 | if (class_exists(MsgQueue::class)) { 34 | go(function (): void { 35 | $mq = new MsgQueue(0x7000001); // @phpstan-ignore class.notFound 36 | for ($i = 0; $i < 3; $i++) { 37 | // On the server side, you will see output messages like the following: 38 | // Process #0 received message "s:35:"Message #0 via class Swoole\MsgQueue!";". (MSGQUEUE) 39 | $mq->push(sprintf('Message #%d via class %s!', $i, MsgQueue::class)); // @phpstan-ignore class.notFound,class.notFound 40 | } 41 | }); 42 | } 43 | // If your PHP is compiled to support System V messages, you can also use message queue functions msg_get_queue() 44 | // and msg_send() to do that. Check PHP manual https://www.php.net/sem for details. 45 | if (function_exists('msg_get_queue')) { 46 | go(function (): void { 47 | $mq = msg_get_queue(0x7000001); 48 | if ($mq === false) { 49 | echo 'Failed to get message queue.', PHP_EOL; 50 | return; 51 | } 52 | for ($i = 0; $i < 3; $i++) { 53 | // On the server side, you will see output messages like the following: 54 | // Process #0 received message "s:35:"Message #0 via function msg_send()!";". (MSGQUEUE) 55 | msg_send($mq, 1, sprintf('Message #%d via function msg_send()!', $i)); 56 | } 57 | }); 58 | } 59 | 60 | // This second example shows how to send messages (and deploy tasks) to a process pool through TCP socket. 61 | go(function () use ($settings): void { 62 | $client = new Client(SWOOLE_SOCK_TCP); 63 | $client->set($settings); 64 | $client->connect('server', 9701); 65 | $requestMessage = 'TCP socket'; 66 | $client->send(pack('N', strlen($requestMessage)) . $requestMessage); 67 | $responseMessage = $client->recv(); 68 | $responseMessage = substr($responseMessage, 4, unpack('N', substr($responseMessage, 0, 4))[1]); // @phpstan-ignore-line 69 | echo $responseMessage, PHP_EOL; 70 | $client->close(); 71 | }); 72 | 73 | // This third example shows how to send messages (and deploy tasks) to a process pool through Unix socket. 74 | // Since Unix socket is faster than TCP socket (in general), this coroutine should finish first than the previous one. 75 | go(function () use ($settings): void { 76 | $client = new Client(SWOOLE_SOCK_UNIX_STREAM); 77 | $client->set($settings); 78 | $client->connect('/var/run/pool-unix-socket.sock'); 79 | $requestMessage = 'Unix socket'; 80 | $client->send(pack('N', strlen($requestMessage)) . $requestMessage); 81 | $responseMessage = $client->recv(); 82 | $responseMessage = substr($responseMessage, 4, unpack('N', substr($responseMessage, 0, 4))[1]); // @phpstan-ignore-line 83 | echo $responseMessage, PHP_EOL; 84 | $client->close(); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /examples/servers/http1-integrated.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | set( 29 | [ 30 | Constant::OPTION_WORKER_NUM => 2, 31 | Constant::OPTION_TASK_WORKER_NUM => swoole_cpu_num(), 32 | ] 33 | ); 34 | 35 | $server->on( 36 | 'start', 37 | function (Server $server): void { 38 | echo '[HTTP1-ADVANCED]: # of CPU units: ', swoole_cpu_num(), PHP_EOL; 39 | 40 | // Here we start the first cron job that runs every 61 seconds. 41 | Timer::tick( 42 | 1000 * 61, 43 | function (): void { 44 | echo '[HTTP1-ADVANCED]: This message is printed out every 61 seconds. (', date('H:i:s'), ')', PHP_EOL; 45 | } 46 | ); 47 | } 48 | ); 49 | $server->on( 50 | 'workerStart', 51 | function (Server $server, int $workerId): void { 52 | echo "[HTTP1-ADVANCED] Worker #{$workerId} is started.", PHP_EOL; 53 | if ($workerId === 0) { 54 | // Here we start the second cron job that runs every 63 seconds. 55 | Coroutine::create(function (): void { 56 | while (true) { // @phpstan-ignore while.alwaysTrue 57 | Coroutine::sleep(63); 58 | echo '[HTTP1-ADVANCED]: This message is printed out every 63 seconds. (', date('H:i:s'), ')', PHP_EOL; 59 | } 60 | }); 61 | } 62 | } 63 | ); 64 | $server->on( 65 | 'request', 66 | function (Request $request, Response $response) use ($server): void { 67 | $type = $request->get['type'] ?? ''; 68 | switch ($type) { 69 | case 'task': 70 | // To deploy an asynchronous task. 71 | $server->task((object) ['type' => 'task']); 72 | $response->end($type . PHP_EOL); 73 | break; 74 | case 'taskwait': 75 | // To deploy an asynchronous task, and wait until it finishes. 76 | $result = $server->taskwait(['type' => 'taskwait']); 77 | $response->end($type . PHP_EOL); 78 | break; 79 | case 'taskWaitMulti': 80 | // To deploy multiple asynchronous tasks, and wait until they finish. (legacy implementation) 81 | $server->taskWaitMulti(['taskWaitMulti #0', 'taskWaitMulti #1', 'taskWaitMulti #2']); 82 | $response->end($type . PHP_EOL); 83 | break; 84 | case 'taskCo': 85 | // To deploy multiple asynchronous tasks, and wait until they finish. 86 | $result = $server->taskCo( 87 | [ 88 | 'taskCo #0', 89 | ['type' => 'taskCo #1'], 90 | (object) ['type' => 'taskCo #2'], 91 | ] 92 | ); 93 | $response->end(print_r($result, true)); 94 | break; 95 | default: 96 | // To deploy an asynchronous task, and process the response through a callback function. 97 | $server->task('taskCallback', -1, function (Server $server, int $taskId, $data) use ($response): void { 98 | $response->end($data . PHP_EOL); 99 | }); 100 | break; 101 | } 102 | } 103 | ); 104 | $server->on( 105 | 'task', 106 | function (Server $server, int $taskId, int $reactorId, $data) { 107 | echo 'Task received with incoming data (serialized already): ', serialize($data), PHP_EOL; 108 | 109 | return $data; 110 | } 111 | ); 112 | $server->on( 113 | 'finish', 114 | function (Server $server, int $taskId, $data) { 115 | echo 'Task returned with data (serialized already): ', serialize($data), PHP_EOL; 116 | 117 | return $data; 118 | } 119 | ); 120 | 121 | $server->start(); 122 | -------------------------------------------------------------------------------- /examples/servers/server-events.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | reload()"), there are four types of events triggered: 13 | * a). Event "onBeforeReload" and "onAfterReload" are triggered in the manager process. 14 | * b). Event "onWorkerExit" and "onWorkerStart" are triggered in each worker process and task worker process. 15 | * c). Event "onAfterReload" doesn't necessarily always happen before/after the "onWorkerStart" events, since they 16 | * run in different processes. 17 | * 3. About "onWorker*" events: 18 | * a) Event "onWorkerStart" happens both in worker processes and in task worker processes. 19 | * a) Event "onWorkerStop" happens in worker processes only. It won't happen in task worker processes. 20 | * a) Event "onWorkerExit" happens only when option \Swoole\Constant::OPTION_RELOAD_ASYNC is turned on. 21 | * a) Event "onWorkerError" happens in the manager process. 22 | * 4. Event "onReceive" is triggered in TCP servers, while event "onPacket" is triggered in UDP servers only. They both 23 | * happen after event "onConnect" but before event "onClose". 24 | * 5. Event "onRequest" is only to process HTTP/1, HTTP/2, and WebSocket requests. 25 | * 6. Event "onTask" is triggered in task worker processes only, while event "onFinish" is triggered in worker processes 26 | * only. Event "onFinish" is triggered only when both conditions are met: 27 | * a) The task is triggered by method call "$server->task()". 28 | * b) Inside the callback function of the "onTask" event, either method call "$task->finish()" is executed, or a 29 | * return statement is used to return something back. 30 | * 7. There are some events not triggered in this example: 31 | * a) Event "onPacket". It is triggered in UDP servers only. 32 | * b) Event "onHandshake", "onOpen", "onMessage", and "onDisconnect". They are to process WebSocket requests. 33 | * 34 | * How to run this script: 35 | * docker run --rm -v $(pwd):/var/www -ti phpswoole/swoole php ./examples/servers/server-events.php 36 | */ 37 | 38 | use Swoole\Constant; 39 | use Swoole\Coroutine; 40 | use Swoole\Http\Request; 41 | use Swoole\Http\Response; 42 | use Swoole\Http\Server; 43 | use Swoole\Timer; 44 | 45 | function printMessage(string $message, bool $newLine = false): void 46 | { 47 | echo ($newLine ? PHP_EOL : ''), 'INFO (', date('H:i:s'), "): {$message}", PHP_EOL; 48 | } 49 | 50 | $server = new Server('0.0.0.0', 9509); 51 | $server->set( 52 | [ 53 | Constant::OPTION_WORKER_NUM => 1, // One worker process to process HTTP requests. Its worker ID is "0". 54 | Constant::OPTION_TASK_WORKER_NUM => 1, // One task worker process to process tasks. Its worker ID is "1". 55 | ] 56 | ); 57 | 58 | $server->on('start', function (Server $server): void { 59 | printMessage('Event "onStart" is triggered.'); 60 | }); 61 | 62 | $server->on('managerStart', function (Server $server): void { 63 | printMessage('Event "onManagerStart" is triggered.'); 64 | 65 | // To make an HTTP request to the sever itself 50 milliseconds later. 66 | Timer::after(50, function (): void { 67 | printMessage('Make an HTTP request to the server.' . PHP_EOL, true); 68 | $ch = curl_init(); 69 | curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:9509'); 70 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 71 | curl_exec($ch); 72 | curl_close($ch); 73 | }); 74 | 75 | // To reload the HTTP server 100 milliseconds later. 76 | Timer::after(100, function () use ($server): void { 77 | printMessage('Reload the server.' . PHP_EOL, true); 78 | $server->reload(); 79 | }); 80 | 81 | // To shutdown the HTTP server 150 milliseconds later. 82 | Timer::after(150, function () use ($server): void { 83 | printMessage('Shutdown the server.' . PHP_EOL, true); 84 | $server->shutdown(); 85 | }); 86 | }); 87 | $server->on('managerStop', function (Server $server): void { 88 | printMessage('Event "onManagerStop" is triggered.'); 89 | }); 90 | 91 | $server->on('workerStart', function (Server $server, int $workerId): void { 92 | printMessage("Event \"onWorkerStart\" is triggered in worker #{$workerId}."); 93 | }); 94 | $server->on('workerStop', function (Server $server, int $workerId): void { 95 | printMessage("Event \"onWorkerStop\" is triggered in worker #{$workerId}."); 96 | }); 97 | $server->on('workerError', function (Server $server, int $workerId, int $exitCode, int $signal): void { 98 | printMessage("Event \"onWorkerError\" is triggered in worker #{$workerId}."); 99 | }); 100 | $server->on('workerExit', function (Server $server, int $workerId): void { 101 | printMessage("Event \"onWorkerExit\" is triggered in worker #{$workerId}."); 102 | }); 103 | 104 | $server->on('connect', function (Server $server, int $fd, int $reactorId): void { 105 | printMessage('Event "onConnect" is triggered.'); 106 | }); 107 | $server->on('receive', function (Server $server, int $fd, int $reactorId, string $data): void { 108 | printMessage('Event "onReceive" is triggered.'); 109 | }); 110 | $server->on('close', function (Server $server, int $fd, int $reactorId): void { 111 | printMessage('Event "onClose" is triggered.' . PHP_EOL); 112 | }); 113 | 114 | $server->on('request', function (Request $request, Response $response) use ($server): void { 115 | printMessage('Event "onRequest" is triggered.'); 116 | $response->end('OK' . PHP_EOL); 117 | Coroutine::create(function () use ($server): void { 118 | Coroutine::sleep(0.01); 119 | $server->task('Hello, World!'); 120 | 121 | Coroutine::sleep(0.01); 122 | $server->sendMessage('Hello, World!', 1); 123 | }); 124 | }); 125 | 126 | $server->on('task', function (Server $server, int $taskId, int $srcWorkerId, $data): void { 127 | printMessage('Event "onTask" is triggered.'); 128 | 129 | // This is to trigger the "onFinish" event. 130 | // Please note that "onTask" events are processed in task worker processes, while "onFinish" events are processed 131 | // in event worker processes (where method call $server->task*() was made to trigger the "onTask" events). 132 | $server->finish('Hello World!'); 133 | }); 134 | $server->on('finish', function (Server $server, int $taskId, $data): void { 135 | printMessage('Event "onFinish" is triggered.'); 136 | }); 137 | $server->on('pipeMessage', function (Server $server, int $srcWorkerId, $message) { 138 | printMessage('Event "onPipeMessage" is triggered.'); 139 | return $message; 140 | }); 141 | 142 | $server->on('beforeReload', function (Server $server): void { 143 | printMessage('Event "onBeforeReload" is triggered.'); 144 | }); 145 | $server->on('afterReload', function (Server $server): void { 146 | printMessage('Event "onAfterReload" is triggered.'); 147 | }); 148 | 149 | $server->on('shutdown', function (Server $server): void { 150 | printMessage('Event "onShutdown" is triggered.' . PHP_EOL); 151 | }); 152 | 153 | printMessage('Start the server.' . PHP_EOL, true); 154 | $server->start(); 155 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swoole by Examples 2 | 3 | [](https://creativecommons.org/licenses/by-nc-nd/4.0/) 4 | 5 | The repository is to help developers to get familiar with [Swoole](https://github.com/swoole/swoole-src) through a 6 | variety of examples. All the examples are fully functioning; they can be executed and verified using the Docker images 7 | provided. 8 | 9 | NOTE: I'm adding examples for latest versions of Swoole, so please be patient. 10 | 11 | ## Setup the Development Environment 12 | 13 | We use Docker to setup our development environment. Other than Docker, you don't need to install any other software to 14 | run and test the examples: you don't need to have PHP, Swoole, Composer, or some other software installed locally. 15 | 16 | We use [the official Docker image of Swoole](https://hub.docker.com/r/phpswoole/swoole) to run the examples. There are 17 | tens of examples under repository [swoole/docker-swoole](https://github.com/swoole/docker-swoole) shown how to use the 18 | image. Please spend some time checking it first. 19 | 20 | Before running the examples, please run command `docker-compose up -d` under the root repository directory to start the 21 | Docker containers. There are two containers used to run the examples: 22 | 23 | * a server container where application servers are running. 24 | * a client container where client-side scripts should be executed. 25 | 26 | Both containers have the same PHP scripts in place, so most standalone scripts (e.g., most CSP programming examples) can be 27 | executed from either container. Once the containers are running, you can use one of following commands to get a Bash shell 28 | in the containers: 29 | 30 | ```bash 31 | docker compose exec -ti server bash # Get a Bash shell in the server container. 32 | docker compose exec -ti client bash # Get a Bash shell in the client container. 33 | ``` 34 | 35 | ## List of Examples 36 | 37 | * CSP programming 38 | * from blocking I/O to non-blocking I/O 39 | * The blocking version can be found [here](https://github.com/deminy/swoole-by-examples/blob/master/examples/io/blocking-io.php). 40 | * The non-blocking version of the same script can be found [here](https://github.com/deminy/swoole-by-examples/blob/master/examples/io/non-blocking-io.php). You can also check [this script](https://github.com/deminy/swoole-by-examples/blob/master/examples/io/non-blocking-io-debug.php) to see how the non-blocking version is executed in order. 41 | * [This example](https://github.com/deminy/swoole-by-examples/blob/master/examples/io/blocking-vs-non-blocking.php) shows how the _return_ statement is treated differently in Swoole. As you can see in the example, a function call could return a value back first before finishing its execution. 42 | * coroutines 43 | * create coroutines 44 | * [use different types of callbacks when creating coroutines](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/coroutines/creation-2.php) 45 | * [use different functions/methods to create coroutines](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/coroutines/creation-1.php) 46 | * enable coroutines 47 | * [coroutines in a for loop](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/coroutines/for.php) 48 | * [nested coroutines](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/coroutines/nested.php) 49 | * [exit from coroutines](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/coroutines/exit.php) 50 | * [yield and resume coroutines](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/coroutines/yield-and-resume.php) 51 | * [context](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/context.php): How to use the [Context](https://github.com/swoole/ide-helper/blob/master/src/swoole/Swoole/Coroutine/Context.php) objects in coroutines. 52 | * [benchmark](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/coroutines/benchmark.php): In this example we create 1,000,000 coroutines in a single process; each coroutine sleeps for 5 seconds. 53 | * channels 54 | * [basic usage](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/channel.php) 55 | * [class \Swoole\Coroutine\WaitGroup](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/waitgroup.php) (like [the WaitGroup type in Golang](https://golang.org/pkg/sync/#WaitGroup)) 56 | * [defer](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/defer.php) 57 | * runtime hooks 58 | * [configure and utilize different runtime hook flags in Swoole](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/hook-flags.php) 59 | * curl. There are two different ways to hook curl functions: 60 | * [Option SWOOLE_HOOK_NATIVE_CURL](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/native-curl.php) (recommended) 61 | * [Option SWOOLE_HOOK_CURL](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/curl.php): This approach is implemented through [Swoole Library](https://github.com/swoole/library); however, it doesn't work for _curl_multi_*_ functions. 62 | * The `mysqli` extension. 63 | * [hook _mysqli_ functions](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/mysqli.php) 64 | * The `PDO` (PHP Data Objects) extension. 65 | * [hook _PDO MySQL_ functionsaccess MySQL databases](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/pdo_mysql.php) 66 | * [hook _PDO_PGSQL_ functions to access PostgreSQL databases](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/pdo_pgsql.php) 67 | * [hook _PDO_SQLITE_ functions to access SQLite 3 databases](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/pdo_sqlite.php) 68 | * Redis clients 69 | * phpredis - [concurrent connections/operations using phpredis](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/redis/phpredis.php) 70 | * predis - [concurrent connections/operations using predis](https://github.com/deminy/swoole-by-examples/blob/master/examples/hooks/redis/predis.php) 71 | * locks 72 | * [use a lock across coroutines](https://github.com/deminy/swoole-by-examples/blob/master/examples/locks/lock-across-coroutines.php) (Swoole v6.0.1+ only) 73 | * [use a lock across processes](https://github.com/deminy/swoole-by-examples/blob/master/examples/locks/lock-across-processes.php) 74 | * [use a lock across threads](https://github.com/deminy/swoole-by-examples/blob/master/examples/locks/lock-across-threads.php) (Swoole v6.0.0+ only) 75 | * deadlocks 76 | * examples on deadlocks 77 | * [pop data from an empty channel](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/an-empty-channel.php) 78 | * [push data to a full channel](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/channel-is-full.php) 79 | * [try to lock a locked file while the existing lock never gets released](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/file-locking.php) 80 | * [acquire a locked lock from another coroutine](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/swoole-lock.php) 81 | * [improperly shutdown or reload a server](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/server-shutdown.php) 82 | * When the only coroutine yields its execution. The examples are shown in the next section when we talk about `How to detect/handle deadlocks`. 83 | * How to detect/handle deadlocks. In the following examples, we trigger deadlocks by yielding the execution of the only coroutine in the program. 84 | * [show deadlock information (the default behavior)](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/coroutine-yielded-1.php) 85 | * [hide deadlock information](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/coroutine-yielded-2.php) 86 | * [set a customized exit condition](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/deadlocks/coroutine-yielded-3.php) 87 | * advanced topics 88 | * CPU-intensive job scheduling 89 | 1. [non-preemptive scheduling](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/scheduling/non-preemptive.php) 90 | 2. [preemptive scheduling](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/scheduling/preemptive.php) 91 | 3. [mixed scheduling](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/scheduling/mixed.php) 92 | * [class \Swoole\Coroutine\Barrier](https://github.com/deminy/swoole-by-examples/blob/master/examples/csp/barrier.php) 93 | * block coroutines/processes 94 | * [block a coroutine](https://github.com/deminy/swoole-by-examples/blob/master/examples/io/block-a-coroutine.php) 95 | * [block a process using class \Swoole\Lock](https://github.com/deminy/swoole-by-examples/blob/master/examples/io/block-a-process-using-swoole-lock.php) 96 | * [block processes using class \Swoole\Lock](https://github.com/deminy/swoole-by-examples/blob/master/examples/io/block-processes-using-swoole-lock.php) 97 | * [block processes using class \Swoole\Atomic](https://github.com/deminy/swoole-by-examples/blob/master/examples/io/block-processes-using-swoole-atomic.php) 98 | * unit tests 99 | * server-side programming 100 | * application servers 101 | * [HTTP/1 server](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/http1.php): support gzip compression, serving static content, customizing status code, etc. 102 | * [HTTP/2 server](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/http2.php) 103 | * HTTP/2 server push 104 | * [WebSocket server](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/websocket.php) 105 | * [Redis server](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/redis.php) 106 | * proxy server 107 | * TCP server 108 | * [event-driven style](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/tcp1.php) 109 | * [coroutine style](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/tcp2.php) 110 | * [UDP server](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/udp.php) 111 | * resource pooling 112 | * process pool 113 | * [standalone](https://github.com/deminy/swoole-by-examples/blob/master/examples/pool/process-pool/pool-standalone.php) 114 | * [using message queue](https://github.com/deminy/swoole-by-examples/blob/master/examples/pool/process-pool/pool-msgqueue.php) 115 | * [using TCP socket](https://github.com/deminy/swoole-by-examples/blob/master/examples/pool/process-pool/pool-tcp-socket.php) 116 | * [using Unix socket](https://github.com/deminy/swoole-by-examples/blob/master/examples/pool/process-pool/pool-unix-socket.php) 117 | * connection pool 118 | * [MySQL connection pool](https://github.com/deminy/swoole-by-examples/blob/master/examples/pool/database-pool/mysqli.php) 119 | * [PostgreSQL connection pool](https://github.com/deminy/swoole-by-examples/blob/master/examples/pool/database-pool/pdo_pgsql.php) 120 | * [Redis connection pool](https://github.com/deminy/swoole-by-examples/blob/master/examples/pool/database-pool/redis.php) 121 | * How to implement a customized connection pool? Check package [crowdstar/vertica-swoole-adapter](https://github.com/Crowdstar/vertica-swoole-adapter) for details. This package implements connection pool for HP Vertica databases through ODBC, and it's maintained by me. 122 | * network connection detection (dead network detection) 123 | * [heartbeat](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/heartbeat.php) 124 | * [TCP keepalive](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/keepalive.php) 125 | * task scheduling and handling 126 | * [timer](https://github.com/deminy/swoole-by-examples/blob/master/examples/timer/timer.php) 127 | * There is [a 2nd example](https://github.com/deminy/swoole-by-examples/blob/master/examples/timer/coroutine-style.php) included to show how to implement timer using coroutines only. 128 | * To see how to setup cronjobs using the _\Swoole\Timer_ class in an application server, please check [integrated HTTP/1 server](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/http1-integrated.php). 129 | * cronjobs 130 | * benchmark 131 | * base mode vs multi-process mode 132 | * advanced topics 133 | * [Rock Paper Scissors](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/rock-paper-scissors.php): implement the hand game Rock Paper Scissors using Swoole. 134 | * [How are different server events triggered?](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/server-events.php) 135 | * Server Combo (two different implementations) 136 | * [integrated HTTP/1 server](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/http1-integrated.php): an HTTP/1 server that supports cron jobs and synchronous/asynchronous tasks. 137 | * [integrated WebSocket server](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/websocket-integrated.php): a WebSocket server that supports cron jobs and asynchronous tasks. This implementation use separate processes to handle cron jobs and task queues. 138 | * mixed protocols 139 | * [support HTTP/1, HTTP/2, and WebSocket on same port](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/mixed-protocols-1.php) 140 | * support multiple protocols on same server 141 | * [DDoS protection](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/ddos-protection.php): How to protect your Swoole-based application server from DDoS attacks. 142 | * [interruptible sleep](https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/interruptible-sleep.php): This example shows how to set up a cronjob in a web server, and allow the cronjob to execute at a last time when the server is shutting down. 143 | * multiple ports listening 144 | * built-in clients provided by Swoole 145 | * [HTTP/1 client](https://github.com/deminy/swoole-by-examples/blob/master/examples/clients/http1.php) 146 | * HTTP/2 client 147 | * [WebSocket client](https://github.com/deminy/swoole-by-examples/blob/master/examples/clients/websocket.php) 148 | * [TCP client](https://github.com/deminy/swoole-by-examples/blob/master/examples/clients/tcp.php) 149 | * [UDP client](https://github.com/deminy/swoole-by-examples/blob/master/examples/clients/udp.php) 150 | * miscellaneous topics 151 | * data management in Swoole: globals, persistence, and caching 152 | * [APCu caching]: APCu caching in Swoole works the same way as in other PHP CLI applications. This example explains it in details. 153 | * atomic counters 154 | * [implement atomic counters using unsigned 32-bit integers](https://github.com/deminy/swoole-by-examples/blob/master/examples/misc/atomic-counter-unsigned-32-bit.php) 155 | * [implement atomic counters using signed 64-bit integers](https://github.com/deminy/swoole-by-examples/blob/master/examples/misc/atomic-counter-signed-64-bit.php) 156 | * multiprocessing 157 | * wait and wakeup processes 158 | * process pool 159 | * pool creation and inter-process communication: Please check previous section `resource pooling` for details. 160 | * detach processes from a process pool 161 | 162 | [APCu Caching]: https://github.com/deminy/swoole-by-examples/blob/master/examples/servers/apcu-caching.php 163 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Attribution-NonCommercial-NoDerivatives 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 58 | International Public License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-NonCommercial-NoDerivatives 4.0 International Public 63 | License ("Public License"). To the extent this Public License may be 64 | interpreted as a contract, You are granted the Licensed Rights in 65 | consideration of Your acceptance of these terms and conditions, and the 66 | Licensor grants You such rights in consideration of benefits the 67 | Licensor receives from making the Licensed Material available under 68 | these terms and conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Copyright and Similar Rights means copyright and/or similar rights 84 | closely related to copyright including, without limitation, 85 | performance, broadcast, sound recording, and Sui Generis Database 86 | Rights, without regard to how the rights are labeled or 87 | categorized. For purposes of this Public License, the rights 88 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 89 | Rights. 90 | 91 | c. Effective Technological Measures means those measures that, in the 92 | absence of proper authority, may not be circumvented under laws 93 | fulfilling obligations under Article 11 of the WIPO Copyright 94 | Treaty adopted on December 20, 1996, and/or similar international 95 | agreements. 96 | 97 | d. Exceptions and Limitations means fair use, fair dealing, and/or 98 | any other exception or limitation to Copyright and Similar Rights 99 | that applies to Your use of the Licensed Material. 100 | 101 | e. Licensed Material means the artistic or literary work, database, 102 | or other material to which the Licensor applied this Public 103 | License. 104 | 105 | f. Licensed Rights means the rights granted to You subject to the 106 | terms and conditions of this Public License, which are limited to 107 | all Copyright and Similar Rights that apply to Your use of the 108 | Licensed Material and that the Licensor has authority to license. 109 | 110 | g. Licensor means the individual(s) or entity(ies) granting rights 111 | under this Public License. 112 | 113 | h. NonCommercial means not primarily intended for or directed towards 114 | commercial advantage or monetary compensation. For purposes of 115 | this Public License, the exchange of the Licensed Material for 116 | other material subject to Copyright and Similar Rights by digital 117 | file-sharing or similar means is NonCommercial provided there is 118 | no payment of monetary compensation in connection with the 119 | exchange. 120 | 121 | i. Share means to provide material to the public by any means or 122 | process that requires permission under the Licensed Rights, such 123 | as reproduction, public display, public performance, distribution, 124 | dissemination, communication, or importation, and to make material 125 | available to the public including in ways that members of the 126 | public may access the material from a place and at a time 127 | individually chosen by them. 128 | 129 | j. Sui Generis Database Rights means rights other than copyright 130 | resulting from Directive 96/9/EC of the European Parliament and of 131 | the Council of 11 March 1996 on the legal protection of databases, 132 | as amended and/or succeeded, as well as other essentially 133 | equivalent rights anywhere in the world. 134 | 135 | k. You means the individual or entity exercising the Licensed Rights 136 | under this Public License. Your has a corresponding meaning. 137 | 138 | 139 | Section 2 -- Scope. 140 | 141 | a. License grant. 142 | 143 | 1. Subject to the terms and conditions of this Public License, 144 | the Licensor hereby grants You a worldwide, royalty-free, 145 | non-sublicensable, non-exclusive, irrevocable license to 146 | exercise the Licensed Rights in the Licensed Material to: 147 | 148 | a. reproduce and Share the Licensed Material, in whole or 149 | in part, for NonCommercial purposes only; and 150 | 151 | b. produce and reproduce, but not Share, Adapted Material 152 | for NonCommercial purposes only. 153 | 154 | 2. Exceptions and Limitations. For the avoidance of doubt, where 155 | Exceptions and Limitations apply to Your use, this Public 156 | License does not apply, and You do not need to comply with 157 | its terms and conditions. 158 | 159 | 3. Term. The term of this Public License is specified in Section 160 | 6(a). 161 | 162 | 4. Media and formats; technical modifications allowed. The 163 | Licensor authorizes You to exercise the Licensed Rights in 164 | all media and formats whether now known or hereafter created, 165 | and to make technical modifications necessary to do so. The 166 | Licensor waives and/or agrees not to assert any right or 167 | authority to forbid You from making technical modifications 168 | necessary to exercise the Licensed Rights, including 169 | technical modifications necessary to circumvent Effective 170 | Technological Measures. For purposes of this Public License, 171 | simply making modifications authorized by this Section 2(a) 172 | (4) never produces Adapted Material. 173 | 174 | 5. Downstream recipients. 175 | 176 | a. Offer from the Licensor -- Licensed Material. Every 177 | recipient of the Licensed Material automatically 178 | receives an offer from the Licensor to exercise the 179 | Licensed Rights under the terms and conditions of this 180 | Public License. 181 | 182 | b. No downstream restrictions. You may not offer or impose 183 | any additional or different terms or conditions on, or 184 | apply any Effective Technological Measures to, the 185 | Licensed Material if doing so restricts exercise of the 186 | Licensed Rights by any recipient of the Licensed 187 | Material. 188 | 189 | 6. No endorsement. Nothing in this Public License constitutes or 190 | may be construed as permission to assert or imply that You 191 | are, or that Your use of the Licensed Material is, connected 192 | with, or sponsored, endorsed, or granted official status by, 193 | the Licensor or others designated to receive attribution as 194 | provided in Section 3(a)(1)(A)(i). 195 | 196 | b. Other rights. 197 | 198 | 1. Moral rights, such as the right of integrity, are not 199 | licensed under this Public License, nor are publicity, 200 | privacy, and/or other similar personality rights; however, to 201 | the extent possible, the Licensor waives and/or agrees not to 202 | assert any such rights held by the Licensor to the limited 203 | extent necessary to allow You to exercise the Licensed 204 | Rights, but not otherwise. 205 | 206 | 2. Patent and trademark rights are not licensed under this 207 | Public License. 208 | 209 | 3. To the extent possible, the Licensor waives any right to 210 | collect royalties from You for the exercise of the Licensed 211 | Rights, whether directly or through a collecting society 212 | under any voluntary or waivable statutory or compulsory 213 | licensing scheme. In all other cases the Licensor expressly 214 | reserves any right to collect such royalties, including when 215 | the Licensed Material is used other than for NonCommercial 216 | purposes. 217 | 218 | 219 | Section 3 -- License Conditions. 220 | 221 | Your exercise of the Licensed Rights is expressly made subject to the 222 | following conditions. 223 | 224 | a. Attribution. 225 | 226 | 1. If You Share the Licensed Material, You must: 227 | 228 | a. retain the following if it is supplied by the Licensor 229 | with the Licensed Material: 230 | 231 | i. identification of the creator(s) of the Licensed 232 | Material and any others designated to receive 233 | attribution, in any reasonable manner requested by 234 | the Licensor (including by pseudonym if 235 | designated); 236 | 237 | ii. a copyright notice; 238 | 239 | iii. a notice that refers to this Public License; 240 | 241 | iv. a notice that refers to the disclaimer of 242 | warranties; 243 | 244 | v. a URI or hyperlink to the Licensed Material to the 245 | extent reasonably practicable; 246 | 247 | b. indicate if You modified the Licensed Material and 248 | retain an indication of any previous modifications; and 249 | 250 | c. indicate the Licensed Material is licensed under this 251 | Public License, and include the text of, or the URI or 252 | hyperlink to, this Public License. 253 | 254 | For the avoidance of doubt, You do not have permission under 255 | this Public License to Share Adapted Material. 256 | 257 | 2. You may satisfy the conditions in Section 3(a)(1) in any 258 | reasonable manner based on the medium, means, and context in 259 | which You Share the Licensed Material. For example, it may be 260 | reasonable to satisfy the conditions by providing a URI or 261 | hyperlink to a resource that includes the required 262 | information. 263 | 264 | 3. If requested by the Licensor, You must remove any of the 265 | information required by Section 3(a)(1)(A) to the extent 266 | reasonably practicable. 267 | 268 | 269 | Section 4 -- Sui Generis Database Rights. 270 | 271 | Where the Licensed Rights include Sui Generis Database Rights that 272 | apply to Your use of the Licensed Material: 273 | 274 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 275 | to extract, reuse, reproduce, and Share all or a substantial 276 | portion of the contents of the database for NonCommercial purposes 277 | only and provided You do not Share Adapted Material; 278 | 279 | b. if You include all or a substantial portion of the database 280 | contents in a database in which You have Sui Generis Database 281 | Rights, then the database in which You have Sui Generis Database 282 | Rights (but not its individual contents) is Adapted Material; and 283 | 284 | c. You must comply with the conditions in Section 3(a) if You Share 285 | all or a substantial portion of the contents of the database. 286 | 287 | For the avoidance of doubt, this Section 4 supplements and does not 288 | replace Your obligations under this Public License where the Licensed 289 | Rights include other Copyright and Similar Rights. 290 | 291 | 292 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 293 | 294 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 295 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 296 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 297 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 298 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 299 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 300 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 301 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 302 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 303 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 304 | 305 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 306 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 307 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 308 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 309 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 310 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 311 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 312 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 313 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 314 | 315 | c. The disclaimer of warranties and limitation of liability provided 316 | above shall be interpreted in a manner that, to the extent 317 | possible, most closely approximates an absolute disclaimer and 318 | waiver of all liability. 319 | 320 | 321 | Section 6 -- Term and Termination. 322 | 323 | a. This Public License applies for the term of the Copyright and 324 | Similar Rights licensed here. However, if You fail to comply with 325 | this Public License, then Your rights under this Public License 326 | terminate automatically. 327 | 328 | b. Where Your right to use the Licensed Material has terminated under 329 | Section 6(a), it reinstates: 330 | 331 | 1. automatically as of the date the violation is cured, provided 332 | it is cured within 30 days of Your discovery of the 333 | violation; or 334 | 335 | 2. upon express reinstatement by the Licensor. 336 | 337 | For the avoidance of doubt, this Section 6(b) does not affect any 338 | right the Licensor may have to seek remedies for Your violations 339 | of this Public License. 340 | 341 | c. For the avoidance of doubt, the Licensor may also offer the 342 | Licensed Material under separate terms or conditions or stop 343 | distributing the Licensed Material at any time; however, doing so 344 | will not terminate this Public License. 345 | 346 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 347 | License. 348 | 349 | 350 | Section 7 -- Other Terms and Conditions. 351 | 352 | a. The Licensor shall not be bound by any additional or different 353 | terms or conditions communicated by You unless expressly agreed. 354 | 355 | b. Any arrangements, understandings, or agreements regarding the 356 | Licensed Material not stated herein are separate from and 357 | independent of the terms and conditions of this Public License. 358 | 359 | 360 | Section 8 -- Interpretation. 361 | 362 | a. For the avoidance of doubt, this Public License does not, and 363 | shall not be interpreted to, reduce, limit, restrict, or impose 364 | conditions on any use of the Licensed Material that could lawfully 365 | be made without permission under this Public License. 366 | 367 | b. To the extent possible, if any provision of this Public License is 368 | deemed unenforceable, it shall be automatically reformed to the 369 | minimum extent necessary to make it enforceable. If the provision 370 | cannot be reformed, it shall be severed from this Public License 371 | without affecting the enforceability of the remaining terms and 372 | conditions. 373 | 374 | c. No term or condition of this Public License will be waived and no 375 | failure to comply consented to unless expressly agreed to by the 376 | Licensor. 377 | 378 | d. Nothing in this Public License constitutes or may be interpreted 379 | as a limitation upon, or waiver of, any privileges and immunities 380 | that apply to the Licensor or You, including from the legal 381 | processes of any jurisdiction or authority. 382 | 383 | ======================================================================= 384 | 385 | Creative Commons is not a party to its public 386 | licenses. Notwithstanding, Creative Commons may elect to apply one of 387 | its public licenses to material it publishes and in those instances 388 | will be considered the “Licensor.” The text of the Creative Commons 389 | public licenses is dedicated to the public domain under the CC0 Public 390 | Domain Dedication. Except for the limited purpose of indicating that 391 | material is shared under a Creative Commons public license or as 392 | otherwise permitted by the Creative Commons policies published at 393 | creativecommons.org/policies, Creative Commons does not authorize the 394 | use of the trademark "Creative Commons" or any other trademark or logo 395 | of Creative Commons without its prior written consent including, 396 | without limitation, in connection with any unauthorized modifications 397 | to any of its public licenses or any other arrangements, 398 | understandings, or agreements concerning use of licensed material. For 399 | the avoidance of doubt, this paragraph does not form part of the 400 | public licenses. 401 | 402 | Creative Commons may be contacted at creativecommons.org. 403 | --------------------------------------------------------------------------------