From 92623941af0aa48b2c94164ed6ac920073a8c512 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Wed, 3 Dec 2025 23:25:41 -0500 Subject: [PATCH 1/7] feat: add gRPC server configuration and initial implementation with example proto --- .rr.yaml | 7 +++ Dockerfile | 1 + composer.json | 6 +- composer.lock | 142 ++++++++++++++++++++++++++++++++++++++++++- generated/README.md | 3 + makefile | 70 +++++++++++++++++++++ protos/example.proto | 23 +++++++ src/Grpc.php | 34 +++++++++++ 8 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 generated/README.md create mode 100644 makefile create mode 100644 protos/example.proto create mode 100644 src/Grpc.php diff --git a/.rr.yaml b/.rr.yaml index 186b19e..7155674 100644 --- a/.rr.yaml +++ b/.rr.yaml @@ -6,6 +6,13 @@ server: rpc: listen: tcp://127.0.0.1:6001 +grpc: + listen: "tcp://0.0.0.0:9001" + pool: + command: "php grpc-worker.php" + proto: + - "proto/helloworld.proto" + http: pool: allocate_timeout: 5s diff --git a/Dockerfile b/Dockerfile index 1fbd4b6..9b86277 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,6 +42,7 @@ COPY --from=library /app/vendor /app/vendor # Copy the RoadRunner configuration file and source ADD src src/ +ADD generated generated/ ADD server.php . ADD .rr.yaml . ADD config.php . diff --git a/composer.json b/composer.json index 2ca465d..53136ba 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,8 @@ "autoload": { "psr-4": { "Siteworxpro\\App\\": "src/", - "Siteworxpro\\Tests\\": "tests/" + "Siteworxpro\\Tests\\": "tests/", + "GRPC\\": "generated/GRPC" } }, "require": { @@ -25,7 +26,8 @@ "react/promise": "^3", "react/async": "^4", "guzzlehttp/guzzle": "^7.10", - "zircote/swagger-php": "^5.7" + "zircote/swagger-php": "^5.7", + "spiral/roadrunner-grpc": "^3.5" }, "require-dev": { "phpunit/phpunit": "^12.4", diff --git a/composer.lock b/composer.lock index eb3ac31..9d3dad5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f12aaf0dae6930c226e719a5705e3f91", + "content-hash": "977f74570c671e4d59fd70d5e732c3d2", "packages": [ { "name": "adhocore/cli", @@ -298,6 +298,65 @@ ], "time": "2025-08-10T19:31:58+00:00" }, + { + "name": "google/common-protos", + "version": "4.12.4", + "source": { + "type": "git", + "url": "https://github.com/googleapis/common-protos-php.git", + "reference": "0127156899af0df2681bd42024c60bd5360d64e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/common-protos-php/zipball/0127156899af0df2681bd42024c60bd5360d64e3", + "reference": "0127156899af0df2681bd42024c60bd5360d64e3", + "shasum": "" + }, + "require": { + "google/protobuf": "^4.31", + "php": "^8.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "extra": { + "component": { + "id": "common-protos", + "path": "CommonProtos", + "entry": "README.md", + "target": "googleapis/common-protos-php.git" + } + }, + "autoload": { + "psr-4": { + "Google\\Api\\": "src/Api", + "Google\\Iam\\": "src/Iam", + "Google\\Rpc\\": "src/Rpc", + "Google\\Type\\": "src/Type", + "Google\\Cloud\\": "src/Cloud", + "GPBMetadata\\Google\\Api\\": "metadata/Api", + "GPBMetadata\\Google\\Iam\\": "metadata/Iam", + "GPBMetadata\\Google\\Rpc\\": "metadata/Rpc", + "GPBMetadata\\Google\\Type\\": "metadata/Type", + "GPBMetadata\\Google\\Cloud\\": "metadata/Cloud", + "GPBMetadata\\Google\\Logging\\": "metadata/Logging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Google API Common Protos for PHP", + "homepage": "https://github.com/googleapis/common-protos-php", + "keywords": [ + "google" + ], + "support": { + "source": "https://github.com/googleapis/common-protos-php/tree/v4.12.4" + }, + "time": "2025-09-20T01:29:44+00:00" + }, { "name": "google/protobuf", "version": "v4.33.1", @@ -2943,6 +3002,87 @@ ], "time": "2025-11-13T17:24:29+00:00" }, + { + "name": "spiral/roadrunner-grpc", + "version": "v3.5.2", + "source": { + "type": "git", + "url": "https://github.com/roadrunner-php/grpc.git", + "reference": "916c061de160d6b2f3efc82dcffac0360d84fab8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/roadrunner-php/grpc/zipball/916c061de160d6b2f3efc82dcffac0360d84fab8", + "reference": "916c061de160d6b2f3efc82dcffac0360d84fab8", + "shasum": "" + }, + "require": { + "ext-json": "*", + "google/common-protos": "^3.1|^4.0", + "google/protobuf": "^3.7 || ^4.0", + "php": ">=8.1", + "spiral/goridge": "^4.0", + "spiral/roadrunner": "^2024.3 || ^2025.1", + "spiral/roadrunner-worker": "^3.0", + "symfony/polyfill-php83": "*" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.0", + "mockery/mockery": "^1.4", + "phpunit/phpunit": "^10.0", + "spiral/code-style": "^2.2", + "spiral/dumper": "^3.3", + "vimeo/psalm": ">=5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spiral\\RoadRunner\\GRPC\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anton Titov (wolfy-j)", + "email": "wolfy-j@spiralscout.com" + }, + { + "name": "Pavel Buchnev (butschster)", + "email": "pavel.buchnev@spiralscout.com" + }, + { + "name": "Aleksei Gagarin (roxblnfk)", + "email": "alexey.gagarin@spiralscout.com" + }, + { + "name": "Maksim Smakouz (msmakouz)", + "email": "maksim.smakouz@spiralscout.com" + }, + { + "name": "RoadRunner Community", + "homepage": "https://github.com/spiral/roadrunner/graphs/contributors" + } + ], + "description": "High-Performance GRPC server for PHP applications", + "homepage": "https://roadrunner.dev/", + "support": { + "chat": "https://discord.gg/V6EK4he", + "docs": "https://docs.roadrunner.dev", + "forum": "https://forum.roadrunner.dev/", + "issues": "https://github.com/roadrunner-server/roadrunner/issues", + "source": "https://github.com/roadrunner-php/grpc/tree/v3.5.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/roadrunner-server", + "type": "github" + } + ], + "time": "2025-05-18T13:54:33+00:00" + }, { "name": "spiral/roadrunner-http", "version": "v3.6.0", diff --git a/generated/README.md b/generated/README.md new file mode 100644 index 0000000..aa2bde0 --- /dev/null +++ b/generated/README.md @@ -0,0 +1,3 @@ +### Note to Developers +Only generated files are allowed in this directory. +Please do not add any other files here manually. \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..d891dae --- /dev/null +++ b/makefile @@ -0,0 +1,70 @@ +help: + @echo "Available commands:" + @echo " start - Start the development runtime container" + @echo " sh - Open a shell in the development runtime container" + @echo " run - Run the application server in the development runtime container" + @echo " stop - Stop and remove the development runtime container" + @echo " migrate - Run database migrations in the migration container" + @echo " composer-install - Install PHP dependencies in the composer runtime container" + @echo " composer-require - Require a PHP package in the composer runtime container (usage: make composer-require package=vendor/package)" + @echo " composer-require-dev - Require a PHP package as dev in the composer runtime container (usage: make composer-require-dev package=vendor/package)" + @echo " composer-update - Update PHP dependencies in the composer runtime container" + @echo " enable-debug - Enable Xdebug in the development runtime container" + @echo " enable-coverage - Enable PCOV code coverage in the composer runtime container" + @echo " protoc - Generate PHP gRPC code from .proto files" + + +build-php-grpc: + git clone -b v1.76.0 https://github.com/grpc/grpc + cd grpc + cd grpc && git submodule update --init + grpc_root="$(pwd)" + cd src/php/ext/grpc + phpize + GRPC_LIB_SUBDIR=libs/opt ./configure --enable-grpc="${grpc_root}" + make + +composer-install: + docker compose exec composer-runtime sh -c "composer install --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" + +composer-require: + docker compose exec composer-runtime sh -c "composer require $(package) --ignore-platform-reqs" + +composer-require-dev: + docker compose exec composer-runtime sh -c "composer require --dev $(package) --ignore-platform-reqs" + +composer-update: + docker compose exec composer-runtime sh -c "composer update --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" + +migrate: + docker compose up migration-container + +enable-coverage: + ${MAKE} start + docker compose exec composer-runtime sh -c "bin/pcov.sh" + +start: + docker compose up dev-runtime -d --no-recreate + +sh: + ${MAKE} start + docker compose exec dev-runtime sh + +run: + ${MAKE} start + docker compose exec dev-runtime sh -c "rr serve" + +enable-debug: + ${MAKE} start + docker compose exec dev-runtime sh -c "bin/xdebug.sh" + +stop: + docker compose down + +protoc: + protoc --plugin=protoc-gen-php-grpc \ + --php_out=./generated \ + --php-grpc_out=./generated \ + protos/example.proto + +.PHONY: help enable-coverage start sh run enable-debug stop protoc \ No newline at end of file diff --git a/protos/example.proto b/protos/example.proto new file mode 100644 index 0000000..78ab89a --- /dev/null +++ b/protos/example.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +option go_package = "proto/greeter"; +option php_namespace = "GRPC\\Greeter"; +option php_metadata_namespace = "GRPC\\GPBMetadata"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/src/Grpc.php b/src/Grpc.php new file mode 100644 index 0000000..af2be6a --- /dev/null +++ b/src/Grpc.php @@ -0,0 +1,34 @@ + (bool) Config::get('app.debug'), + ]); + + $server->registerService(GreeterInterface::class, new Greeter()); + + $server->serve(Worker::create()); + + return 0; + } +} -- 2.49.1 From 373035d2cc47e142533338a53651a30606f67047 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Wed, 3 Dec 2025 23:28:46 -0500 Subject: [PATCH 2/7] feat: remove outdated PHP gRPC build instructions from makefile --- makefile | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/makefile b/makefile index d891dae..f8c9254 100644 --- a/makefile +++ b/makefile @@ -13,17 +13,6 @@ help: @echo " enable-coverage - Enable PCOV code coverage in the composer runtime container" @echo " protoc - Generate PHP gRPC code from .proto files" - -build-php-grpc: - git clone -b v1.76.0 https://github.com/grpc/grpc - cd grpc - cd grpc && git submodule update --init - grpc_root="$(pwd)" - cd src/php/ext/grpc - phpize - GRPC_LIB_SUBDIR=libs/opt ./configure --enable-grpc="${grpc_root}" - make - composer-install: docker compose exec composer-runtime sh -c "composer install --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" -- 2.49.1 From 18a182f3cd7444b7dc4a516a5e0dbf8fad274cf0 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Thu, 4 Dec 2025 00:04:57 -0500 Subject: [PATCH 3/7] feat: implement gRPC Greeter service with example proto and enhance makefile --- .rr.yaml | 9 +- generated/GRPC/GPBMetadata/Example.php | 25 ++++ generated/GRPC/Greeter/GreeterInterface.php | 22 +++ generated/GRPC/Greeter/HelloReply.php | 61 ++++++++ generated/GRPC/Greeter/HelloRequest.php | 61 ++++++++ grpc-worker.php | 14 ++ makefile | 151 ++++++++++++++------ server.php | 3 - src/Grpc.php | 17 ++- src/GrpcHandlers/GreeterHandler.php | 21 +++ 10 files changed, 333 insertions(+), 51 deletions(-) create mode 100644 generated/GRPC/GPBMetadata/Example.php create mode 100644 generated/GRPC/Greeter/GreeterInterface.php create mode 100644 generated/GRPC/Greeter/HelloReply.php create mode 100644 generated/GRPC/Greeter/HelloRequest.php create mode 100644 grpc-worker.php create mode 100644 src/GrpcHandlers/GreeterHandler.php diff --git a/.rr.yaml b/.rr.yaml index 7155674..763504a 100644 --- a/.rr.yaml +++ b/.rr.yaml @@ -10,8 +10,15 @@ grpc: listen: "tcp://0.0.0.0:9001" pool: command: "php grpc-worker.php" + num_workers: ${GRPC_WORKERS:-4} + allocate_timeout: 5s + reset_timeout: 5s + destroy_timeout: 5s + stream_timeout: 5s + reflection: ${GRPC_REFLECTION:-true} + health_check: ${GRPC_HEALTH_CHECK:-true} proto: - - "proto/helloworld.proto" + - "protos/example.proto" http: pool: diff --git a/generated/GRPC/GPBMetadata/Example.php b/generated/GRPC/GPBMetadata/Example.php new file mode 100644 index 0000000..09e16d5 --- /dev/null +++ b/generated/GRPC/GPBMetadata/Example.php @@ -0,0 +1,25 @@ +internalAddGeneratedFile( + "\x0A\xE5\x01\x0A\x14protos/example.proto\x12\x0Ahelloworld\"\x1C\x0A\x0CHelloRequest\x12\x0C\x0A\x04name\x18\x01 \x01(\x09\"\x1D\x0A\x0AHelloReply\x12\x0F\x0A\x07message\x18\x01 \x01(\x092I\x0A\x07Greeter\x12>\x0A\x08SayHello\x12\x18.helloworld.HelloRequest\x1A\x16.helloworld.HelloReply\"\x00B1Z\x0Dproto/greeter\xCA\x02\x0CGRPC\\Greeter\xE2\x02\x10GRPC\\GPBMetadatab\x06proto3" + , true); + + static::$is_initialized = true; + } +} + diff --git a/generated/GRPC/Greeter/GreeterInterface.php b/generated/GRPC/Greeter/GreeterInterface.php new file mode 100644 index 0000000..ad49827 --- /dev/null +++ b/generated/GRPC/Greeter/GreeterInterface.php @@ -0,0 +1,22 @@ +helloworld.HelloReply + */ +class HelloReply extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string message = 1; + */ + protected $message = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $message + * } + */ + public function __construct($data = NULL) { + \GRPC\GPBMetadata\Example::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string message = 1; + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Generated from protobuf field string message = 1; + * @param string $var + * @return $this + */ + public function setMessage($var) + { + GPBUtil::checkString($var, True); + $this->message = $var; + + return $this; + } + +} + diff --git a/generated/GRPC/Greeter/HelloRequest.php b/generated/GRPC/Greeter/HelloRequest.php new file mode 100644 index 0000000..87bd96f --- /dev/null +++ b/generated/GRPC/Greeter/HelloRequest.php @@ -0,0 +1,61 @@ +helloworld.HelloRequest + */ +class HelloRequest extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string name = 1; + */ + protected $name = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $name + * } + */ + public function __construct($data = NULL) { + \GRPC\GPBMetadata\Example::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string name = 1; + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Generated from protobuf field string name = 1; + * @param string $var + * @return $this + */ + public function setName($var) + { + GPBUtil::checkString($var, True); + $this->name = $var; + + return $this; + } + +} + diff --git a/grpc-worker.php b/grpc-worker.php new file mode 100644 index 0000000..6f7ed2b --- /dev/null +++ b/grpc-worker.php @@ -0,0 +1,14 @@ +start(); +} catch (\Exception $e) { + echo $e->getMessage(); + + exit(1); +} diff --git a/makefile b/makefile index f8c9254..8322662 100644 --- a/makefile +++ b/makefile @@ -1,59 +1,120 @@ -help: +# Makefile (enhanced) +SHELL := /bin/sh +.DEFAULT_GOAL := help + +# Reusable vars +DOCKER := docker compose +COMPOSER_RUNTIME := composer-runtime +DEV_RUNTIME := dev-runtime +MIGRATION_CONTAINER := migration-container + +PROTOC_GEN_DIR := ./protoc-gen-php-grpc-2025.1.5-darwin-arm64 +PROTOC_GEN := $(PROTOC_GEN_DIR)/protoc-gen-php-grpc +PROTOC_URL := https://github.com/roadrunner-server/roadrunner/releases/download/v2025.1.5/protoc-gen-php-grpc-2025.1.5-darwin-arm64.tar.gz + +COMPOSER := $(DOCKER) exec $(COMPOSER_RUNTIME) sh -c +DEV := $(DOCKER) exec $(DEV_RUNTIME) sh -c + +# Colors +GREEN := \033[32m +YELLOW := \033[33m +RESET := \033[0m + +# Align width for help display +HELP_COL_WIDTH := 26 + +# Help: auto-generate from targets with "##" comments +help: ## Show this help @echo "Available commands:" - @echo " start - Start the development runtime container" - @echo " sh - Open a shell in the development runtime container" - @echo " run - Run the application server in the development runtime container" - @echo " stop - Stop and remove the development runtime container" - @echo " migrate - Run database migrations in the migration container" - @echo " composer-install - Install PHP dependencies in the composer runtime container" - @echo " composer-require - Require a PHP package in the composer runtime container (usage: make composer-require package=vendor/package)" - @echo " composer-require-dev - Require a PHP package as dev in the composer runtime container (usage: make composer-require-dev package=vendor/package)" - @echo " composer-update - Update PHP dependencies in the composer runtime container" - @echo " enable-debug - Enable Xdebug in the development runtime container" - @echo " enable-coverage - Enable PCOV code coverage in the composer runtime container" - @echo " protoc - Generate PHP gRPC code from .proto files" + @awk -F':|##' '/^[a-zA-Z0-9._-]+:.*##/ {printf " %-$(HELP_COL_WIDTH)s - %s\n", $$1, $$3}' $(MAKEFILE_LIST) | sort -composer-install: - docker compose exec composer-runtime sh -c "composer install --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" +start: ## Start the development runtime container + @printf "$(GREEN)Starting $(DEV_RUNTIME)$(RESET)\n" + $(DOCKER) up $(DEV_RUNTIME) -d --no-recreate -composer-require: - docker compose exec composer-runtime sh -c "composer require $(package) --ignore-platform-reqs" +sh: ## Open a shell in the development runtime container + @$(MAKE) start + $(DOCKER) exec $(DEV_RUNTIME) sh -composer-require-dev: - docker compose exec composer-runtime sh -c "composer require --dev $(package) --ignore-platform-reqs" +run: ## Run the application server in the development runtime container + @$(MAKE) start + $(DEV) "rr serve" -composer-update: - docker compose exec composer-runtime sh -c "composer update --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" +stop: ## Stop and remove the development runtime container + @printf "$(YELLOW)Stopping all containers$(RESET)\n" + $(DOCKER) down -migrate: - docker compose up migration-container +restart: ## Restart dev container (stop + start) + @$(MAKE) stop + @$(MAKE) start -enable-coverage: - ${MAKE} start - docker compose exec composer-runtime sh -c "bin/pcov.sh" +rebuild: ## Rebuild containers (useful after Dockerfile changes) + @printf "$(YELLOW)Rebuilding containers$(RESET)\n" + $(DOCKER) build + $(DOCKER) up --force-recreate --build -d -start: - docker compose up dev-runtime -d --no-recreate +ps: ## Show docker compose ps + $(DOCKER) ps -sh: - ${MAKE} start - docker compose exec dev-runtime sh +migrate: ## Run database migrations in the migration container + $(DOCKER) up $(MIGRATION_CONTAINER) -run: - ${MAKE} start - docker compose exec dev-runtime sh -c "rr serve" +# Composer helpers +composer-install: ## Install PHP dependencies in the composer runtime container + $(COMPOSER) "composer install --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" -enable-debug: - ${MAKE} start - docker compose exec dev-runtime sh -c "bin/xdebug.sh" +composer-require: ## Require a PHP package in the composer runtime container (usage: make composer-require package=vendor/package) +ifndef package + $(error package variable is required: make composer-require package=vendor/package) +endif + $(COMPOSER) "composer require $(package) --ignore-platform-reqs" -stop: - docker compose down +composer-require-dev: ## Require a PHP package as dev in the composer runtime container (usage: make composer-require-dev package=vendor/package) +ifndef package + $(error package variable is required: make composer-require-dev package=vendor/package) +endif + $(COMPOSER) "composer require --dev $(package) --ignore-platform-reqs" -protoc: - protoc --plugin=protoc-gen-php-grpc \ - --php_out=./generated \ - --php-grpc_out=./generated \ - protos/example.proto +composer-update: ## Update PHP dependencies in the composer runtime container + $(COMPOSER) "composer update --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" -.PHONY: help enable-coverage start sh run enable-debug stop protoc \ No newline at end of file +enable-debug: ## Enable Xdebug in the development runtime container + @$(MAKE) start + $(DEV) "bin/xdebug.sh" + +enable-coverage: ## Enable PCOV code coverage in the composer runtime container + @$(MAKE) start + $(COMPOSER) "bin/pcov.sh" + +protoc: ## Generate PHP gRPC code from .proto files + @printf "$(GREEN)Setting up protoc-gen-php-grpc plugin$(RESET)\n" + @curl -LOs $(PROTOC_URL) + @tar -xzf protoc-gen-php-grpc-2025.1.5-darwin-arm64.tar.gz + @printf "$(GREEN)Generating PHP gRPC code from .proto files$(RESET)\n" + @protoc --plugin=./protoc-gen-php-grpc-2025.1.5-darwin-arm64/protoc-gen-php-grpc \ + --php_out=./generated \ + --php-grpc_out=./generated \ + protos/example.proto + @printf "$(GREEN)Cleaning up protoc-gen-php-grpc plugin files$(RESET)\n" + @rm -rf $(PROTOC_GEN_DIR) protoc-gen-php-grpc-2025.1.5-darwin-arm64.tar.gz + +# Developer tasks +lint: ## Run linting (phpcs/phpstan) in composer runtime + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate + $(COMPOSER) "composer run-script tests:lint || true" + $(COMPOSER) "composer run-script tests:phpstan || true" + +fmt: ## Format code (php-cs-fixer) + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate + $(COMPOSER) "composer run-script tests:lint:fix" + +test: ## Run test suite (phpunit) + $(COMPOSER) "composer run-script tests:unit || true" + +# Convenience aliases +dev: run ## Alias for start +ci: composer-install test ## CI-like local flow +down: stop ## Alias for stop +up: start ## Alias for start + +.PHONY: help start sh run stop restart rebuild ps logs migrate composer-install composer-require composer-require-dev composer-update enable-debug enable-coverage protoc lint fmt test check-lock dev ci \ No newline at end of file diff --git a/server.php b/server.php index dd031c6..74aabcf 100644 --- a/server.php +++ b/server.php @@ -5,10 +5,7 @@ use Siteworxpro\App\Api; require __DIR__ . '/vendor/autoload.php'; try { - // Instantiate the ExternalServer class $server = new Api(); - - // Start the server $server->startServer(); } catch (JsonException $e) { echo $e->getMessage(); diff --git a/src/Grpc.php b/src/Grpc.php index af2be6a..9cc9d6f 100644 --- a/src/Grpc.php +++ b/src/Grpc.php @@ -4,11 +4,20 @@ declare(strict_types=1); namespace Siteworxpro\App; +use GRPC\Greeter\GreeterInterface; +use Siteworxpro\App\GrpcHandlers\GreeterHandler; use Siteworxpro\App\Services\Facades\Config; use Spiral\RoadRunner\GRPC\Invoker; use Spiral\RoadRunner\GRPC\Server; use Spiral\RoadRunner\Worker; +/** + * Class Grpc + * + * starts a gRPC server using RoadRunner + * + * @package Siteworxpro\App + */ class Grpc { /** @@ -19,14 +28,18 @@ class Grpc Kernel::boot(); } + /** + * Starts the gRPC server + * + * @return int + */ public function start(): int { $server = new Server(new Invoker(), [ 'debug' => (bool) Config::get('app.debug'), ]); - $server->registerService(GreeterInterface::class, new Greeter()); - + $server->registerService(GreeterInterface::class, new GreeterHandler()); $server->serve(Worker::create()); return 0; diff --git a/src/GrpcHandlers/GreeterHandler.php b/src/GrpcHandlers/GreeterHandler.php new file mode 100644 index 0000000..72028d7 --- /dev/null +++ b/src/GrpcHandlers/GreeterHandler.php @@ -0,0 +1,21 @@ +setMessage('Hello ' . $in->getName()); + + return $reply; + } +} -- 2.49.1 From 24f067106675e032dc6d18e7f118d8494e479f60 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Thu, 4 Dec 2025 00:25:52 -0500 Subject: [PATCH 4/7] feat: enhance makefile with emoji support and improve command output --- makefile | 51 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/makefile b/makefile index 8322662..96e2bc9 100644 --- a/makefile +++ b/makefile @@ -20,16 +20,28 @@ GREEN := \033[32m YELLOW := \033[33m RESET := \033[0m +# Fancy emoji +SPARK := โœจ +ROCKET := ๐Ÿš€ +WARN := โš ๏ธ +MAGNIFY := ๐Ÿ” +BUG := ๐Ÿž +COMPOSE := ๐Ÿณ +TRASH := ๐Ÿงน +PROTO := ๐Ÿงฉ +CHECK := โœ… +CROSS := โŒ + # Align width for help display HELP_COL_WIDTH := 26 # Help: auto-generate from targets with "##" comments help: ## Show this help - @echo "Available commands:" + @echo "$(SPARK) Available commands:" @awk -F':|##' '/^[a-zA-Z0-9._-]+:.*##/ {printf " %-$(HELP_COL_WIDTH)s - %s\n", $$1, $$3}' $(MAKEFILE_LIST) | sort start: ## Start the development runtime container - @printf "$(GREEN)Starting $(DEV_RUNTIME)$(RESET)\n" + @printf "$(GREEN)$(ROCKET) Starting $(DEV_RUNTIME)$(RESET)\n" $(DOCKER) up $(DEV_RUNTIME) -d --no-recreate sh: ## Open a shell in the development runtime container @@ -41,7 +53,7 @@ run: ## Run the application server in the development runtime container $(DEV) "rr serve" stop: ## Stop and remove the development runtime container - @printf "$(YELLOW)Stopping all containers$(RESET)\n" + @printf "$(YELLOW)$(WARN) Stopping all containers$(RESET)\n" $(DOCKER) down restart: ## Restart dev container (stop + start) @@ -49,7 +61,7 @@ restart: ## Restart dev container (stop + start) @$(MAKE) start rebuild: ## Rebuild containers (useful after Dockerfile changes) - @printf "$(YELLOW)Rebuilding containers$(RESET)\n" + @printf "$(YELLOW)$(SPARK) Rebuilding containers$(RESET)\n" $(DOCKER) build $(DOCKER) up --force-recreate --build -d @@ -61,59 +73,78 @@ migrate: ## Run database migrations in the migration container # Composer helpers composer-install: ## Install PHP dependencies in the composer runtime container + @printf "$(COMPOSE) $(GREEN)Installing PHP dependencies in $(COMPOSER_RUNTIME)$(RESET)\n" + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate $(COMPOSER) "composer install --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" composer-require: ## Require a PHP package in the composer runtime container (usage: make composer-require package=vendor/package) ifndef package $(error package variable is required: make composer-require package=vendor/package) endif + @printf "$(COMPOSE) $(MAGNIFY) Requiring package $(package) in $(COMPOSER_RUNTIME)$(RESET)\n" + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate $(COMPOSER) "composer require $(package) --ignore-platform-reqs" composer-require-dev: ## Require a PHP package as dev in the composer runtime container (usage: make composer-require-dev package=vendor/package) ifndef package $(error package variable is required: make composer-require-dev package=vendor/package) endif + @printf "$(COMPOSE) $(MAGNIFY) Requiring dev package $(package) in $(COMPOSER_RUNTIME)$(RESET)\n" + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate $(COMPOSER) "composer require --dev $(package) --ignore-platform-reqs" composer-update: ## Update PHP dependencies in the composer runtime container + @printf "$(COMPOSE) $(MAGNIFY) Updating PHP dependencies$(RESET)\n" + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate $(COMPOSER) "composer update --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs" enable-debug: ## Enable Xdebug in the development runtime container - @$(MAKE) start + @$(DOCKER) up $(DEV_RUNTIME) -d --no-recreate + @printf "$(GREEN)$(BUG) Enabling Xdebug in $(DEV_RUNTIME)$(RESET)\n" $(DEV) "bin/xdebug.sh" enable-coverage: ## Enable PCOV code coverage in the composer runtime container - @$(MAKE) start + @printf "$(GREEN)$(MAGNIFY) Enabling PCOV in $(COMPOSER_RUNTIME)$(RESET)\n" + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate $(COMPOSER) "bin/pcov.sh" protoc: ## Generate PHP gRPC code from .proto files - @printf "$(GREEN)Setting up protoc-gen-php-grpc plugin$(RESET)\n" + @printf "$(PROTO) $(GREEN)Setting up protoc-gen-php-grpc plugin$(RESET)\n" @curl -LOs $(PROTOC_URL) @tar -xzf protoc-gen-php-grpc-2025.1.5-darwin-arm64.tar.gz - @printf "$(GREEN)Generating PHP gRPC code from .proto files$(RESET)\n" + @printf "$(PROTO) $(GREEN)Generating PHP gRPC code from .proto files$(RESET)\n" @protoc --plugin=./protoc-gen-php-grpc-2025.1.5-darwin-arm64/protoc-gen-php-grpc \ --php_out=./generated \ --php-grpc_out=./generated \ protos/example.proto - @printf "$(GREEN)Cleaning up protoc-gen-php-grpc plugin files$(RESET)\n" + @printf "$(TRASH) $(GREEN)Cleaning up protoc-gen-php-grpc plugin files$(RESET)\n" @rm -rf $(PROTOC_GEN_DIR) protoc-gen-php-grpc-2025.1.5-darwin-arm64.tar.gz +license-check: ## Check license headers in source files + @printf "$(MAGNIFY) $(GREEN)Checking license headers$(RESET)\n" + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate + $(COMPOSER) "composer run-script tests:license || true" + # Developer tasks lint: ## Run linting (phpcs/phpstan) in composer runtime + @printf "$(MAGNIFY) $(GREEN)Running linters$(RESET)\n" @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate $(COMPOSER) "composer run-script tests:lint || true" $(COMPOSER) "composer run-script tests:phpstan || true" fmt: ## Format code (php-cs-fixer) + @printf "$(MAGNIFY) $(GREEN)Formatting code$(RESET)\n" @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate $(COMPOSER) "composer run-script tests:lint:fix" test: ## Run test suite (phpunit) + @printf "$(CHECK) $(GREEN)Running unit tests$(RESET)\n" + @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate $(COMPOSER) "composer run-script tests:unit || true" # Convenience aliases dev: run ## Alias for start -ci: composer-install test ## CI-like local flow +ci: composer-install license-check lint test ## CI-like local flow down: stop ## Alias for stop up: start ## Alias for start -- 2.49.1 From 1b4e1468fc3fd0b5e3400197a45277ccc4ff544e Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Thu, 4 Dec 2025 08:43:19 -0500 Subject: [PATCH 5/7] feat: update gRPC server configuration and enhance Docker setup --- .rr.yaml | 7 +-- Dockerfile | 2 + README.md | 139 +++++++++++++++++++++++++++++++++++---------- config.php | 1 - docker-compose.yml | 17 ++++++ grpc-worker.php | 2 +- src/Grpc.php | 2 +- 7 files changed, 131 insertions(+), 39 deletions(-) diff --git a/.rr.yaml b/.rr.yaml index 763504a..180c970 100644 --- a/.rr.yaml +++ b/.rr.yaml @@ -11,12 +11,9 @@ grpc: pool: command: "php grpc-worker.php" num_workers: ${GRPC_WORKERS:-4} - allocate_timeout: 5s - reset_timeout: 5s - destroy_timeout: 5s - stream_timeout: 5s + debug: ${DEBUG:-false} reflection: ${GRPC_REFLECTION:-true} - health_check: ${GRPC_HEALTH_CHECK:-true} + destroy_timeout: 5s proto: - "protos/example.proto" diff --git a/Dockerfile b/Dockerfile index 9b86277..885b972 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,11 +43,13 @@ COPY --from=library /app/vendor /app/vendor # Copy the RoadRunner configuration file and source ADD src src/ ADD generated generated/ +ADD protos protos/ ADD server.php . ADD .rr.yaml . ADD config.php . EXPOSE 9501 +EXPOSE 9001 # Entrypoint command to run the RoadRunner server with the specified configuration ENTRYPOINT ["rr", "serve", "-c", ".rr.yaml", "-s"] \ No newline at end of file diff --git a/README.md b/README.md index c60c60e..072f710 100644 --- a/README.md +++ b/README.md @@ -2,55 +2,132 @@ ![pipeline status](https://gitea.siteworxpro.com/siteworxpro/Php-Template/actions/workflows/tests.yml/badge.svg?branch=master&style=flat-square) +## Overview + +This is a PHP project template that provides a structured development environment using Docker Compose and Make. +It includes tools for code quality, testing, dependency management, and gRPC support. + ## Dev Environment -### Prerequisites -- Docker -- Docker Compose +This project uses Docker Compose and Make to manage the development environment. The `makefile` provides convenient +commands for common development tasks. -### migrations +## Prerequisites -create a new migration -```shell -docker run --rm -v $(PWD):/app siteworxpro/migrate:v4.18.3 create -ext sql -dir /app/db/migrations -seq create_users_table +- Docker and Docker Compose +- Make +- protoc (Protocol Buffers compiler) - for gRPC code generation + +## Quick Start + +```bash +# Install PHP dependencies +make composer-install + +# Start the development container +make start + +# Run the application server +make run ``` -```text -postgres://siteworxpro:password@localhost:5432/siteworxpro?sslmode=disable +## Available Commands + +### Container Management + +- `make start` - Start the development runtime container +- `make stop` - Stop and remove all containers +- `make restart` - Restart the development container +- `make rebuild` - Rebuild containers (use after Dockerfile changes) +- `make sh` - Open a shell in the development container +- `make ps` - Show running containers + +### Application + +- `make run` - Run the application server (RoadRunner) +- `make migrate` - Run database migrations + +### Composer & Dependencies + +- `make composer-install` - Install PHP dependencies +- `make composer-update` - Update PHP dependencies +- `make composer-require package=vendor/package` - Add a new dependency +- `make composer-require-dev package=vendor/package` - Add a new dev dependency + +### Code Quality + +- `make lint` - Run linters (phpcs and phpstan) +- `make fmt` - Format code with php-cs-fixer +- `make test` - Run the test suite (phpunit) +- `make license-check` - Check license headers in source files + +### Debugging & Coverage + +- `make enable-debug` - Enable Xdebug for debugging +- `make enable-coverage` - Enable PCOV for code coverage + +### gRPC + +- `make protoc` - Generate PHP gRPC code from `.proto` files + +### CI Workflow + +- `make ci` - Run the full CI pipeline locally (install, license check, lint, test) + +### Help + +- `make help` - Show all available commands with descriptions + +## Common Workflows + +### Starting Development + +```bash +make composer-install +make start +make run ``` -```shell -docker run --rm -v $(PWD):/app siteworxpro/migrate:v4.18.3 -database "postgres://siteworxpro:password@localhost:5432/siteworxpro?sslmode=disable" -path /app/db/migrations up +### Adding a New Package + +```bash +make composer-require package=vendor/package-name ``` -### Starting the Runtime -```shell -docker-compose up -d -``` -### Start the server -```shell -docker exec -it template-dev-runtime-1 rr serve +### Running Tests + +```bash +make test ``` -You can access the api at `http://localhost:9501/` +### Code Quality Check -### Xdebug - -xdebug needs to be built into the container before it will work -```shell - docker exec -it php-template-composer-runtime-1 bin/xdebug.sh +```bash +make lint +make fmt ``` -### Install the dependencies -```shell -docker run --rm -v $(PWD):/app siteworxpro/composer install --ignore-platform-reqs +### Debugging + +```bash +make enable-debug +make run ``` -### Running all tests -```shell -docker run --rm -v $(PWD):/app siteworxpro/composer run tests:all -``` +## Notes +- All commands run inside Docker containers, ensuring a consistent environment +- The development runtime uses RoadRunner as the application server +- Composer commands run in a separate `composer-runtime` container +- Database migrations run in a dedicated `migration-container` + +## Additional Information + +### Accessing Services + +- You can access the api at [https://localhost](https://localhost) +- Traefik dashboard is at [https://127.0.0.1/dashboard/](https://127.0.0.1/dashboard/) +- the grpc server is at [tcp://localhost:9001](tcp://localhost:9001) ## License diff --git a/config.php b/config.php index 48dd820..4b00213 100644 --- a/config.php +++ b/config.php @@ -14,7 +14,6 @@ return [ */ 'server' => [ 'port' => Env::get('HTTP_PORT', 9501, 'int'), - 'dev_mode' => Env::get('DEV_MODE', false, 'bool'), ], /** diff --git a/docker-compose.yml b/docker-compose.yml index a7c1f4c..aad53f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,12 @@ volumes: services: traefik: + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik.entrypoints=web-secure" + - "traefik.http.routers.traefik.rule=Host(`127.0.0.1`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))" + - "traefik.http.routers.traefik.tls=true" + - "traefik.http.routers.traefik.service=api@internal" image: traefik:latest container_name: traefik healthcheck: @@ -15,15 +21,18 @@ services: ports: - "80:80" - "443:443" + - "9001:9001" volumes: - "/var/run/docker.sock:/var/run/docker.sock" restart: always command: - "--providers.docker=true" + - "--api.insecure=true" - "--ping" - "--providers.docker.exposedByDefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.web-secure.address=:443" + - "--entrypoints.grpc.address=:9001" - "--accesslog=true" - "--entrypoints.web.http.redirections.entryPoint.to=web-secure" - "--entrypoints.web.http.redirections.entryPoint.scheme=https" @@ -79,6 +88,13 @@ services: - "traefik.http.services.api.loadbalancer.healthcheck.path=/healthz" - "traefik.http.services.api.loadbalancer.healthcheck.interval=5s" - "traefik.http.services.api.loadbalancer.healthcheck.timeout=60s" + - "traefik.tcp.services.api.loadbalancer.server.port=9001" + - "traefik.http.services.api.loadbalancer.server.port=9501" + - "traefik.tcp.routers.grpc.entrypoints=grpc" + - "traefik.tcp.routers.grpc.rule=HostSNI(`localhost`) || HostSNI(`127.0.0.1`)" + - "traefik.tcp.routers.grpc.tls=true" + - "traefik.tcp.routers.grpc.service=api" + container_name: dev-runtime volumes: - .:/app build: @@ -103,6 +119,7 @@ services: QUEUE_BROKER: redis PHP_IDE_CONFIG: serverName=localhost WORKERS: 1 + GRPC_WORKERS: 1 DEBUG: 1 REDIS_HOST: redis DB_HOST: postgres diff --git a/grpc-worker.php b/grpc-worker.php index 6f7ed2b..0dbc05c 100644 --- a/grpc-worker.php +++ b/grpc-worker.php @@ -6,7 +6,7 @@ require __DIR__ . '/vendor/autoload.php'; try { $server = new Grpc(); - $server->start(); + exit($server->start()); } catch (\Exception $e) { echo $e->getMessage(); diff --git a/src/Grpc.php b/src/Grpc.php index 9cc9d6f..d8c2779 100644 --- a/src/Grpc.php +++ b/src/Grpc.php @@ -36,7 +36,7 @@ class Grpc public function start(): int { $server = new Server(new Invoker(), [ - 'debug' => (bool) Config::get('app.debug'), + 'debug' => Config::get('app.dev_mode'), ]); $server->registerService(GreeterInterface::class, new GreeterHandler()); -- 2.49.1 From 0c2f1e4b36dfd74273f257a82e855099c26b7cb6 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Thu, 4 Dec 2025 08:50:04 -0500 Subject: [PATCH 6/7] feat: update gRPC server configuration and enhance Docker setup --- makefile | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/makefile b/makefile index 96e2bc9..c8e481c 100644 --- a/makefile +++ b/makefile @@ -62,8 +62,10 @@ restart: ## Restart dev container (stop + start) rebuild: ## Rebuild containers (useful after Dockerfile changes) @printf "$(YELLOW)$(SPARK) Rebuilding containers$(RESET)\n" - $(DOCKER) build - $(DOCKER) up --force-recreate --build -d + @$(MAKE) stop + @printf "$(YELLOW)$(TRASH) Deleting all Docker resources$(RESET)\n" + docker system prune --all --volumes --force + @$(MAKE) start ps: ## Show docker compose ps $(DOCKER) ps @@ -139,9 +141,15 @@ fmt: ## Format code (php-cs-fixer) test: ## Run test suite (phpunit) @printf "$(CHECK) $(GREEN)Running unit tests$(RESET)\n" - @$(DOCKER) up $(COMPOSER_RUNTIME) -d --no-recreate + @$(DOCKER) up $(COMPOSER_RUNTIME) -d $(COMPOSER) "composer run-script tests:unit || true" +test-coverage: ## Run test suite with coverage report + @printf "$(CHECK) $(GREEN)Running unit tests with coverage report$(RESET)\n" + @$(DOCKER) up $(COMPOSER_RUNTIME) -d + @$(MAKE) enable-coverage + $(COMPOSER) "composer run-script tests:unit:coverage || true" + # Convenience aliases dev: run ## Alias for start ci: composer-install license-check lint test ## CI-like local flow -- 2.49.1 From 4eb28a17afa5544e2b5af4c3846afb8137957b76 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Thu, 4 Dec 2025 08:51:43 -0500 Subject: [PATCH 7/7] feat: update gRPC server configuration and enhance Docker setup --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index c8e481c..e4d4d80 100644 --- a/makefile +++ b/makefile @@ -156,4 +156,4 @@ ci: composer-install license-check lint test ## CI-like local flow down: stop ## Alias for stop up: start ## Alias for start -.PHONY: help start sh run stop restart rebuild ps logs migrate composer-install composer-require composer-require-dev composer-update enable-debug enable-coverage protoc lint fmt test check-lock dev ci \ No newline at end of file +.PONY: help start sh run stop restart rebuild ps migrate composer-install composer-require composer-require-dev composer-update enable-debug enable-coverage protoc license-check lint fmt test test-coverage dev ci down up \ No newline at end of file -- 2.49.1