@@ -217,11 +217,154 @@ return [
217217 'grpc' => [
218218 'services' => [
219219 \A pp\G RPC\E choServiceInterface::class => \A pp\G RPC\E choService::class,
220+
221+ // Service with specific interceptors
222+ \A pp\G RPC\UserServi ceInterface::class => [
223+ 'service' => \A pp\G RPC\UserServi ce::class,
224+ 'interceptors' => [
225+ \A pp\G RPC\I nterceptors\V alidationInterceptor::class,
226+ \A pp\G RPC\I nterceptors\C acheInterceptor::class,
227+ ],
228+ ],
229+ ]
230+ ],
231+ ];
232+ ` ` `
233+
234+ # ### gRPC Server Interceptors
235+
236+ Create your interceptor by implementing `Spiral\Interceptors\InterceptorInterface` :
237+
238+ ` ` ` php
239+ <?php
240+
241+ namespace App\G RPC\I nterceptors;
242+
243+ use Spiral\I nterceptors\C ontext\C allContextInterface;
244+ use Spiral\I nterceptors\H andlerInterface;
245+ use Spiral\I nterceptors\I nterceptorInterface;
246+
247+ class LoggingInterceptor implements InterceptorInterface
248+ {
249+ public function intercept(CallContextInterface $context, HandlerInterface $handler): mixed
250+ {
251+ $method = $context->getTarget()->getPath();
252+ \L og::info("gRPC call: {$method}");
253+
254+ $response = $handler->handle($context);
255+
256+ \L og::info("gRPC response: {$method}");
257+
258+ return $response;
259+ }
260+ }
261+ ` ` `
262+
263+ # #### Interceptors Configuration
264+
265+ Configure interceptors in `config/roadrunner.php`. You can use global interceptors that apply to all services, service-specific interceptors, or both :
266+
267+ ` ` ` php
268+ return [
269+ // ... other configuration
270+ 'grpc' => [
271+ 'services' => [
272+ // Simple service configuration
273+ \A pp\G RPC\E choServiceInterface::class => \A pp\G RPC\E choService::class,
274+
275+ // Service with specific interceptors
276+ \A pp\G RPC\UserServi ceInterface::class => [
277+ 'service' => \A pp\G RPC\UserServi ce::class,
278+ 'interceptors' => [
279+ \A pp\G RPC\I nterceptors\V alidationInterceptor::class,
280+ \A pp\G RPC\I nterceptors\C acheInterceptor::class,
281+ ],
282+ ],
283+ ],
284+ // Global interceptors - applied to all services
285+ 'interceptors' => [
286+ \A pp\G RPC\I nterceptors\L oggingInterceptor::class,
287+ \A pp\G RPC\I nterceptors\A uthenticationInterceptor::class,
220288 ],
221289 ],
222290];
223291` ` `
224292
293+ # #### Using Attribute-Based Interceptors
294+
295+ For additional flexibility and convenience, you can use the `AttributesInterceptor` to apply interceptors via PHP attributes directly on your service classes and methods. This allows you to define which interceptors should be applied at a more granular level.
296+
297+ To enable attribute-based interceptors, add the `AttributesInterceptor` to your global interceptors list :
298+
299+ ` ` ` php
300+ 'interceptors' => [
301+ // ... other global interceptors before
302+ \S piral\R oadRunnerLaravel\C ommon\I nterceptor\A ttributesInterceptor::class,
303+ // ... other global interceptors after
304+ ],
305+ ` ` `
306+
307+ Then, create interceptors that can be used as attributes :
308+
309+ ` ` ` php
310+ <?php
311+
312+ namespace App\G RPC\I nterceptors;
313+
314+ use Spiral\I nterceptors\C ontext\C allContextInterface;
315+ use Spiral\I nterceptors\H andlerInterface;
316+ use Spiral\I nterceptors\I nterceptorInterface;
317+ use Attribute;
318+
319+ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
320+ class RoleInterceptor implements InterceptorInterface
321+ {
322+ public function __construct(
323+ private readonly string $role,
324+ ) {}
325+
326+ public function intercept(CallContextInterface $context, HandlerInterface $handler): mixed
327+ {
328+ // Check user role
329+ if (!$this->checkRole($this->role)) {
330+ throw new \R untimeException('Access denied');
331+ }
332+
333+ return $handler->handle($context);
334+ }
335+ }
336+ ` ` `
337+
338+ Apply interceptors to your gRPC service classes or methods :
339+
340+ ` ` ` php
341+ <?php
342+
343+ namespace App\G RPC;
344+
345+ use App\G RPC\I nterceptors\L oggingInterceptor;
346+ use App\G RPC\I nterceptors\A uthInterceptor;
347+ use App\G RPC\I nterceptors\R oleInterceptor;
348+
349+ #[LoggingInterceptor]
350+ #[AuthInterceptor]
351+ class UserService implements UserServiceInterface
352+ {
353+ #[RoleInterceptor('admin')]
354+ public function DeleteUser(GRPC\C ontextInterface $ctx, DeleteUserRequest $in): DeleteUserResponse
355+ {
356+ // Implementation - will use class-level + method-level interceptors
357+ }
358+
359+ public function GetUser(GRPC\C ontextInterface $ctx, GetUserRequest $in): GetUserResponse
360+ {
361+ // Implementation - will use only class-level interceptors
362+ }
363+ }
364+ ` ` `
365+
366+ Interceptors are applied in order : first class-level attributes, then method-level attributes.
367+
225368# ### gRPC Client Usage
226369
227370The package also allows your Laravel application to act as a gRPC client, making requests to external gRPC services.
@@ -451,6 +594,75 @@ return [
451594The key in the `workers` array should match the value of the `RR_MODE` environment variable
452595set by the RoadRunner server for your plugin.
453596
597+ # ## Example: Centrifugo Worker
598+
599+ Here's an example of a custom worker for the [Centrifugo](https://docs.roadrunner.dev/docs/plugins/centrifuge) plugin :
600+
601+ ` ` ` php
602+ namespace App\W orkers;
603+
604+ use Spiral\R oadRunnerLaravel\W orkerInterface;
605+ use Spiral\R oadRunnerLaravel\W orkerOptionsInterface;
606+ use Spiral\R oadRunner\C entrifugo\C entrifugoWorker as RRCentrifugoWorker;
607+ use Spiral\R oadRunner\C entrifugo\C entrifugoWorkerInterface;
608+
609+ class CentrifugoWorker implements WorkerInterface
610+ {
611+ public function start(WorkerOptionsInterface $options): void
612+ {
613+ $worker = RRCentrifugoWorker::create();
614+
615+ $worker->onConnect(function (CentrifugoWorkerInterface $worker, string $client, array $request): array {
616+ // Handle client connection
617+ $app = $options->getAppContainer();
618+
619+ // Your connection handling logic
620+
621+ return ['status' => 200];
622+ });
623+
624+ $worker->onSubscribe(function (CentrifugoWorkerInterface $worker, string $client, array $request): array {
625+ // Handle client subscription
626+ $app = $options->getAppContainer();
627+
628+ // Your subscription handling logic
629+
630+ return ['status' => 200];
631+ });
632+
633+ $worker->onPublish(function (CentrifugoWorkerInterface $worker, string $client, array $request): array {
634+ // Handle client publish
635+ $app = $options->getAppContainer();
636+
637+ // Your publish handling logic
638+
639+ return ['status' => 200];
640+ });
641+
642+ $worker->start();
643+ }
644+ }
645+ ` ` `
646+
647+ Then register it in your configuration :
648+
649+ ` ` ` php
650+ return [
651+ 'workers' => [
652+ // ... other workers
653+ 'centrifugo' => \A pp\W orkers\C entrifugoWorker::class,
654+ ],
655+ ];
656+ ` ` `
657+
658+ And update your `.rr.yaml` with the Centrifugo plugin configuration :
659+
660+ ` ` ` yaml
661+ centrifugo:
662+ address: "tcp://localhost:8000"
663+ api_key: "your-api-key"
664+ ` ` `
665+
454666# # Support
455667
456668If you find this package helpful, please consider giving it a star on GitHub.
0 commit comments