mirror of https://github.com/grpc/grpc.git
PHP: php server commit 2/n, add Server Call (#25397)
* php server commit 2, server call * re-run generate_projects.sh * temp solution to avoid using autoload * remove type-hint ofpull/25531/merge
parent
fbc5194df6
commit
061fcbb214
4 changed files with 423 additions and 0 deletions
@ -0,0 +1,52 @@ |
||||
<?php |
||||
/* |
||||
* |
||||
* Copyright 2020 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
namespace Grpc; |
||||
|
||||
/** |
||||
* This is an experimental and incomplete implementation of gRPC server |
||||
* for PHP. APIs are _definitely_ going to be changed. |
||||
* |
||||
* DO NOT USE in production. |
||||
*/ |
||||
|
||||
class ServerCallReader |
||||
{ |
||||
public function __construct($call, string $request_type) |
||||
{ |
||||
$this->call_ = $call; |
||||
$this->request_type_ = $request_type; |
||||
} |
||||
|
||||
public function read() |
||||
{ |
||||
$event = $this->call_->startBatch([ |
||||
OP_RECV_MESSAGE => true, |
||||
]); |
||||
if ($event->message === null) { |
||||
return null; |
||||
} |
||||
$data = new $this->request_type_; |
||||
$data->mergeFromString($event->message); |
||||
return $data; |
||||
} |
||||
|
||||
private $call_; |
||||
private $request_type_; |
||||
} |
@ -0,0 +1,101 @@ |
||||
<?php |
||||
/* |
||||
* |
||||
* Copyright 2020 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
namespace Grpc; |
||||
|
||||
/** |
||||
* This is an experimental and incomplete implementation of gRPC server |
||||
* for PHP. APIs are _definitely_ going to be changed. |
||||
* |
||||
* DO NOT USE in production. |
||||
*/ |
||||
|
||||
class ServerCallWriter |
||||
{ |
||||
public function __construct($call) |
||||
{ |
||||
$this->call_ = $call; |
||||
} |
||||
|
||||
public function start( |
||||
array $initialMetadata, |
||||
$data = null, |
||||
array $options = [] |
||||
) { |
||||
$batch = []; |
||||
$this->addSendInitialMetadataOpIfNotSent($batch, $initialMetadata); |
||||
$this->addSendMessageOpIfHasData($batch, $data, $options); |
||||
$this->call_->startBatch($batch); |
||||
} |
||||
|
||||
public function write( |
||||
$data, |
||||
array $options = [], |
||||
array $initialMetadata = null |
||||
) { |
||||
$batch = []; |
||||
$this->addSendInitialMetadataOpIfNotSent($batch, $initialMetadata); |
||||
$this->addSendMessageOpIfHasData($batch, $data, $options); |
||||
$this->call_->startBatch($batch); |
||||
} |
||||
|
||||
public function finish( |
||||
array $status = null, |
||||
array $initialMetadata = null, |
||||
$data = null, |
||||
array $options = [] |
||||
) { |
||||
$batch = [ |
||||
OP_SEND_STATUS_FROM_SERVER => $status ?? Status::ok(), |
||||
OP_RECV_CLOSE_ON_SERVER => true, |
||||
]; |
||||
$this->addSendInitialMetadataOpIfNotSent($batch, $initialMetadata); |
||||
$this->addSendMessageOpIfHasData($batch, $data, $options); |
||||
$this->call_->startBatch($batch); |
||||
} |
||||
|
||||
//////////////////////////// |
||||
|
||||
private function addSendInitialMetadataOpIfNotSent( |
||||
array &$batch, |
||||
array $initialMetadata = null |
||||
) { |
||||
if (!$this->initialMetadataSent_) { |
||||
$batch[OP_SEND_INITIAL_METADATA] = $initialMetadata ?? []; |
||||
$this->initialMetadataSent_ = true; |
||||
} |
||||
} |
||||
|
||||
private function addSendMessageOpIfHasData( |
||||
array &$batch, |
||||
$data = null, |
||||
array $options = [] |
||||
) { |
||||
if ($data) { |
||||
$message_array = ['message' => $data->serializeToString()]; |
||||
if (array_key_exists('flags', $options)) { |
||||
$message_array['flags'] = $options['flags']; |
||||
} |
||||
$batch[OP_SEND_MESSAGE] = $message_array; |
||||
} |
||||
} |
||||
|
||||
private $call_; |
||||
private $initialMetadataSent_ = false; |
||||
} |
@ -0,0 +1,268 @@ |
||||
<?php |
||||
/* |
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
require_once(dirname(__FILE__) . '/../../lib/Grpc/ServerCallReader.php'); |
||||
require_once(dirname(__FILE__) . '/../../lib/Grpc/ServerCallWriter.php'); |
||||
require_once(dirname(__FILE__) . '/../../lib/Grpc/Status.php'); |
||||
|
||||
// load protobuf from third_party |
||||
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/../../../../third_party/protobuf/php/src/'); |
||||
|
||||
spl_autoload_register(function ($className) { |
||||
$classPath = str_replace('\\', DIRECTORY_SEPARATOR, $className); |
||||
if (strpos($classPath, 'Google/Protobuf') === 0 || strpos($classPath, 'GPBMetadata/Google/Protobuf') === 0) { |
||||
require_once($classPath . '.php'); |
||||
} |
||||
}); |
||||
|
||||
class StartBatchEvent |
||||
{ |
||||
public function __construct(string $message) |
||||
{ |
||||
$this->message = $message; |
||||
} |
||||
public $message; |
||||
} |
||||
|
||||
class ServerCallTest extends \PHPUnit\Framework\TestCase |
||||
{ |
||||
public function setUp(): void |
||||
{ |
||||
$this->mockCall = $this->getMockBuilder(stdClass::class) |
||||
->setMethods(['startBatch']) |
||||
->getMock(); |
||||
} |
||||
|
||||
public function newStringMessage(string $value = 'a string') |
||||
{ |
||||
$message = new \Google\Protobuf\StringValue(); |
||||
$message->setValue($value); |
||||
return $message; |
||||
} |
||||
|
||||
public function testRead() |
||||
{ |
||||
$message = $this->newStringMessage(); |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_RECV_MESSAGE => true, |
||||
]))->willReturn(new StartBatchEvent($message->serializeToString())); |
||||
|
||||
$serverCallReader = new \Grpc\ServerCallReader( |
||||
$this->mockCall, |
||||
'\Google\Protobuf\StringValue' |
||||
); |
||||
$return = $serverCallReader->read(); |
||||
$this->assertEquals($message, $return); |
||||
} |
||||
|
||||
public function testStartEmptyMetadata() |
||||
{ |
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_INITIAL_METADATA => [], |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->start([]); |
||||
} |
||||
|
||||
public function testStartWithMetadata() |
||||
{ |
||||
$metadata = ['a' => 1]; |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_INITIAL_METADATA => $metadata, |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->start($metadata); |
||||
return $serverCallWriter; |
||||
} |
||||
|
||||
public function testStartWithMessage() |
||||
{ |
||||
$metadata = ['a' => 1]; |
||||
$message = $this->newStringMessage(); |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_INITIAL_METADATA => $metadata, |
||||
\Grpc\OP_SEND_MESSAGE => ['message' => $message->serializeToString()], |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->start($metadata, $message); |
||||
} |
||||
|
||||
public function testWriteStartWithMessageAndOptions() |
||||
{ |
||||
$metadata = ['a' => 1]; |
||||
$message = $this->newStringMessage(); |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_INITIAL_METADATA => $metadata, |
||||
\Grpc\OP_SEND_MESSAGE => [ |
||||
'message' => $message->serializeToString(), |
||||
'flags' => 0x02, |
||||
], |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->start($metadata, $message, ['flags' => 0x02]); |
||||
} |
||||
|
||||
public function testWriteDataOnly() |
||||
{ |
||||
$message = $this->newStringMessage(); |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_INITIAL_METADATA => [], |
||||
\Grpc\OP_SEND_MESSAGE => ['message' => $message->serializeToString()], |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->write($message); |
||||
} |
||||
|
||||
public function testWriteDataWithOptions() |
||||
{ |
||||
$message = $this->newStringMessage(); |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_INITIAL_METADATA => [], |
||||
\Grpc\OP_SEND_MESSAGE => [ |
||||
'message' => $message->serializeToString(), |
||||
'flags' => 0x02 |
||||
], |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->write($message, ['flags' => 0x02]); |
||||
} |
||||
|
||||
public function testWriteDataWithMetadata() |
||||
{ |
||||
$metadata = ['a' => 1]; |
||||
$message = $this->newStringMessage(); |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_INITIAL_METADATA => $metadata, |
||||
\Grpc\OP_SEND_MESSAGE => ['message' => $message->serializeToString()], |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->write($message, [], $metadata); |
||||
} |
||||
|
||||
public function testFinish() |
||||
{ |
||||
$status = \Grpc\Status::status( |
||||
\Grpc\STATUS_INVALID_ARGUMENT, |
||||
"invalid argument", |
||||
['trailiingMeta' => 100] |
||||
); |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_STATUS_FROM_SERVER => $status, |
||||
\Grpc\OP_RECV_CLOSE_ON_SERVER => true, |
||||
\Grpc\OP_SEND_INITIAL_METADATA => [], |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->finish($status); |
||||
} |
||||
|
||||
public function testFinishWithMetadataAndMessage() |
||||
{ |
||||
$metadata = ['a' => 1]; |
||||
$message = $this->newStringMessage(); |
||||
$status = \Grpc\Status::ok(['trailiingMeta' => 100]); |
||||
|
||||
$this->mockCall->expects($this->once()) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_STATUS_FROM_SERVER => $status, |
||||
\Grpc\OP_RECV_CLOSE_ON_SERVER => true, |
||||
\Grpc\OP_SEND_INITIAL_METADATA => $metadata, |
||||
\Grpc\OP_SEND_MESSAGE => [ |
||||
'message' => $message->serializeToString(), |
||||
'flags' => 0x02, |
||||
], |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->finish($status, $metadata, $message, ['flags' => 0x02]); |
||||
} |
||||
|
||||
public function testStartWriteFinish() |
||||
{ |
||||
$metadata = ['a' => 1]; |
||||
$metadata2 = ['a' => 2]; |
||||
$message1 = $this->newStringMessage(); |
||||
$message2 = $this->newStringMessage('another string'); |
||||
|
||||
$this->mockCall->expects($this->at(0)) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_INITIAL_METADATA => $metadata, |
||||
])); |
||||
$this->mockCall->expects($this->at(1)) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_MESSAGE => ['message' => $message1->serializeToString()], |
||||
])); |
||||
$this->mockCall->expects($this->at(2)) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_MESSAGE => [ |
||||
'message' => $message2->serializeToString(), |
||||
'flags' => 0x02, |
||||
] |
||||
])); |
||||
$this->mockCall->expects($this->at(3)) |
||||
->method('startBatch') |
||||
->with($this->identicalTo([ |
||||
\Grpc\OP_SEND_STATUS_FROM_SERVER => \Grpc\Status::ok(), |
||||
\Grpc\OP_RECV_CLOSE_ON_SERVER => true, |
||||
])); |
||||
|
||||
$serverCallWriter = new \Grpc\ServerCallWriter($this->mockCall); |
||||
$serverCallWriter->start($metadata); |
||||
$serverCallWriter->write($message1, [], $metadata2 /* should not send */); |
||||
$serverCallWriter->write($message2, ['flags' => 0x02]); |
||||
$serverCallWriter->finish(); |
||||
} |
||||
} |
Loading…
Reference in new issue