Skip to content

Commit 3a4b0ef

Browse files
Update to v1.30.0
1 parent a461cb1 commit 3a4b0ef

File tree

3 files changed

+174
-25
lines changed

3 files changed

+174
-25
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"keywords": ["rpc"],
66
"homepage": "https://grpc.io",
77
"license": "Apache-2.0",
8-
"version": "1.27.0",
8+
"version": "1.30.0",
99
"require": {
1010
"php": ">=5.5.0"
1111
},

src/lib/AbstractCall.php

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,7 @@ public function cancel()
114114
protected function _serializeMessage($data)
115115
{
116116
// Proto3 implementation
117-
if (method_exists($data, 'encode')) {
118-
return $data->encode();
119-
} elseif (method_exists($data, 'serializeToString')) {
120-
return $data->serializeToString();
121-
}
122-
123-
// Protobuf-PHP implementation
124-
return $data->serialize();
117+
return $data->serializeToString();
125118
}
126119

127120
/**
@@ -136,22 +129,10 @@ protected function _deserializeResponse($value)
136129
if ($value === null) {
137130
return;
138131
}
139-
140-
// Proto3 implementation
141-
if (is_array($this->deserialize)) {
142-
list($className, $deserializeFunc) = $this->deserialize;
143-
$obj = new $className();
144-
if (method_exists($obj, $deserializeFunc)) {
145-
$obj->$deserializeFunc($value);
146-
} else {
147-
$obj->mergeFromString($value);
148-
}
149-
150-
return $obj;
151-
}
152-
153-
// Protobuf-PHP implementation
154-
return call_user_func($this->deserialize, $value);
132+
list($className, $deserializeFunc) = $this->deserialize;
133+
$obj = new $className();
134+
$obj->mergeFromString($value);
135+
return $obj;
155136
}
156137

157138
/**

src/lib/RpcServer.php

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
/*
3+
*
4+
* Copyright 2020 gRPC authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
*/
19+
20+
namespace Grpc;
21+
22+
/**
23+
* This is an experimental and incomplete implementation of gRPC server
24+
* for PHP. APIs are _definitely_ going to be changed.
25+
*
26+
* DO NOT USE in production.
27+
*/
28+
29+
/**
30+
* Class RpcServer
31+
* @package Grpc
32+
*/
33+
class RpcServer extends Server
34+
{
35+
protected $call;
36+
// [ <String method_full_path> => [
37+
// 'service' => <Object service>,
38+
// 'method' => <String method_name>,
39+
// 'request' => <Object request>,
40+
// ] ]
41+
protected $paths_map;
42+
43+
private function waitForNextEvent() {
44+
return $this->requestCall();
45+
}
46+
47+
private function loadRequest($request) {
48+
if (!$this->call) {
49+
throw new Exception("serverCall is not ready");
50+
}
51+
$event = $this->call->startBatch([
52+
OP_RECV_MESSAGE => true,
53+
]);
54+
if (!$event->message) {
55+
throw new Exception("Did not receive a proper message");
56+
}
57+
$request->mergeFromString($event->message);
58+
return $request;
59+
}
60+
61+
protected function sendOkResponse($response) {
62+
if (!$this->call) {
63+
throw new Exception("serverCall is not ready");
64+
}
65+
$this->call->startBatch([
66+
OP_SEND_INITIAL_METADATA => [],
67+
OP_SEND_MESSAGE => ['message' =>
68+
$response->serializeToString()],
69+
OP_SEND_STATUS_FROM_SERVER => [
70+
'metadata' => [],
71+
'code' => STATUS_OK,
72+
'details' => 'OK',
73+
],
74+
OP_RECV_CLOSE_ON_SERVER => true,
75+
]);
76+
}
77+
78+
/**
79+
* Add a service to this server
80+
*
81+
* @param Object $service The service to be added
82+
*/
83+
public function handle($service) {
84+
$rf = new \ReflectionClass($service);
85+
86+
// If input does not have a parent class, which should be the
87+
// generated stub, don't proceeed. This might change in the
88+
// future.
89+
if (!$rf->getParentClass()) return;
90+
91+
// The input class name needs to match the service name
92+
$service_name = $rf->getName();
93+
$namespace = $rf->getParentClass()->getNamespaceName();
94+
$prefix = "";
95+
if ($namespace) {
96+
$parts = explode("\\", $namespace);
97+
foreach ($parts as $part) {
98+
$prefix .= lcfirst($part) . ".";
99+
}
100+
}
101+
$base_path = "/" . $prefix . $service_name;
102+
103+
// Right now, assume all the methods in the class are RPC method
104+
// implementations. Might change in the future.
105+
$methods = $rf->getMethods();
106+
foreach ($methods as $method) {
107+
$method_name = $method->getName();
108+
$full_path = $base_path . "/" . ucfirst($method_name);
109+
110+
$method_params = $method->getParameters();
111+
// RPC should have exactly 1 request param
112+
if (count($method_params) != 1) continue;
113+
$request_param = $method_params[0];
114+
// Method implementation must have type hint for request param
115+
if (!$request_param->getType()) continue;
116+
$request_type = $request_param->getType()->getName();
117+
118+
// $full_path needs to match the incoming event->method
119+
// from requestCall() for us to know how to handle the request
120+
$this->paths_map[$full_path] = [
121+
'service' => $service,
122+
'method' => $method_name,
123+
'request' => new $request_type(),
124+
];
125+
}
126+
}
127+
128+
public function run() {
129+
$this->start();
130+
while (true) {
131+
// This blocks until the server receives a request
132+
$event = $this->waitForNextEvent();
133+
if (!$event) {
134+
throw new Exception(
135+
"Unexpected error: server->waitForNextEvent delivers"
136+
. " an empty event");
137+
}
138+
if (!$event->call) {
139+
throw new Exception(
140+
"Unexpected error: server->waitForNextEvent delivers"
141+
. " an event without a call");
142+
}
143+
$this->call = $event->call;
144+
$full_path = $event->method;
145+
146+
// TODO: Can send a proper UNIMPLEMENTED response in the future
147+
if (!array_key_exists($full_path, $this->paths_map)) continue;
148+
149+
$service = $this->paths_map[$full_path]['service'];
150+
$method = $this->paths_map[$full_path]['method'];
151+
$request = $this->paths_map[$full_path]['request'];
152+
153+
$request = $this->loadRequest($request);
154+
if (!$request) {
155+
throw new Exception("Unexpected error: fail to parse request");
156+
}
157+
if (!method_exists($service, $method)) {
158+
// TODO: Can send a proper UNIMPLEMENTED response in the future
159+
throw new Exception("Method not implemented");
160+
}
161+
162+
// Dispatch to actual server logic
163+
$response = $service->$method($request);
164+
$this->sendOkResponse($response);
165+
$this->call = null;
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)