1*cc02d7e2SAndroid Build Coastguard Worker<?php 2*cc02d7e2SAndroid Build Coastguard Worker/* 3*cc02d7e2SAndroid Build Coastguard Worker * 4*cc02d7e2SAndroid Build Coastguard Worker * Copyright 2020 gRPC authors. 5*cc02d7e2SAndroid Build Coastguard Worker * 6*cc02d7e2SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 7*cc02d7e2SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 8*cc02d7e2SAndroid Build Coastguard Worker * You may obtain a copy of the License at 9*cc02d7e2SAndroid Build Coastguard Worker * 10*cc02d7e2SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 11*cc02d7e2SAndroid Build Coastguard Worker * 12*cc02d7e2SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 13*cc02d7e2SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 14*cc02d7e2SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15*cc02d7e2SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 16*cc02d7e2SAndroid Build Coastguard Worker * limitations under the License. 17*cc02d7e2SAndroid Build Coastguard Worker * 18*cc02d7e2SAndroid Build Coastguard Worker */ 19*cc02d7e2SAndroid Build Coastguard Worker 20*cc02d7e2SAndroid Build Coastguard Workernamespace Grpc; 21*cc02d7e2SAndroid Build Coastguard Worker 22*cc02d7e2SAndroid Build Coastguard Worker/** 23*cc02d7e2SAndroid Build Coastguard Worker * This is an experimental and incomplete implementation of gRPC server 24*cc02d7e2SAndroid Build Coastguard Worker * for PHP. APIs are _definitely_ going to be changed. 25*cc02d7e2SAndroid Build Coastguard Worker * 26*cc02d7e2SAndroid Build Coastguard Worker * DO NOT USE in production. 27*cc02d7e2SAndroid Build Coastguard Worker */ 28*cc02d7e2SAndroid Build Coastguard Worker 29*cc02d7e2SAndroid Build Coastguard Worker/** 30*cc02d7e2SAndroid Build Coastguard Worker * Class RpcServer 31*cc02d7e2SAndroid Build Coastguard Worker * @package Grpc 32*cc02d7e2SAndroid Build Coastguard Worker */ 33*cc02d7e2SAndroid Build Coastguard Workerclass RpcServer extends Server 34*cc02d7e2SAndroid Build Coastguard Worker{ 35*cc02d7e2SAndroid Build Coastguard Worker // [ <String method_full_path> => MethodDescriptor ] 36*cc02d7e2SAndroid Build Coastguard Worker private $paths_map = []; 37*cc02d7e2SAndroid Build Coastguard Worker 38*cc02d7e2SAndroid Build Coastguard Worker private function waitForNextEvent() 39*cc02d7e2SAndroid Build Coastguard Worker { 40*cc02d7e2SAndroid Build Coastguard Worker return $this->requestCall(); 41*cc02d7e2SAndroid Build Coastguard Worker } 42*cc02d7e2SAndroid Build Coastguard Worker 43*cc02d7e2SAndroid Build Coastguard Worker /** 44*cc02d7e2SAndroid Build Coastguard Worker * Add a service to this server 45*cc02d7e2SAndroid Build Coastguard Worker * 46*cc02d7e2SAndroid Build Coastguard Worker * @param Object $service The service to be added 47*cc02d7e2SAndroid Build Coastguard Worker */ 48*cc02d7e2SAndroid Build Coastguard Worker public function handle($service) 49*cc02d7e2SAndroid Build Coastguard Worker { 50*cc02d7e2SAndroid Build Coastguard Worker $methodDescriptors = $service->getMethodDescriptors(); 51*cc02d7e2SAndroid Build Coastguard Worker $exist_methods = array_intersect_key($this->paths_map, $methodDescriptors); 52*cc02d7e2SAndroid Build Coastguard Worker if (!empty($exist_methods)) { 53*cc02d7e2SAndroid Build Coastguard Worker fwrite(STDERR, "WARNING: " . 'override already registered methods: ' . 54*cc02d7e2SAndroid Build Coastguard Worker implode(', ', array_keys($exist_methods)) . PHP_EOL); 55*cc02d7e2SAndroid Build Coastguard Worker } 56*cc02d7e2SAndroid Build Coastguard Worker 57*cc02d7e2SAndroid Build Coastguard Worker $this->paths_map = array_merge($this->paths_map, $methodDescriptors); 58*cc02d7e2SAndroid Build Coastguard Worker return $this->paths_map; 59*cc02d7e2SAndroid Build Coastguard Worker } 60*cc02d7e2SAndroid Build Coastguard Worker 61*cc02d7e2SAndroid Build Coastguard Worker public function run() 62*cc02d7e2SAndroid Build Coastguard Worker { 63*cc02d7e2SAndroid Build Coastguard Worker $this->start(); 64*cc02d7e2SAndroid Build Coastguard Worker while (true) try { 65*cc02d7e2SAndroid Build Coastguard Worker // This blocks until the server receives a request 66*cc02d7e2SAndroid Build Coastguard Worker $event = $this->waitForNextEvent(); 67*cc02d7e2SAndroid Build Coastguard Worker 68*cc02d7e2SAndroid Build Coastguard Worker $full_path = $event->method; 69*cc02d7e2SAndroid Build Coastguard Worker $context = new ServerContext($event); 70*cc02d7e2SAndroid Build Coastguard Worker $server_writer = new ServerCallWriter($event->call, $context); 71*cc02d7e2SAndroid Build Coastguard Worker 72*cc02d7e2SAndroid Build Coastguard Worker if (!array_key_exists($full_path, $this->paths_map)) { 73*cc02d7e2SAndroid Build Coastguard Worker $context->setStatus(Status::unimplemented()); 74*cc02d7e2SAndroid Build Coastguard Worker $server_writer->finish(); 75*cc02d7e2SAndroid Build Coastguard Worker continue; 76*cc02d7e2SAndroid Build Coastguard Worker }; 77*cc02d7e2SAndroid Build Coastguard Worker 78*cc02d7e2SAndroid Build Coastguard Worker $method_desc = $this->paths_map[$full_path]; 79*cc02d7e2SAndroid Build Coastguard Worker $server_reader = new ServerCallReader( 80*cc02d7e2SAndroid Build Coastguard Worker $event->call, 81*cc02d7e2SAndroid Build Coastguard Worker $method_desc->request_type 82*cc02d7e2SAndroid Build Coastguard Worker ); 83*cc02d7e2SAndroid Build Coastguard Worker 84*cc02d7e2SAndroid Build Coastguard Worker try { 85*cc02d7e2SAndroid Build Coastguard Worker $this->processCall( 86*cc02d7e2SAndroid Build Coastguard Worker $method_desc, 87*cc02d7e2SAndroid Build Coastguard Worker $server_reader, 88*cc02d7e2SAndroid Build Coastguard Worker $server_writer, 89*cc02d7e2SAndroid Build Coastguard Worker $context 90*cc02d7e2SAndroid Build Coastguard Worker ); 91*cc02d7e2SAndroid Build Coastguard Worker } catch (\Exception $e) { 92*cc02d7e2SAndroid Build Coastguard Worker $context->setStatus(Status::status( 93*cc02d7e2SAndroid Build Coastguard Worker STATUS_INTERNAL, 94*cc02d7e2SAndroid Build Coastguard Worker $e->getMessage() 95*cc02d7e2SAndroid Build Coastguard Worker )); 96*cc02d7e2SAndroid Build Coastguard Worker $server_writer->finish(); 97*cc02d7e2SAndroid Build Coastguard Worker } 98*cc02d7e2SAndroid Build Coastguard Worker } catch (\Exception $e) { 99*cc02d7e2SAndroid Build Coastguard Worker fwrite(STDERR, "ERROR: " . $e->getMessage() . PHP_EOL); 100*cc02d7e2SAndroid Build Coastguard Worker exit(1); 101*cc02d7e2SAndroid Build Coastguard Worker } 102*cc02d7e2SAndroid Build Coastguard Worker } 103*cc02d7e2SAndroid Build Coastguard Worker 104*cc02d7e2SAndroid Build Coastguard Worker private function processCall( 105*cc02d7e2SAndroid Build Coastguard Worker MethodDescriptor $method_desc, 106*cc02d7e2SAndroid Build Coastguard Worker ServerCallReader $server_reader, 107*cc02d7e2SAndroid Build Coastguard Worker ServerCallWriter $server_writer, 108*cc02d7e2SAndroid Build Coastguard Worker ServerContext $context 109*cc02d7e2SAndroid Build Coastguard Worker ) { 110*cc02d7e2SAndroid Build Coastguard Worker // Dispatch to actual server logic 111*cc02d7e2SAndroid Build Coastguard Worker switch ($method_desc->call_type) { 112*cc02d7e2SAndroid Build Coastguard Worker case MethodDescriptor::UNARY_CALL: 113*cc02d7e2SAndroid Build Coastguard Worker $request = $server_reader->read(); 114*cc02d7e2SAndroid Build Coastguard Worker $response = 115*cc02d7e2SAndroid Build Coastguard Worker call_user_func( 116*cc02d7e2SAndroid Build Coastguard Worker array($method_desc->service, $method_desc->method_name), 117*cc02d7e2SAndroid Build Coastguard Worker $request ?? new $method_desc->request_type, 118*cc02d7e2SAndroid Build Coastguard Worker $context 119*cc02d7e2SAndroid Build Coastguard Worker ); 120*cc02d7e2SAndroid Build Coastguard Worker $server_writer->finish($response); 121*cc02d7e2SAndroid Build Coastguard Worker break; 122*cc02d7e2SAndroid Build Coastguard Worker case MethodDescriptor::SERVER_STREAMING_CALL: 123*cc02d7e2SAndroid Build Coastguard Worker $request = $server_reader->read(); 124*cc02d7e2SAndroid Build Coastguard Worker call_user_func( 125*cc02d7e2SAndroid Build Coastguard Worker array($method_desc->service, $method_desc->method_name), 126*cc02d7e2SAndroid Build Coastguard Worker $request ?? new $method_desc->request_type, 127*cc02d7e2SAndroid Build Coastguard Worker $server_writer, 128*cc02d7e2SAndroid Build Coastguard Worker $context 129*cc02d7e2SAndroid Build Coastguard Worker ); 130*cc02d7e2SAndroid Build Coastguard Worker break; 131*cc02d7e2SAndroid Build Coastguard Worker case MethodDescriptor::CLIENT_STREAMING_CALL: 132*cc02d7e2SAndroid Build Coastguard Worker $response = call_user_func( 133*cc02d7e2SAndroid Build Coastguard Worker array($method_desc->service, $method_desc->method_name), 134*cc02d7e2SAndroid Build Coastguard Worker $server_reader, 135*cc02d7e2SAndroid Build Coastguard Worker $context 136*cc02d7e2SAndroid Build Coastguard Worker ); 137*cc02d7e2SAndroid Build Coastguard Worker $server_writer->finish($response); 138*cc02d7e2SAndroid Build Coastguard Worker break; 139*cc02d7e2SAndroid Build Coastguard Worker case MethodDescriptor::BIDI_STREAMING_CALL: 140*cc02d7e2SAndroid Build Coastguard Worker call_user_func( 141*cc02d7e2SAndroid Build Coastguard Worker array($method_desc->service, $method_desc->method_name), 142*cc02d7e2SAndroid Build Coastguard Worker $server_reader, 143*cc02d7e2SAndroid Build Coastguard Worker $server_writer, 144*cc02d7e2SAndroid Build Coastguard Worker $context 145*cc02d7e2SAndroid Build Coastguard Worker ); 146*cc02d7e2SAndroid Build Coastguard Worker break; 147*cc02d7e2SAndroid Build Coastguard Worker default: 148*cc02d7e2SAndroid Build Coastguard Worker throw new \Exception(); 149*cc02d7e2SAndroid Build Coastguard Worker } 150*cc02d7e2SAndroid Build Coastguard Worker } 151*cc02d7e2SAndroid Build Coastguard Worker} 152