add
This commit is contained in:
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc;
|
||||
|
||||
class Error implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = '';
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @param int $code
|
||||
* @param string $message
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return Error
|
||||
*/
|
||||
public static function make(int $code, string $message, $data = null): self
|
||||
{
|
||||
$instance = new static();
|
||||
|
||||
$instance->code = $code;
|
||||
$instance->message = $message;
|
||||
$instance->data = $data;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCode(): int
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'code' => $this->code,
|
||||
'message' => $this->message,
|
||||
'data' => $this->data,
|
||||
];
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class File extends \think\File
|
||||
{
|
||||
public function __destruct()
|
||||
{
|
||||
//销毁时删除临时文件
|
||||
try {
|
||||
if (file_exists($this->getPathname())) {
|
||||
unlink($this->getPathname());
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc;
|
||||
|
||||
use Exception;
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
|
||||
class JsonParser implements ParserInterface
|
||||
{
|
||||
/**
|
||||
* Json-rpc version
|
||||
*/
|
||||
const VERSION = '2.0';
|
||||
|
||||
const DELIMITER = "@";
|
||||
|
||||
/**
|
||||
* @param Protocol $protocol
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encode(Protocol $protocol): string
|
||||
{
|
||||
$interface = $protocol->getInterface();
|
||||
$methodName = $protocol->getMethod();
|
||||
|
||||
$method = $interface . self::DELIMITER . $methodName;
|
||||
$data = [
|
||||
'jsonrpc' => self::VERSION,
|
||||
'method' => $method,
|
||||
'params' => $protocol->getParams(),
|
||||
'id' => '',
|
||||
];
|
||||
|
||||
$string = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return Protocol
|
||||
*/
|
||||
public function decode(string $string): Protocol
|
||||
{
|
||||
$data = json_decode($string, true);
|
||||
|
||||
$error = json_last_error();
|
||||
if ($error != JSON_ERROR_NONE) {
|
||||
throw new Exception(
|
||||
sprintf('Data(%s) is not json format!', $string)
|
||||
);
|
||||
}
|
||||
|
||||
$method = $data['method'] ?? '';
|
||||
$params = $data['params'] ?? [];
|
||||
|
||||
if (empty($method)) {
|
||||
throw new Exception(
|
||||
sprintf('Method(%s) cant not be empty!', $string)
|
||||
);
|
||||
}
|
||||
|
||||
$methodAry = explode(self::DELIMITER, $method);
|
||||
if (count($methodAry) < 2) {
|
||||
throw new Exception(
|
||||
sprintf('Method(%s) is bad format!', $method)
|
||||
);
|
||||
}
|
||||
|
||||
[$interfaceClass, $methodName] = $methodAry;
|
||||
|
||||
if (empty($interfaceClass) || empty($methodName)) {
|
||||
throw new Exception(
|
||||
sprintf('Interface(%s) or Method(%s) can not be empty!', $interfaceClass, $method)
|
||||
);
|
||||
}
|
||||
|
||||
return Protocol::make($interfaceClass, $methodName, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decodeResponse(string $string)
|
||||
{
|
||||
$data = json_decode($string, true);
|
||||
|
||||
if (array_key_exists('result', $data)) {
|
||||
return $data['result'];
|
||||
}
|
||||
|
||||
$code = $data['error']['code'] ?? 0;
|
||||
$message = $data['error']['message'] ?? '';
|
||||
$data = $data['error']['data'] ?? null;
|
||||
|
||||
return Error::make($code, $message, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $result
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encodeResponse($result): string
|
||||
{
|
||||
$data = [
|
||||
'jsonrpc' => self::VERSION,
|
||||
];
|
||||
|
||||
if ($result instanceof Error) {
|
||||
$data['error'] = $result;
|
||||
} else {
|
||||
$data['result'] = $result;
|
||||
}
|
||||
|
||||
$string = json_encode($data);
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc;
|
||||
|
||||
class Packer
|
||||
{
|
||||
const HEADER_SIZE = 8;
|
||||
const HEADER_STRUCT = "Nlength/Ntype";
|
||||
const HEADER_PACK = "NN";
|
||||
|
||||
const TYPE_BUFFER = 0;
|
||||
const TYPE_FILE = 1;
|
||||
|
||||
public static function pack($data, $type = self::TYPE_BUFFER)
|
||||
{
|
||||
return pack(self::HEADER_PACK, strlen($data), $type) . $data;
|
||||
}
|
||||
|
||||
public static function unpack($data)
|
||||
{
|
||||
$header = unpack(self::HEADER_STRUCT, substr($data, 0, self::HEADER_SIZE));
|
||||
$data = substr($data, self::HEADER_SIZE);
|
||||
|
||||
return [$header, $data];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc;
|
||||
|
||||
class Protocol
|
||||
{
|
||||
const ACTION_INTERFACE = '@action_interface';
|
||||
const FILE = '@file';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $interface = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $method = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $params = [];
|
||||
|
||||
/**
|
||||
* Replace constructor
|
||||
*
|
||||
* @param string $interface
|
||||
* @param string $method
|
||||
* @param array $params
|
||||
*
|
||||
* @return Protocol
|
||||
*/
|
||||
public static function make(string $interface, string $method, array $params)
|
||||
{
|
||||
$instance = new static();
|
||||
|
||||
$instance->interface = $interface;
|
||||
$instance->method = $method;
|
||||
$instance->params = $params;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getInterface(): string
|
||||
{
|
||||
return $this->interface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getParams(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\client;
|
||||
|
||||
use Generator;
|
||||
|
||||
interface Connector
|
||||
{
|
||||
/**
|
||||
* @param Generator|string $data
|
||||
* @return string
|
||||
*/
|
||||
public function sendAndRecv($data);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\client;
|
||||
|
||||
use RuntimeException;
|
||||
use Swoole\Client;
|
||||
use Swoole\Coroutine;
|
||||
use think\File;
|
||||
use think\helper\Arr;
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
use think\swoole\exception\RpcClientException;
|
||||
use think\swoole\exception\RpcResponseException;
|
||||
use think\swoole\rpc\Error;
|
||||
use think\swoole\rpc\Packer;
|
||||
use think\swoole\rpc\Protocol;
|
||||
|
||||
class Gateway
|
||||
{
|
||||
protected $connector;
|
||||
|
||||
/** @var ParserInterface */
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Gateway constructor.
|
||||
* @param Connector|array $connector
|
||||
* @param ParserInterface $parser
|
||||
*/
|
||||
public function __construct($connector, ParserInterface $parser)
|
||||
{
|
||||
if (is_array($connector)) {
|
||||
$connector = $this->createDefaultConnector($connector);
|
||||
}
|
||||
$this->connector = $connector;
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
protected function encodeData(Protocol $protocol)
|
||||
{
|
||||
$params = $protocol->getParams();
|
||||
|
||||
//有文件,先传输
|
||||
foreach ($params as $index => $param) {
|
||||
if ($param instanceof File) {
|
||||
$handle = fopen($param->getPathname(), "rb");
|
||||
yield pack(Packer::HEADER_PACK, $param->getSize(), Packer::TYPE_FILE);
|
||||
while (!feof($handle)) {
|
||||
yield fread($handle, 8192);
|
||||
}
|
||||
fclose($handle);
|
||||
$params[$index] = Protocol::FILE;
|
||||
}
|
||||
}
|
||||
|
||||
$protocol = Protocol::make($protocol->getInterface(), $protocol->getMethod(), $params);
|
||||
|
||||
$data = $this->parser->encode($protocol);
|
||||
|
||||
yield Packer::pack($data);
|
||||
}
|
||||
|
||||
protected function decodeResponse($response)
|
||||
{
|
||||
[, $response] = Packer::unpack($response);
|
||||
$result = $this->parser->decodeResponse($response);
|
||||
|
||||
if ($result instanceof Error) {
|
||||
throw new RpcResponseException($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function sendAndRecv(Protocol $protocol)
|
||||
{
|
||||
$response = $this->connector->sendAndRecv($this->encodeData($protocol));
|
||||
|
||||
return $this->decodeResponse($response);
|
||||
}
|
||||
|
||||
public function getServices()
|
||||
{
|
||||
$response = $this->connector->sendAndRecv(Packer::pack(Protocol::ACTION_INTERFACE));
|
||||
|
||||
return $this->decodeResponse($response);
|
||||
}
|
||||
|
||||
protected function createDefaultConnector($config)
|
||||
{
|
||||
$class = Coroutine::getCid() > -1 ? Coroutine\Client::class : Client::class;
|
||||
|
||||
/** @var Client|Coroutine\Client $client */
|
||||
$client = new $class(SWOOLE_SOCK_TCP);
|
||||
|
||||
$host = Arr::pull($config, 'host');
|
||||
$port = Arr::pull($config, 'port');
|
||||
$timeout = Arr::pull($config, 'timeout');
|
||||
|
||||
$client->set(array_merge($config, [
|
||||
'open_length_check' => true,
|
||||
'package_length_type' => Packer::HEADER_PACK,
|
||||
'package_length_offset' => 0,
|
||||
'package_body_offset' => 8,
|
||||
]));
|
||||
|
||||
if (!$client->connect($host, $port, $timeout)) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Connect failed host=%s port=%d', $host, $port)
|
||||
);
|
||||
}
|
||||
|
||||
return new class($client) implements Connector {
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* constructor.
|
||||
* @param Client|Coroutine\Client $client
|
||||
*/
|
||||
public function __construct($client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
public function sendAndRecv($data)
|
||||
{
|
||||
if (!$data instanceof \Generator) {
|
||||
$data = [$data];
|
||||
}
|
||||
|
||||
foreach ($data as $string) {
|
||||
if (!$this->client->send($string)) {
|
||||
throw new RpcClientException(swoole_strerror($this->client->errCode), $this->client->errCode);
|
||||
}
|
||||
}
|
||||
|
||||
$response = $this->client->recv();
|
||||
|
||||
if ($response === false || empty($response)) {
|
||||
throw new RpcClientException(swoole_strerror($this->client->errCode), $this->client->errCode);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\client;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Nette\PhpGenerator\Factory;
|
||||
use Nette\PhpGenerator\PhpNamespace;
|
||||
use ReflectionClass;
|
||||
use think\swoole\rpc\Protocol;
|
||||
|
||||
abstract class Proxy
|
||||
{
|
||||
protected $interface;
|
||||
|
||||
/** @var Gateway */
|
||||
protected $gateway;
|
||||
|
||||
final public function __construct(Gateway $gateway)
|
||||
{
|
||||
$this->gateway = $gateway;
|
||||
}
|
||||
|
||||
final protected function proxyCall($method, $params)
|
||||
{
|
||||
$protocol = Protocol::make($this->interface, $method, $params);
|
||||
|
||||
return $this->gateway->sendAndRecv($protocol);
|
||||
}
|
||||
|
||||
final public static function getClassName($client, $interface)
|
||||
{
|
||||
if (!interface_exists($interface)) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf('%s must be exist interface!', $interface)
|
||||
);
|
||||
}
|
||||
|
||||
$proxyName = class_basename($interface) . "Service";
|
||||
$className = "rpc\\service\\{$client}\\{$proxyName}";
|
||||
|
||||
if (!class_exists($className, false)) {
|
||||
|
||||
$namespace = new PhpNamespace("rpc\\service\\{$client}");
|
||||
$namespace->addUse(Proxy::class);
|
||||
$namespace->addUse($interface);
|
||||
|
||||
$class = $namespace->addClass($proxyName);
|
||||
|
||||
$class->setExtends(Proxy::class);
|
||||
$class->addImplement($interface);
|
||||
$class->addProperty('interface', class_basename($interface));
|
||||
|
||||
$reflection = new ReflectionClass($interface);
|
||||
|
||||
foreach ($reflection->getMethods() as $methodRef) {
|
||||
$method = (new Factory)->fromMethodReflection($methodRef);
|
||||
$body = "\$this->proxyCall('{$methodRef->getName()}', func_get_args());";
|
||||
if ($method->getReturnType() != 'void') {
|
||||
$body = "return {$body}";
|
||||
}
|
||||
$method->setBody($body);
|
||||
$class->addMember($method);
|
||||
}
|
||||
|
||||
eval($namespace);
|
||||
}
|
||||
return $className;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\server;
|
||||
|
||||
use Swoole\Coroutine;
|
||||
use think\swoole\rpc\Packer;
|
||||
use think\swoole\rpc\server\channel\Buffer;
|
||||
use think\swoole\rpc\server\channel\File;
|
||||
|
||||
class Channel
|
||||
{
|
||||
protected $header;
|
||||
|
||||
protected $queue;
|
||||
|
||||
public function __construct($header)
|
||||
{
|
||||
$this->header = $header;
|
||||
$this->queue = new Coroutine\Channel(1);
|
||||
|
||||
Coroutine::create(function () use ($header) {
|
||||
switch ($header['type']) {
|
||||
case Packer::TYPE_BUFFER:
|
||||
$type = Buffer::class;
|
||||
break;
|
||||
case Packer::TYPE_FILE:
|
||||
$type = File::class;
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException('not support data type');
|
||||
}
|
||||
|
||||
$handle = new $type($header['length']);
|
||||
$this->queue->push($handle);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return File|Buffer
|
||||
*/
|
||||
public function pop()
|
||||
{
|
||||
return $this->queue->pop();
|
||||
}
|
||||
|
||||
public function push($handle)
|
||||
{
|
||||
return $this->queue->push($handle);
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
return $this->queue->close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\server;
|
||||
|
||||
use Exception;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionNamedType;
|
||||
use Swoole\Server;
|
||||
use think\App;
|
||||
use think\swoole\contract\rpc\ParserInterface;
|
||||
use think\swoole\rpc\Error;
|
||||
use think\swoole\rpc\File;
|
||||
use think\swoole\rpc\Packer;
|
||||
use think\swoole\rpc\Protocol;
|
||||
use Throwable;
|
||||
|
||||
class Dispatcher
|
||||
{
|
||||
/**
|
||||
* Parser error
|
||||
*/
|
||||
const PARSER_ERROR = -32700;
|
||||
|
||||
/**
|
||||
* Invalid Request
|
||||
*/
|
||||
const INVALID_REQUEST = -32600;
|
||||
|
||||
/**
|
||||
* Method not found
|
||||
*/
|
||||
const METHOD_NOT_FOUND = -32601;
|
||||
|
||||
/**
|
||||
* Invalid params
|
||||
*/
|
||||
const INVALID_PARAMS = -32602;
|
||||
|
||||
/**
|
||||
* Internal error
|
||||
*/
|
||||
const INTERNAL_ERROR = -32603;
|
||||
|
||||
protected $app;
|
||||
|
||||
protected $parser;
|
||||
|
||||
protected $services = [];
|
||||
|
||||
protected $server;
|
||||
|
||||
protected $files = [];
|
||||
|
||||
public function __construct(App $app, ParserInterface $parser, Server $server, $services)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->parser = $parser;
|
||||
$this->server = $server;
|
||||
$this->prepareServices($services);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务接口
|
||||
* @param $services
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function prepareServices($services)
|
||||
{
|
||||
foreach ($services as $className) {
|
||||
$reflectionClass = new ReflectionClass($className);
|
||||
$interfaces = $reflectionClass->getInterfaceNames();
|
||||
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->services[class_basename($interface)] = [
|
||||
'interface' => $interface,
|
||||
'class' => $className,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口信息
|
||||
* @return array
|
||||
*/
|
||||
protected function getInterfaces()
|
||||
{
|
||||
$interfaces = [];
|
||||
foreach ($this->services as $key => ['interface' => $interface]) {
|
||||
$interfaces[$key] = $this->getMethods($interface);
|
||||
}
|
||||
return $interfaces;
|
||||
}
|
||||
|
||||
protected function getMethods($interface)
|
||||
{
|
||||
$methods = [];
|
||||
|
||||
$reflection = new ReflectionClass($interface);
|
||||
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
$returnType = $method->getReturnType();
|
||||
if ($returnType instanceof ReflectionNamedType) {
|
||||
$returnType = $returnType->getName();
|
||||
}
|
||||
$methods[$method->getName()] = [
|
||||
'parameters' => $this->getParameters($method),
|
||||
'returnType' => $returnType,
|
||||
'comment' => $method->getDocComment(),
|
||||
];
|
||||
}
|
||||
return $methods;
|
||||
}
|
||||
|
||||
protected function getParameters(ReflectionMethod $method)
|
||||
{
|
||||
$parameters = [];
|
||||
foreach ($method->getParameters() as $parameter) {
|
||||
$type = $parameter->getType();
|
||||
if ($type instanceof ReflectionNamedType) {
|
||||
$type = $type->getName();
|
||||
}
|
||||
$param = [
|
||||
'name' => $parameter->getName(),
|
||||
'type' => $type,
|
||||
];
|
||||
|
||||
if ($parameter->isOptional()) {
|
||||
$param['default'] = $parameter->getDefaultValue();
|
||||
}
|
||||
|
||||
$parameters[] = $param;
|
||||
}
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调度
|
||||
* @param int $fd
|
||||
* @param string|File $data
|
||||
*/
|
||||
public function dispatch(int $fd, $data)
|
||||
{
|
||||
if ($data instanceof File) {
|
||||
$this->files[$fd][] = $data;
|
||||
} else {
|
||||
try {
|
||||
if ($data === Protocol::ACTION_INTERFACE) {
|
||||
$result = $this->getInterfaces();
|
||||
} else {
|
||||
$protocol = $this->parser->decode($data);
|
||||
|
||||
$interface = $protocol->getInterface();
|
||||
$method = $protocol->getMethod();
|
||||
$params = $protocol->getParams();
|
||||
|
||||
//文件参数
|
||||
foreach ($params as $index => $param) {
|
||||
if ($param === Protocol::FILE) {
|
||||
$params[$index] = array_shift($this->files[$fd]);
|
||||
}
|
||||
}
|
||||
|
||||
$service = $this->services[$interface] ?? null;
|
||||
if (empty($service)) {
|
||||
throw new Exception(
|
||||
sprintf('Service %s is not founded!', $interface),
|
||||
self::INVALID_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
$result = $this->app->invoke([$this->app->make($service['class']), $method], $params);
|
||||
}
|
||||
} catch (Throwable | Exception $e) {
|
||||
$result = Error::make($e->getCode(), $e->getMessage());
|
||||
}
|
||||
|
||||
$data = $this->parser->encodeResponse($result);
|
||||
|
||||
$this->server->send($fd, Packer::pack($data));
|
||||
//清空文件缓存
|
||||
unset($this->files[$fd]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\server\channel;
|
||||
|
||||
class Buffer
|
||||
{
|
||||
protected $data = '';
|
||||
protected $length;
|
||||
|
||||
public function __construct($length)
|
||||
{
|
||||
$this->length = $length;
|
||||
}
|
||||
|
||||
public function write(&$data)
|
||||
{
|
||||
$size = strlen($this->data);
|
||||
$string = substr($data, 0, $this->length - $size);
|
||||
|
||||
$this->data .= $string;
|
||||
|
||||
if (strlen($data) >= $this->length - $size) {
|
||||
$data = substr($data, $this->length - $size);
|
||||
|
||||
return $this->data;
|
||||
} else {
|
||||
$data = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace think\swoole\rpc\server\channel;
|
||||
|
||||
class File
|
||||
{
|
||||
protected $name;
|
||||
protected $handle;
|
||||
protected $length;
|
||||
|
||||
public function __construct($length)
|
||||
{
|
||||
$this->name = tempnam(sys_get_temp_dir(), "swoole_rpc_");
|
||||
$this->handle = fopen($this->name, 'ab');
|
||||
$this->length = $length;
|
||||
}
|
||||
|
||||
public function write(&$data)
|
||||
{
|
||||
$size = fstat($this->handle)['size'];
|
||||
$string = substr($data, 0, $this->length - $size);
|
||||
|
||||
fwrite($this->handle, $string);
|
||||
|
||||
if (strlen($data) >= $this->length - $size) {
|
||||
fclose($this->handle);
|
||||
$data = substr($data, $this->length - $size);
|
||||
|
||||
return new \think\swoole\rpc\File($this->name);
|
||||
} else {
|
||||
$data = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user