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;
+ }
+}