Commit 78528d36 by 王召彬

删除

parent 47c2869f
.buildpath
.settings/
.project
*.patch
.idea/
.git/
runtime/
vendor/
temp/
*.lock
.phpintel/
.DS_Store
\ No newline at end of file
language: php
php:
- 7.1
- 7.2
- 7.3
install:
- wget https://github.com/swoole/swoole-src/archive/v4.4.6.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd -
- echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
before_script:
- composer config -g process-timeout 900 && composer update
- phpenv config-rm xdebug.ini
script:
- composer test
This diff is collapsed. Click to expand it.
# Swoft Rpc Server
[![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-server.svg)](https://packagist.org/packages/swoft/rpc-server)
[![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/)
[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs)
[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE)
Swoft Rpc Server Component
## Install
- composer command
```bash
composer require swoft/rpc-server
```
## Resources
* [Documentation](https://swoft.org/docs)
* [Contributing](https://github.com/swoft-cloud/swoft/blob/master/CONTRIBUTING.md)
* [Report Issues][issues] and [Send Pull Requests][pulls] in the [Main Swoft Repository][repository]
[pulls]: https://github.com/swoft-cloud/swoft-component/pulls
[repository]: https://github.com/swoft-cloud/swoft
[issues]: https://github.com/swoft-cloud/swoft/issues
## LICENSE
The Component is open-sourced software licensed under the [Apache license](LICENSE).
{
"name": "hdll/swoft-rpc-server",
"type": "library",
"version": "v2.0.7",
"keywords": [
"php",
"swoole",
"swoft",
"rpc"
],
"description": "swoft rpc server component",
"homepage": "https://github.com/swoft-cloud/swoft-rpc-server",
"license": "Apache-2.0",
"require": {
"swoft/framework": "~2.0.0",
"swoft/connection-pool": "~2.0.0",
"swoft/rpc": "~2.0.0"
},
"autoload": {
"psr-4": {
"Swoft\\Rpc\\Server\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"SwoftTest\\Rpc\\Server\\Testing\\": "test/testing",
"SwoftTest\\Rpc\\Server\\Unit\\": "test/unit"
}
},
"require-dev": {
"phpunit/phpunit": "^7.5"
},
"scripts": {
"test": "php run.php -c phpunit.xml"
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
bootstrap="test/bootstrap.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="test">
<directory suffix="Test.php">./test/unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src/*</directory>
</whitelist>
</filter>
</phpunit>
\ No newline at end of file
<?php
/** For Swoole coroutine tests */
use PHPUnit\TextUI\Command;
use Swoole\ExitException;
Co::set([
'log_level' => SWOOLE_LOG_INFO,
'trace_flags' => 0
]);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (version_compare('7.1.0', PHP_VERSION, '>')) {
fwrite(
STDERR,
sprintf(
'This version of PHPUnit is supported on PHP 7.1 and PHP 7.2.' . PHP_EOL .
'You are using PHP %s (%s).' . PHP_EOL,
PHP_VERSION,
PHP_BINARY
)
);
die(1);
}
if (!ini_get('date.timezone')) {
ini_set('date.timezone', 'UTC');
}
foreach ([
__DIR__ . '/../../autoload.php',
__DIR__ . '/../vendor/autoload.php',
__DIR__ . '/vendor/autoload.php'
] as $file
) {
if (file_exists($file)) {
define('PHPUNIT_COMPOSER_INSTALL', $file);
break;
}
}
unset($file);
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
fwrite(
STDERR,
'You need to set up the project dependencies using Composer:' . PHP_EOL . PHP_EOL .
' composer install' . PHP_EOL . PHP_EOL .
'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL
);
die(1);
} else {
if (array_reverse(explode('/', __DIR__))[0] ?? '' === 'tests') {
$vendor_dir = dirname(PHPUNIT_COMPOSER_INSTALL);
$bin_unit = "{$vendor_dir}/bin/phpunit";
$unit_uint = "{$vendor_dir}/phpunit/phpunit/phpunit";
if (file_exists($bin_unit)) {
@unlink($bin_unit);
@symlink(__FILE__, $bin_unit);
}
if (file_exists($unit_uint)) {
@unlink($unit_uint);
@symlink(__FILE__, $unit_uint);
}
}
}
if (!in_array('-c', $_SERVER['argv'])) {
$_SERVER['argv'][] = '-c';
$_SERVER['argv'][] = __DIR__ . '/phpunit.xml';
}
require PHPUNIT_COMPOSER_INSTALL;
$status = 0;
srun(function (){
// Status
global $status;
try {
$status = PHPUnit\TextUI\Command::main(false);
} catch (ExitException $e) {
var_dump($e->getMessage());
$status = $e->getCode();
}
});
exit($status);
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Annotation\Mapping;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
/**
* Class Middleware
*
* @since 2.0
*
* @Annotation
* @Target({"CLASS", "METHOD", "ANNOTATION"})
* @Attributes({
* @Attribute("name", type="string"),
* })
*/
class Middleware
{
/**
* @var string
*
* @Required()
*/
private $name = '';
/**
* Middleware constructor.
*
* @param array $values
*/
public function __construct(array $values)
{
if (isset($values['value'])) {
$this->name = $values['value'];
}
if (isset($values['name'])) {
$this->name = $values['name'];
}
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Annotation\Mapping;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
/**
* Class Middlewares
*
* @since 2.0
*
* @Annotation
* @Target({"CLASS", "METHOD"})
* @Attributes({
* @Attribute("name", type="array"),
* })
*/
class Middlewares
{
/**
* Middlewares
*
* @var Middleware[]
*
* @Required()
*/
private $middlewares = [];
/**
* Middlewares constructor.
*
* @param $values
*/
public function __construct($values)
{
if (isset($values['value'])) {
$this->middlewares = $values['value'];
}
if (isset($values['middlewares'])) {
$this->middlewares = $values['middlewares'];
}
}
/**
* @return Middleware[]
*/
public function getMiddlewares(): array
{
return $this->middlewares;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Annotation\Mapping;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Doctrine\Common\Annotations\Annotation\Target;
use Swoft\Rpc\Protocol;
/**
* Class Service
*
* @since 2.0
*
* @Annotation
* @Target({"CLASS"})
* @Attributes({
* @Attribute("version", type="string"),
* })
*/
class Service
{
/**
* @var string
*/
private $version = Protocol::DEFAULT_VERSION;
/**
* Service constructor.
*
* @param array $values
*/
public function __construct(array $values)
{
if (isset($values['value'])) {
$this->version = $values['value'];
}
if (isset($values['version'])) {
$this->version = $values['version'];
}
}
/**
* @return string
*/
public function getVersion(): string
{
return $this->version;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Annotation\Parser;
use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use Swoft\Rpc\Server\Annotation\Mapping\Middleware;
use Swoft\Rpc\Server\Middleware\MiddlewareRegister;
/**
* Class MiddlewareParser
*
* @since 2.0
*
* @AnnotationParser(Middleware::class)
*/
class MiddlewareParser extends Parser
{
/**
* @param int $type
* @param Middleware $annotationObject
*
* @return array
*/
public function parse(int $type, $annotationObject): array
{
$name =$annotationObject->getName();
if ($type === self::TYPE_CLASS) {
MiddlewareRegister::registerByClassName($name, $this->className);
return [];
}
if ($type === self::TYPE_METHOD) {
MiddlewareRegister::registerByMethodName($name, $this->className, $this->methodName);
return [];
}
return [];
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Annotation\Parser;
use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use Swoft\Rpc\Server\Annotation\Mapping\Middleware;
use Swoft\Rpc\Server\Annotation\Mapping\Middlewares;
use Swoft\Rpc\Server\Middleware\MiddlewareRegister;
/**
* Class MiddlewaresParser
*
* @since 2.0
*
* @AnnotationParser(Middlewares::class)
*/
class MiddlewaresParser extends Parser
{
/**
* @param int $type
* @param Middlewares $annotationObject
*
* @return array
*/
public function parse(int $type, $annotationObject): array
{
$middlewares = $annotationObject->getMiddlewares();
foreach ($middlewares as $middleware) {
if (!$middleware instanceof Middleware) {
continue;
}
$name = $middleware->getName();
if ($type === self::TYPE_CLASS) {
MiddlewareRegister::registerByClassName($name, $this->className);
continue;
}
if ($type === self::TYPE_METHOD) {
MiddlewareRegister::registerByMethodName($name, $this->className, $this->methodName);
}
}
return [];
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Annotation\Parser;
use ReflectionClass;
use ReflectionException;
use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Annotation\Mapping\Service;
use Swoft\Rpc\Server\Router\RouteRegister;
/**
* Class ServiceParser
*
* @since 2.0
*
* @AnnotationParser(annotation=Service::class)
*/
class ServiceParser extends Parser
{
/**
* @param int $type
* @param Service $annotationObject
*
* @return array
* @throws ReflectionException
*/
public function parse(int $type, $annotationObject): array
{
$reflectionClass = new ReflectionClass($this->className);
$interfaces = $reflectionClass->getInterfaceNames();
foreach ($interfaces as $interface) {
RouteRegister::register($interface, $annotationObject->getVersion(), $this->className);
}
return [$this->className, $this->className, Bean::SINGLETON, ''];
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use ReflectionException;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Helper\ComposerJSON;
use Swoft\Rpc\Packet;
use Swoft\Rpc\Server\Swoole\CloseListener;
use Swoft\Rpc\Server\Swoole\ConnectListener;
use Swoft\Rpc\Server\Swoole\ReceiveListener;
use Swoft\Server\SwooleEvent;
use Swoft\SwoftComponent;
use function bean;
/**
* Class AutoLoader
*
* @since 2.0
*/
class AutoLoader extends SwoftComponent
{
/**
* @return array
*/
public function getPrefixDirs(): array
{
return [
__NAMESPACE__ => __DIR__,
];
}
/**
* @return array
*/
public function metadata(): array
{
$jsonFile = dirname(__DIR__) . '/composer.json';
return ComposerJSON::open($jsonFile)->getMetadata();
}
/**
* @return array
*/
public function beans(): array
{
return [
'rpcServer' => [
'class' => ServiceServer::class,
'on' => [
SwooleEvent::CONNECT => bean(ConnectListener::class),
SwooleEvent::CLOSE => bean(CloseListener::class),
SwooleEvent::RECEIVE => bean(ReceiveListener::class),
]
],
'rpcServerPacket' => [
'class' => Packet::class
]
];
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Command;
use Swoft;
use Swoft\Console\Annotation\Mapping\Command;
use Swoft\Console\Annotation\Mapping\CommandMapping;
use Swoft\Console\Annotation\Mapping\CommandOption;
use Swoft\Rpc\Server\ServiceServer;
use Swoft\Server\Command\BaseServerCommand;
use Swoft\Server\Exception\ServerException;
use function bean;
use function input;
use function output;
/**
* Class ServiceServerCommand
*
* @since 2.0
*
* @Command("rpc", coroutine=false, desc="Provide some commands to manage swoft RPC server")
*
* @example
* {fullCmd}:start Start the rpc server
* {fullCmd}:stop Stop the rpc server
*/
class ServiceServerCommand extends BaseServerCommand
{
/**
* Start the http server
*
* @CommandMapping(usage="{fullCommand} [-d|--daemon]")
* @CommandOption("daemon", short="d", desc="Run server on the background")
*
* @throws ServerException
* @example
* {fullCommand}
* {fullCommand} -d
*
*/
public function start(): void
{
$server = $this->createServer();
$this->showServerInfoPanel($server);
// Start the server
$server->start();
}
/**
* Reload worker processes
*
* @CommandMapping(usage="{fullCommand} [-t]")
* @CommandOption("t", desc="Only to reload task processes, default to reload worker and task")
*/
public function reload(): void
{
$server = $this->createServer();
// Reload server
$this->reloadServer($server);
}
/**
* Stop the currently running server
*
* @CommandMapping()
*
*/
public function stop(): void
{
$server = $this->createServer();
// Check if it has started
if (!$server->isRunning()) {
output()->writeln('<error>The RPC server is not running! cannot stop.</error>');
return;
}
// Do stopping.
$server->stop();
}
/**
* Restart the http server
*
* @CommandMapping(usage="{fullCommand} [-d|--daemon]",)
* @CommandOption("daemon", short="d", desc="Run server on the background")
*
* @example
* {fullCommand}
* {fullCommand} -d
*/
public function restart(): void
{
$server = $this->createServer();
// Restart server
$this->restartServer($server);
}
/**
* @return ServiceServer
*/
private function createServer(): ServiceServer
{
$script = input()->getScriptFile();
$command = $this->getFullCommand();
/** @var ServiceServer $server */
$server = bean('rpcServer');
$server->setScriptFile(Swoft::app()->getPath($script));
$server->setFullCommand($command);
return $server;
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Contract;
/**
* Class MiddlewareInterface
*
* @since 2.0
*/
interface MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Contract;
/**
* Class RequestHandlerInterface
*
* @since 2.0
*/
interface RequestHandlerInterface
{
/**
* Handles a request and produces a response.
*
* May call other collaborating code to generate the response.
*
* @param RequestInterface $request
*
* @return ResponseInterface
*/
public function handle(RequestInterface $request): ResponseInterface;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Contract;
use Swoft\Rpc\Server\Request;
use Swoole\Server;
/**
* Class RequestInterface
*
* @since 2.0
*/
interface RequestInterface
{
/**
* @return string
*/
public function getVersion(): string;
/**
* @return string
*/
public function getInterface(): string;
/**
* @return string
*/
public function getMethod(): string;
/**
* @return array
*/
public function getParams(): array;
/**
* @return array
*/
public function getParamsMap(): array;
/**
* @param array $params
*
* @return self
*/
public function withParams(array $params): self;
/**
* @return array
*/
public function getExt(): array;
/**
* @param string $key
* @param mixed|null $default
*
* @return mixed|null
*/
public function getExtKey(string $key, $default = null);
/**
* @param int $index
* @param mixed|null $default
*
* @return mixed|null
*/
public function getParam(int $index, $default = null);
/**
* @return string
*/
public function getData(): string;
/**
* @return Server
*/
public function getServer(): Server;
/**
* @return int
*/
public function getFd(): int;
/**
* @return int
*/
public function getReactorId(): int;
/**
* @return float
*/
public function getRequestTime(): float;
/**
* @param string $key
* @param mixed $value
*/
public function setAttribute(string $key, $value): void;
/**
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public function getAttribute(string $key, $default = null);
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Contract;
use Swoft\Rpc\Error;
use Swoole\Server;
/**
* Class ResponseInterface
*
* @since 2.0
*/
interface ResponseInterface
{
/**
* @param Error $error
*
* @return ResponseInterface
*/
public function setError(Error $error): ResponseInterface;
/**
* @param $data
*
* @return ResponseInterface
*/
public function setData($data): ResponseInterface;
/**
* @param string $content
*
* @return ResponseInterface
*/
public function setContent(string $content): ResponseInterface;
/**
* @return bool
*/
public function send(): bool;
/**
* @return Server
*/
public function getServer(): Server;
/**
* @return int
*/
public function getFd(): int;
/**
* @return int
*/
public function getReactorId(): int;
/**
* @return mixed
*/
public function getData();
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Contract;
/**
* Class RouterInterface
*
* @since 2.0
*/
interface RouterInterface extends \Swoft\Contract\RouterInterface
{
/**
* @param string $interface
* @param string $version
* @param string $className
*/
public function addRoute(string $interface, string $version, string $className): void;
/**
* @param string $version
* @param string $interface
*
* @return array
*/
public function match(string $version, string $interface): array;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Contract;
use Swoft\Error\Contract\ErrorHandlerInterface;
use Swoft\Rpc\Server\Response;
use Throwable;
/**
* Class RpcServerErrorHandlerInterface
*
* @since 2.0
*/
interface RpcServerErrorHandlerInterface extends ErrorHandlerInterface
{
/**
* @param Throwable $e
* @param Response $response
*
* @return Response
*/
public function handle(Throwable $e, Response $response): Response;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Exception\Handler;
use Swoft\Error\ErrorType;
use Swoft\Rpc\Server\Contract\RpcServerErrorHandlerInterface;
/**
* Class AbstractRpcServerErrorHandler
*
* @since 2.0
*/
abstract class AbstractRpcServerErrorHandler implements RpcServerErrorHandlerInterface
{
/**
* @return int
*/
public function getType(): int
{
return ErrorType::RPC;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Exception\Handler;
/**
* Class RpcErrorHandler
*
* @since 2.0
*/
abstract class RpcErrorHandler extends AbstractRpcServerErrorHandler
{
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Exception;
use Exception;
/**
* Class RpcServerException
*
* @since 2.0
*/
class RpcServerException extends Exception
{
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Listener;
use Swoft;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\SwoftEvent;
use Swoft\Rpc\Server\ServiceServerEvent;
/**
* Class AfterCloseListener
*
* @since 2.0
*
* @Listener(event=ServiceServerEvent::AFTER_CLOSE)
*/
class AfterCloseListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*
*/
public function handle(EventInterface $event): void
{
// Defer
Swoft::trigger(SwoftEvent::COROUTINE_DEFER);
// Destroy
Swoft::trigger(SwoftEvent::COROUTINE_COMPLETE);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Listener;
use Swoft;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Rpc\Server\ServiceServerEvent;
use Swoft\SwoftEvent;
/**
* Class AfterConnectListener
*
* @since 2.0
*
* @Listener(ServiceServerEvent::AFTER_CONNECT)
*/
class AfterConnectListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*
*/
public function handle(EventInterface $event): void
{
// Defer
Swoft::trigger(SwoftEvent::COROUTINE_DEFER);
// Destroy
Swoft::trigger(SwoftEvent::COROUTINE_COMPLETE);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Listener;
use ReflectionException;
use Swoft;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Rpc\Exception\RpcException;
use Swoft\Rpc\Server\Response;
use Swoft\SwoftEvent;
use Swoft\Rpc\Server\ServiceServerEvent;
/**
* Class AfterReceiveListener
*
* @since 2.0
*
* @Listener(event=ServiceServerEvent::AFTER_RECEIVE)
*/
class AfterReceiveListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*
* @throws RpcException
*/
public function handle(EventInterface $event): void
{
/* @var Response $response */
$response = $event->getParam(0);
$response->send();
// Defer
Swoft::trigger(SwoftEvent::COROUTINE_DEFER);
// Destroy
Swoft::trigger(SwoftEvent::COROUTINE_COMPLETE);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Listener;
use ReflectionException;
use Swoft\Bean\BeanFactory;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Rpc\Server\Middleware\MiddlewareRegister;
use Swoft\Rpc\Server\Router\Router;
use Swoft\Rpc\Server\Router\RouteRegister;
use Swoft\SwoftEvent;
/**
* Class AppInitCompleteListener
*
* @since 2.0
*
* @Listener(event=SwoftEvent::APP_INIT_COMPLETE)
*/
class AppInitCompleteListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*
* @throws \Swoft\Rpc\Server\Exception\RpcServerException
*/
public function handle(EventInterface $event): void
{
/* @var Router $router */
$router = BeanFactory::getBean('serviceRouter');
// Register router
RouteRegister::registerRoutes($router);
// Register middleware
MiddlewareRegister::register();
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Listener;
use ReflectionException;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Context\Context;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Log\Helper\Log;
use Swoft\Rpc\Server\ServiceCloseContext;
use Swoft\Server\SwooleEvent;
use Swoft\Rpc\Server\ServiceServerEvent;
/**
* Class BeforeCloseListener
*
* @since 2.0
*
* @Listener(event=ServiceServerEvent::BEFORE_CLOSE)
*/
class BeforeCloseListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*
*/
public function handle(EventInterface $event): void
{
list($server, $fd, $reactorId) = $event->getParams();
$context = ServiceCloseContext::new($server, $fd, $reactorId);
if (Log::getLogger()->isEnable()) {
$data = [
'event' => SwooleEvent::CLOSE,
'uri' => (string)$fd,
'requestTime' => microtime(true),
];
$context->setMulti($data);
}
Context::set($context);
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Listener;
use ReflectionException;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Context\Context;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Log\Helper\Log;
use Swoft\Rpc\Server\ServiceConnectContext;
use Swoft\Server\SwooleEvent;
use Swoft\Rpc\Server\ServiceServerEvent;
/**
* Class BeforeConnectListener
*
* @since 2.0
*
* @Listener(event=ServiceServerEvent::BEFORE_CONNECT)
*/
class BeforeConnectListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*
*/
public function handle(EventInterface $event): void
{
list($server, $fd, $reactorId) = $event->getParams();
$context = ServiceConnectContext::new($server, $fd, $reactorId);
if (Log::getLogger()->isEnable()) {
$data = [
'event' => SwooleEvent::CONNECT,
'uri' => (string)$fd,
'requestTime' => microtime(true),
];
$context->setMulti($data);
}
Context::set($context);
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Listener;
use ReflectionException;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Context\Context;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Log\Helper\Log;
use Swoft\Rpc\Server\Request;
use Swoft\Rpc\Server\Response;
use Swoft\Rpc\Server\ServiceContext;
use Swoft\Rpc\Server\ServiceServerEvent;
/**
* Class BeforeReceiveListener
*
* @since 2.0
*
* @Listener(event=ServiceServerEvent::BEFORE_RECEIVE)
*/
class BeforeReceiveListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*
*/
public function handle(EventInterface $event): void
{
/**
* @var Request $request
* @var Response $response
*/
[$request, $response] = $event->getParams();
$serviceContext = ServiceContext::new($request, $response);
if (Log::getLogger()->isEnable()) {
$uri = sprintf('%s::%s::%s', $request->getVersion(), $request->getInterface(), $request->getMethod());
$data = [
'traceid' => $request->getExtKey('traceid', ''),
'spanid' => $request->getExtKey('spanid', ''),
'parentid' => $request->getExtKey('parentid', ''),
'uri' => $uri,
'requestTime' => $request->getRequestTime(),
];
$serviceContext->setMulti($data);
}
Context::set($serviceContext);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Middleware;
use function context;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Code;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
use Swoft\Rpc\Server\Exception\RpcServerException;
use Swoft\Rpc\Server\Request;
use Swoft\Rpc\Server\Router\Router;
use Swoft\Stdlib\Helper\PhpHelper;
/**
* Class DefaultMiddleware
*
* @since 2.0
*
* @Bean()
*/
class DefaultMiddleware implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
* @throws RpcServerException
* @throws \Swoft\Exception\SwoftException
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
return $this->handler($request);
}
/**
* @param RequestInterface $request
*
* @return ResponseInterface
* @throws RpcServerException
* @throws \Swoft\Exception\SwoftException
*/
private function handler(RequestInterface $request): ResponseInterface
{
$version = $request->getVersion();
$interface = $request->getInterface();
$method = $request->getMethod();
$params = $request->getParams();
[$status, $className] = $request->getAttribute(Request::ROUTER_ATTRIBUTE);
if ($status != Router::FOUND) {
throw new RpcServerException(
sprintf('Route(%s-%s) is not founded!', $version, $interface),
Code::INVALID_REQUEST
);
}
$object = BeanFactory::getBean($className);
if (!$object instanceof $interface) {
throw new RpcServerException(
sprintf('Object is not instanceof %s', $interface),
Code::INVALID_REQUEST
);
}
if (!method_exists($object, $method)) {
throw new RpcServerException(
sprintf('Method(%s::%s) is not founded!', $interface, $method),
Code::METHOD_NOT_FOUND
);
}
$data = PhpHelper::call([$object, $method], ...$params);
$response = context()->getResponse();
return $response->setData($data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Middleware;
use function array_unique;
use Swoft\Rpc\Server\Exception\RpcServerException;
use Swoft\Rpc\Server\Router\RouteRegister;
/**
* Class MiddlewareRegister
*
* @since 2.0
*/
class MiddlewareRegister
{
/**
* All Middlewares within class and method
*
* @var array
*
* @example
* [
* 'className' => [
* 'class' => [
* 'middlewareName',
* 'middlewareName',
* 'middlewareName'
* ]
* 'methods' => [
* 'actionName' => [
* 'middlewareName',
* 'middlewareName',
* 'middlewareName'
* ]
* ]
* ]
* ]
*/
private static $middlewares = [];
/**
* @var array
*
* @example
* [
* 'className' => [
* 'methodName' => [
* 'middlewareName',
* 'middlewareName',
* ]
* ]
* ]
*/
private static $handlerMiddlewares = [];
/**
* Register class middleware
*
* @param string $name middleware name
* @param string $className class name
*
* @return void
*/
public static function registerByClassName(string $name, string $className): void
{
$middlewares = self::$middlewares[$className]['class'] ?? [];
$middlewares[] = $name;
self::$middlewares[$className]['class'] = array_unique($middlewares);
}
/**
* Register method middleware
*
* @param string $name
* @param string $className
* @param string $methodName
*
* @return void
*/
public static function registerByMethodName(string $name, string $className, string $methodName): void
{
$middlewares = self::$middlewares[$className]['methods'][$methodName] ?? [];
$middlewares[] = $name;
self::$middlewares[$className]['methods'][$methodName] = array_unique($middlewares);
}
/**
* Register handler middleware
*/
public static function register(): void
{
foreach (self::$middlewares as $className => $middlewares) {
// `@Service` is undefined on class
if (!RouteRegister::hasRouteByClassName($className)) {
throw new RpcServerException(sprintf('`@Service` is undefined on class(%s)', $className));
}
$classMiddlewares = $middlewares['class'] ?? [];
$methodMiddlewares = $middlewares['methods'] ?? [];
foreach ($methodMiddlewares as $methodName => $oneMethodMiddlewares) {
if (!empty($oneMethodMiddlewares)) {
$allMiddlewares = array_merge($classMiddlewares, $oneMethodMiddlewares);
self::$handlerMiddlewares[$className][$methodName] = array_unique($allMiddlewares);
}
}
}
}
/**
* @param string $className
* @param string $methodName
*
* @return array
*/
public static function getMiddlewares(string $className, string $methodName): array
{
$middlewares = self::$handlerMiddlewares[$className][$methodName] ?? [];
if (!empty($middlewares)) {
return $middlewares;
}
$middlewares = self::$middlewares[$className]['class'] ?? [];
return $middlewares;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Middleware;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
use Swoft\Rpc\Server\Exception\RpcServerException;
use Swoft\Rpc\Server\Request;
use Swoft\Rpc\Server\Router\Router;
use Swoft\Rpc\Server\ServiceHandler;
/**
* Class UserMiddleware
*
* @since 2.0
*
* @Bean()
*/
class UserMiddleware implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
* @throws RpcServerException
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
$version = $request->getVersion();
$interface = $request->getInterface();
$method = $request->getMethod();
/* @var Router $router */
$router = BeanFactory::getBean('serviceRouter');
$handler = $router->match($version, $interface);
$request->setAttribute(Request::ROUTER_ATTRIBUTE, $handler);
[$status, $className] = $handler;
if ($status != Router::FOUND) {
return $requestHandler->handle($request);
}
$middlewares = MiddlewareRegister::getMiddlewares($className, $method);
if (!empty($middlewares) && $requestHandler instanceof ServiceHandler) {
$requestHandler->insertMiddlewares($middlewares);
}
return $requestHandler->handle($request);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Middleware;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
use Swoft\Rpc\Server\Request;
use Swoft\Rpc\Server\Router\Router;
use Swoft\Validator\Exception\ValidatorException;
use Swoft\Validator\ValidateRegister;
use Swoft\Validator\Validator;
/**
* Class ValidatorMiddleware
*
* @Bean()
*
* @since 2.0
*/
class ValidatorMiddleware implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
* @throws ValidatorException
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
[$status, $className] = $request->getAttribute(Request::ROUTER_ATTRIBUTE);
if ($status != Router::FOUND) {
return $requestHandler->handle($request);
}
$method = $request->getMethod();
$paramsMap = $request->getParamsMap();
$validates = ValidateRegister::getValidates($className, $method);
if (empty($validates)) {
return $requestHandler->handle($request);
}
/* @var Validator $validator */
$validator = BeanFactory::getBean('validator');
[$paramsMap] = $validator->validateRequest($paramsMap, $validates);
$request = $request->withParams(array_values($paramsMap));
return $requestHandler->handle($request);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Bean\Concern\PrototypeTrait;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Exception\RpcException;
use Swoft\Rpc\Packet;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoole\Server;
/**
* Class Request
*
* @since 2.0
*
* @Bean(scope=Bean::PROTOTYPE)
*/
class Request implements RequestInterface
{
use PrototypeTrait;
/**
* Router handler attribute
*/
public const ROUTER_ATTRIBUTE = 'swoftRouterHandler';
/**
* @var string
*/
protected $version = '';
/**
* @var string
*/
protected $interface = '';
/**
* @var string
*/
protected $method = '';
/**
* @var array
*/
protected $params = [];
/**
* @var array
*/
protected $ext = [];
/**
* Raw data
*
* @var string
*/
protected $data = '';
/**
* @var Server
*/
protected $server;
/**
* @var int
*/
protected $fd = 0;
/**
* @var int
*/
protected $reactorId = 0;
/**
* @var float
*/
protected $requestTime = 0;
/**
* @var array
*
* @example
* [
* 'key' => value,
* 'key' => value,
* ]
*/
protected $attributes = [];
/**
* @param Server $server
* @param int $fd
* @param int $reactorId
* @param string $data
*
* @return Request
* @throws RpcException
*/
public static function new(
Server $server = null,
int $fd = null,
int $reactorId = null,
string $data = null
): self {
$instance = self::__instance();
/* @var Packet $packet */
$packet = \bean('rpcServerPacket');
$protocol = $packet->decode($data);
$instance->version = $protocol->getVersion();
$instance->interface = $protocol->getInterface();
$instance->method = $protocol->getMethod();
$instance->params = $protocol->getParams();
$instance->ext = $protocol->getExt();
$instance->data = $data;
$instance->server = $server;
$instance->reactorId = $reactorId;
$instance->fd = $fd;
$instance->requestTime = microtime(true);
return $instance;
}
/**
* @return array
* @throws ReflectionException
*/
public function getParamsMap(): array
{
$rc = BeanFactory::getReflection($this->interface);
$rxParams = $rc['methods'][$this->method]['params'];
$index = 0;
$paramsMap = [];
foreach ($rxParams as $methodParams) {
if (!isset($this->params[$index])) {
break;
}
[$name] = $methodParams;
$paramsMap[$name] = $this->params[$index];
}
return $paramsMap;
}
/**
* @param array $params
*
* @return RequestInterface
*/
public function withParams(array $params): RequestInterface
{
$clone = clone $this;
$clone->params = $params;
return $clone;
}
/**
* @return string
*/
public function getVersion(): string
{
return $this->version;
}
/**
* @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;
}
/**
* @return array
*/
public function getExt(): array
{
return $this->ext;
}
/**
* @param string $key
* @param mixed|null $default
*
* @return mixed|null
*/
public function getExtKey(string $key, $default = null)
{
return $this->ext[$key] ?? $default;
}
/**
* @param int $index
* @param mixed|null $default
*
* @return mixed|null
*/
public function getParam(int $index, $default = null)
{
return $this->params[$index] ?? $default;
}
/**
* @return string
*/
public function getData(): string
{
return $this->data;
}
/**
* @return Server
*/
public function getServer(): Server
{
return $this->server;
}
/**
* @return int
*/
public function getFd(): int
{
return $this->fd;
}
/**
* @return int
*/
public function getReactorId(): int
{
return $this->reactorId;
}
/**
* @return float
*/
public function getRequestTime(): float
{
return $this->requestTime;
}
/**
* @param string $key
* @param mixed $value
*/
public function setAttribute(string $key, $value): void
{
$this->attributes[$key] = $value;
}
/**
* @param string $key
* @param null $default
*
* @return mixed
*/
public function getAttribute(string $key, $default = null)
{
return $this->attributes[$key] ?? $default;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Concern\PrototypeTrait;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Error;
use Swoft\Rpc\Exception\RpcException;
use Swoft\Rpc\Packet;
use Swoft\Rpc\Server\Contract\ResponseInterface;
use Swoole\Server;
/**
* Class Response
*
* @since 2.0
*
* @Bean(scope=Bean::PROTOTYPE)
*/
class Response implements ResponseInterface
{
use PrototypeTrait;
/**
* @var Server
*/
protected $server;
/**
* @var int
*/
protected $fd = 0;
/**
* @var int
*/
protected $reactorId = 0;
/**
* @var string
*/
protected $content = '';
/**
* @var mixed
*/
protected $data;
/**
* @var Error
*/
protected $error;
/**
* @param Server $server
* @param int $fd
* @param int $reactorId
*
* @return Response
*/
public static function new(Server $server = null, int $fd = null, int $reactorId = null): self
{
$instance = self::__instance();
$instance->server = $server;
$instance->reactorId = $reactorId;
$instance->fd = $fd;
return $instance;
}
/**
* @param Error $error
*
* @return ResponseInterface
*/
public function setError(Error $error): ResponseInterface
{
$this->error = $error;
return $this;
}
/**
* @param $data
*
* @return ResponseInterface
*/
public function setData($data): ResponseInterface
{
$this->data = $data;
return $this;
}
/**
* @param string $content
*
* @return ResponseInterface
*/
public function setContent(string $content): ResponseInterface
{
$this->content = $content;
return $this;
}
/**
* @return bool
* @throws RpcException
*/
public function send(): bool
{
$this->prepare();
return $this->server->send($this->fd, $this->content);
}
/**
* @return Server
*/
public function getServer(): Server
{
return $this->server;
}
/**
* @return int
*/
public function getFd(): int
{
return $this->fd;
}
/**
* @return int
*/
public function getReactorId(): int
{
return $this->reactorId;
}
/**
* @return mixed
*/
public function getData()
{
return $this->data;
}
/**
* @throws RpcException
*/
protected function prepare(): void
{
/* @var Packet $packet */
$packet = \bean('rpcServerPacket');
if ($this->error === null) {
$this->content = $packet->encodeResponse($this->data);
return;
}
$code = $this->error->getCode();
$message = $this->error->getMessage();
$data = $this->error->getData();
$this->content = $packet->encodeResponse(null, $code, $message, $data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Router;
/**
* Class RouteRegister
*
* @since 2.0
*/
class RouteRegister
{
/**
* @var array
*
* @example
* [
* 'interface' => [
* 'version' => 'className'
* 'version2' => 'className2'
* ]
* ]
*/
private static $services = [];
/**
* @var array
*
* @example
* [
* 'className' => [
* 'interface',
* 'interface',
* ]
* ]
*/
private static $serviceClassNames = [];
/**
* @param string $interface
* @param string $version
* @param string $className
*/
public static function register(string $interface, string $version, string $className): void
{
self::$services[$interface][$version] = $className;
// Record classNames
self::$serviceClassNames[$className] = $interface;
}
/**
* @param string $className
*
* @return bool
*/
public static function hasRouteByClassName(string $className): bool
{
return isset(self::$serviceClassNames[$className]);
}
/**
* @param Router $router
*/
public static function registerRoutes(Router $router): void
{
foreach (self::$services as $interface => $service) {
foreach ($service as $version => $className) {
$router->addRoute($interface, $version, $className);
}
}
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Router;
use function sprintf;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Contract\RouterInterface;
/**
* Class Router
*
* @since 2.0
*
* @Bean("serviceRouter")
*/
class Router implements RouterInterface
{
/**
* @var array
*
* @example
* [
* 'interface@version' => $className
* ]
*/
private $routes = [];
/**
* @param string $interface
* @param string $version
* @param string $className
*/
public function addRoute(string $interface, string $version, string $className): void
{
$route = $this->getRoute($interface, $version);
$this->routes[$route] = $className;
}
/**
* @param string $version
* @param string $interface
*
* @return array
*/
public function match(string $version, string $interface): array
{
$route = $this->getRoute($interface, $version);
if (isset($this->routes[$route])) {
return [self::FOUND, $this->routes[$route]];
}
return [self::NOT_FOUND, ''];
}
/**
* @param string $interface
* @param string $version
*
* @return string
*/
private function getRoute(string $interface, string $version): string
{
return sprintf('%s@%s', $interface, $version);
}
/**
* @return array
*/
public function getRoutes(): array
{
return $this->routes;
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use ReflectionException;
use Swoft;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Error\ErrorManager;
use Swoft\Error\ErrorType;
use Swoft\Log\Debug;
use Swoft\Rpc\Error;
use Swoft\Rpc\Server\Contract\RpcServerErrorHandlerInterface;
use Throwable;
use Swoft\Bean\Annotation\Mapping\Bean;
/**
* Class RpcErrorDispatcher
*
* @since 2.0
*
* @Bean()
*/
class RpcErrorDispatcher
{
/**
* @param Throwable $e
* @param Response $response
*
* @return Response
*/
public function run(Throwable $e, Response $response): Response
{
/** @var ErrorManager $handlers */
$handlers = Swoft::getSingleton(ErrorManager::class);
/** @var RpcServerErrorHandlerInterface $handler */
if ($handler = $handlers->matchHandler($e, ErrorType::RPC)) {
return $handler->handle($e, $response);
}
Debug::log("Rpc Error(no handler, %s): %s\nAt File %s line %d\nTrace:\n%s",
get_class($e),
$e->getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString());
$error = Error::new($e->getCode(), $e->getMessage(), null);
$response->setError($error);
return $response;
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use Swoft\Bean\Annotation\Mapping\Bean;
/**
* Class ServiceCloseContext
*
* @since 2.0
*
* @Bean(scope=Bean::PROTOTYPE)
*/
class ServiceCloseContext extends ServiceConnectContext
{
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Concern\PrototypeTrait;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Context\AbstractContext;
use Swoole\Server;
/**
* Class ServiceConnectContext
*
* @since 2.0
*
* @Bean(scope=Bean::PROTOTYPE)
*/
class ServiceConnectContext extends AbstractContext
{
use PrototypeTrait;
/**
* @var Server
*/
protected $server;
/**
* @var int
*/
protected $fd;
/**
* @var int
*/
protected $reactorId;
/**
* @param Server $server
* @param int $fd
* @param int $reactorId
*
* @return ServiceConnectContext
*/
public static function new(Server $server, int $fd, int $reactorId): self
{
$instance = self::__instance();
$instance->server = $server;
$instance->fd = $fd;
$instance->reactorId = $reactorId;
return $instance;
}
/**
* @return Server
*/
public function getServer(): Server
{
return $this->server;
}
/**
* @return int
*/
public function getFd(): int
{
return $this->fd;
}
/**
* @return int
*/
public function getReactorId(): int
{
return $this->reactorId;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Concern\PrototypeTrait;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Context\AbstractContext;
/**
* Class ServiceContext
*
* @since 2.0
*
* @Bean(scope=Bean::PROTOTYPE)
*/
class ServiceContext extends AbstractContext
{
use PrototypeTrait;
/**
* @var Request
*/
protected $request;
/**
* @var Response
*/
protected $response;
/**
* @param Request $request
* @param Response $response
*
* @return ServiceContext
*/
public static function new(Request $request, Response $response): self
{
$instance = self::__instance();
$instance->request = $request;
$instance->response = $response;
return $instance;
}
/**
* @return Request
*/
public function getRequest(): Request
{
return $this->request;
}
/**
* @return Response
*/
public function getResponse(): Response
{
return $this->response;
}
/**
* Clear
*/
public function clear(): void
{
$this->data = [];
$this->request = $this->response = null;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use ReflectionException;
use Swoft;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Concern\AbstractDispatcher;
use Swoft\Rpc\Server\Middleware\DefaultMiddleware;
use Swoft\Rpc\Server\Middleware\UserMiddleware;
use Throwable;
/**
* Class ServiceDispatcher
*
* @since 2.0
*
* @Bean(name="serviceDispatcher")
*/
class ServiceDispatcher extends AbstractDispatcher
{
/**
* @var string
*/
protected $defaultMiddleware = DefaultMiddleware::class;
/**
* @param array $params
*
*/
public function dispatch(...$params)
{
/**
* @var Request $request
* @var Response $response
*/
[$request, $response] = $params;
try {
Swoft::trigger(ServiceServerEvent::BEFORE_RECEIVE, null, $request, $response);
$handler = ServiceHandler::new($this->requestMiddleware(), $this->defaultMiddleware);
$response = $handler->handle($request);
} catch (Throwable $e) {
/** @var RpcErrorDispatcher $errDispatcher */
$errDispatcher = BeanFactory::getSingleton(RpcErrorDispatcher::class);
// Handle request error
$response = $errDispatcher->run($e, $response);
}
Swoft::trigger(ServiceServerEvent::AFTER_RECEIVE, null, $response);
}
/**
* @return array
*/
public function preMiddleware(): array
{
return [];
}
/**
* @return array
*/
public function afterMiddleware(): array
{
return [
UserMiddleware::class
];
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use function array_splice;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Bean\Concern\PrototypeTrait;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
use Swoft\Rpc\Server\Exception\RpcServerException;
/**
* Class ServiceHandler
*
* @since 2.0
*
* @Bean(scope=Bean::PROTOTYPE)
*/
class ServiceHandler implements RequestHandlerInterface
{
use PrototypeTrait;
/**
* @var array
*/
protected $middlewares = [];
/**
* @var string
*/
protected $defaultMiddleware = '';
/**
* Current offset
*
* @var int
*/
protected $offset = 0;
/**
* @param array $middlewares
* @param string $defaultMiddleware
*
* @return self
*
*/
public static function new(array $middlewares, string $defaultMiddleware): self
{
$instance = self::__instance();
$instance->offset = 0;
$instance->middlewares = $middlewares;
$instance->defaultMiddleware = $defaultMiddleware;
return $instance;
}
/**
* @param RequestInterface $request
*
* @return ResponseInterface
*/
public function handle(RequestInterface $request): ResponseInterface
{
// Default middleware to handle request route
$middleware = $this->middlewares[$this->offset] ?? $this->defaultMiddleware;
/* @var MiddlewareInterface $bean */
$bean = BeanFactory::getBean($middleware);
// Next middleware
$this->offset++;
return $bean->process($request, $this);
}
/**
* Insert middleware at offset
*
* @param array $middlewares
* @param int|null $offset
*
* @throws RpcServerException
*/
public function insertMiddlewares(array $middlewares, int $offset = null): void
{
$offset = $offset ?? $this->offset;
if ($offset > $this->offset) {
throw new RpcServerException('Insert middleware offset must more than ' . $this->offset);
}
// Insert middlewares
array_splice($this->middlewares, $offset, 0, $middlewares);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Server\Exception\ServerException;
use Swoft\Server\Server;
use Swoft\Stdlib\Helper\Arr;
use Swoole\Server as SwooleServer;
/**
* Class ServiceServer
*
* @since 2.0
*
* @Bean(name="rpcServer")
*/
class ServiceServer extends Server
{
/**
* @var string
*/
protected static $serverType = 'RPC';
/**
* Default port
*
* @var int
*/
protected $port = 18307;
/**
* @var string
*/
protected $pidName = 'swoft-rpc';
/**
* @var string
*/
protected $commandFile = '@runtime/swoft-rpc.command';
/**
* @var string
*/
protected $pidFile = '@runtime/swoft-rpc.pid';
/**
* Start server
*
* @throws ServerException
*/
public function start(): void
{
$this->swooleServer = new SwooleServer($this->host, $this->port, $this->mode, $this->type);
$this->startSwoole();
}
/**
* @return array
*/
public function defaultSetting(): array
{
$setting = [
'open_eof_check' => true,
'package_eof' => "\r\n\r\n",
];
return Arr::merge(parent::defaultSetting(), $setting);
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server;
/**
* Class ServiceServerEvent
*
* @since 2.0
*/
class ServiceServerEvent
{
/**
* Before connect
*/
public const BEFORE_CONNECT = 'swoft.rpc.server.connect.before';
/**
* Connect
*/
public const CONNECT = 'swoft.rpc.server.connect';
/**
* After connect
*/
public const AFTER_CONNECT = 'swoft.rpc.server.connect.after';
/**
* Before close
*/
public const BEFORE_CLOSE = 'swoft.rpc.server.close.before';
/**
* Close
*/
public const CLOSE = 'swoft.rpc.server.close';
/**
* After close
*/
public const AFTER_CLOSE = 'swoft.rpc.server.close.after';
/**
* Before receive
*/
public const BEFORE_RECEIVE = 'swoft.rpc.server.receive.before';
/**
* After receive
*/
public const AFTER_RECEIVE = 'swoft.rpc.server.receive.after';
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Swoole;
use Swoft;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Server\ServiceServerEvent;
use Swoft\Server\Contract\CloseInterface;
use Swoole\Server;
/**
* Class CloseListener
*
* @since 2.0
*
* @Bean()
*/
class CloseListener implements CloseInterface
{
/**
* @param Server $server
* @param int $fd
* @param int $reactorId
*
*/
public function onClose(Server $server, int $fd, int $reactorId): void
{
// Before close
Swoft::trigger(ServiceServerEvent::BEFORE_CLOSE, null, $server, $fd, $reactorId);
// Close event
Swoft::trigger(ServiceServerEvent::CLOSE);
// After close
Swoft::trigger(ServiceServerEvent::AFTER_CLOSE);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Swoole;
use Swoft;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\ServiceServerEvent;
use Swoft\Server\Contract\ConnectInterface;
use Swoole\Server;
/**
* Class ConnectListener
*
* @since 2.0
*
* @Bean()
*/
class ConnectListener implements ConnectInterface
{
/**
* @param Server $server
* @param int $fd
* @param int $reactorId
*
*/
public function onConnect(Server $server, int $fd, int $reactorId): void
{
// Before connect
Swoft::trigger(ServiceServerEvent::BEFORE_CONNECT, null, $server, $fd, $reactorId);
// Connect event
Swoft::trigger(ServiceServerEvent::CONNECT);
// After connect
Swoft::trigger(ServiceServerEvent::AFTER_CONNECT);
}
}
<?php declare(strict_types=1);
namespace Swoft\Rpc\Server\Swoole;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Exception\RpcException;
use Swoft\Rpc\Server\Request;
use Swoft\Rpc\Server\Response;
use Swoft\Rpc\Server\ServiceDispatcher;
use Swoft\Server\Contract\ReceiveInterface;
use Swoole\Server;
/**
* Class ReceiveListener
*
* @since 2.0
*
* @Bean()
*/
class ReceiveListener implements ReceiveInterface
{
/**
* @param Server $server
* @param int $fd
* @param int $reactorId
* @param string $data
*
* @throws RpcException
*/
public function onReceive(Server $server, int $fd, int $reactorId, string $data): void
{
$request = Request::new($server, $fd, $reactorId, $data);
$response = Response::new($server, $fd, $reactorId);
/* @var ServiceDispatcher $dispatcher */
$dispatcher = BeanFactory::getSingleton('serviceDispatcher');
$dispatcher->dispatch($request, $response);
}
}
\ No newline at end of file
<?php
// vendor at component dir
use SwoftTest\Testing\TestApplication;
if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
require dirname(__DIR__) . '/vendor/autoload.php';
} elseif (file_exists(dirname(__DIR__, 3) . '/vendor/autoload.php')) {
/** @var \Composer\Autoload\ClassLoader $loader */
$loader = require dirname(__DIR__, 3) . '/vendor/autoload.php';
// need load testing psr4 config map
$componentDir = dirname(__DIR__, 3);
$componentJson = $componentDir . '/composer.json';
$composerData = json_decode(file_get_contents($componentJson), true);
foreach ($composerData['autoload-dev']['psr-4'] as $prefix => $dir) {
$loader->addPsr4($prefix, $componentDir . '/' . $dir);
}
// application's vendor
} elseif (file_exists(dirname(__DIR__, 5) . '/autoload.php')) {
/** @var \Composer\Autoload\ClassLoader $loader */
$loader = require dirname(__DIR__, 5) . '/autoload.php';
// need load testing psr4 config map
$componentDir = dirname(__DIR__, 3);
$componentJson = $componentDir . '/composer.json';
$composerData = json_decode(file_get_contents($componentJson), true);
foreach ($composerData['autoload-dev']['psr-4'] as $prefix => $dir) {
$loader->addPsr4($prefix, $componentDir . '/' . $dir);
}
} else {
exit('Please run "composer install" to install the dependencies' . PHP_EOL);
}
$application = new TestApplication();
$application->setBeanFile(__DIR__ . '/testing/bean.php');
$application->run();
<?php
return [
];
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing;
use Swoft\SwoftComponent;
/**
* Class AutoLoader
*
* @since 2.0
*/
class AutoLoader extends SwoftComponent
{
/**
* Get namespace and dirs
*
* @return array
*/
public function getPrefixDirs(): array
{
return [
__NAMESPACE__ => __DIR__,
];
}
/**
* @return array
*/
public function metadata(): array
{
return [];
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Concern;
use PHPUnit\Framework\Assert;
use function strpos;
use Swoft\Rpc\Response;
/**
* Trait RpcResponseAssertTrait
*
* @since 2.0
*/
trait RpcResponseAssertTrait
{
/**
* @var Response
*/
protected $returnResponse;
/**
* Assert
*/
public function assertSuccess(): void
{
Assert::assertTrue($this->returnResponse->getError() == null);
}
/**
* Assert
*/
public function assertFail(): void
{
Assert::assertTrue($this->returnResponse->getError() != null);
}
/**
* @param int $code
*/
public function assertErrorCode(int $code): void
{
$error = $this->returnResponse->getError();
if ($error === null) {
Assert::assertTrue(false);
return;
}
$errorCode = $error->getCode();
Assert::assertEquals($code, $errorCode);
}
/**
* @param string $message
*/
public function assertErrorMessage(string $message): void
{
$error = $this->returnResponse->getError();
if ($error === null) {
Assert::assertTrue(false);
return;
}
$errorMessage = $error->getMessage();
Assert::assertContains($message, $errorMessage);
}
/**
* @param string $message
*/
public function assertContainErrorMessage(string $message): void
{
$error = $this->returnResponse->getError();
if ($error === null) {
Assert::assertTrue(false);
return;
}
$errorMessage = $error->getMessage();
Assert::assertTrue(strpos($errorMessage, $message) !== false);
}
/**
* @param array $data
*/
public function assertEqualJsonResult(array $data): void
{
Assert::assertEquals($data, $this->returnResponse->getResult());
}
/**
* @param mixed $result
*/
public function assertEqualResult($result): void
{
Assert::assertEquals($result, $this->returnResponse->getResult());
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing;
use SwoftTest\Rpc\Server\Testing\Lib\DemoInterface;
use Swoft\Rpc\Server\Annotation\Mapping\Service;
use Swoft\Rpc\Server\Annotation\Mapping\Middlewares;
use Swoft\Rpc\Server\Annotation\Mapping\Middleware;
use SwoftTest\Rpc\Server\Testing\Middleware\ClassMd;
use SwoftTest\Rpc\Server\Testing\Middleware\ClassMd2;
use SwoftTest\Rpc\Server\Testing\Middleware\ClassMd3;
use SwoftTest\Rpc\Server\Testing\Middleware\MethodMd;
use SwoftTest\Rpc\Server\Testing\Middleware\MethodMd2;
use SwoftTest\Rpc\Server\Testing\Middleware\MethodMd3;
/**
* Class DemoMdService
*
* @since 2.0
*
* @Service(version="1.3")
*
* @Middlewares({
* @Middleware(ClassMd::class),
* @Middleware(ClassMd3::class)
* })
* @Middleware(ClassMd2::class,)
*/
class DemoMdService implements DemoInterface
{
/**
* @Middlewares({
* @Middleware(MethodMd::class),
* @Middleware(MethodMd3::class)
* })
*
* @Middleware(MethodMd2::class)
*
* @param int $uid
* @param string $type
*
* @return array
*/
public function getList(int $uid, string $type): array
{
return ['name' => 'list'];
}
/**
* @Middleware(MethodMd2::class)
*
* @param $uid
*
* @return array
*/
public function getInfo($uid)
{
return ['name' => 'info'];
}
/**
* @return array
*/
public function notClassMd(): array
{
return [
'name' => 'notClassMd'
];
}
public function returnNull(): void
{
return ;
}
public function delete(int $id): bool
{
return true;
}
public function error(): bool
{
return false;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing;
use Swoft\Rpc\Server\Annotation\Mapping\Service;
use SwoftTest\Rpc\Server\Testing\Lib\DemoInterface;
/**
* Class DemoService
*
* @since 2.0
*
* @Service()
*/
class DemoService implements DemoInterface
{
/**
* @param int $uid
* @param string $type
*
* @return array
*/
public function getList(int $uid, string $type): array
{
return [
'name' => 'list',
'list' => [
'id' => $uid,
'type' => $type,
'name' => 'name'
]
];
}
/**
* @param $uid
*
* @return array|mixed
*/
public function getInfo($uid)
{
return [
'name' => 'info',
'item' => [
'id' => $uid,
'name' => 'name'
]
];
}
/**
* @return array
*/
public function notClassMd(): array
{
return [];
}
/**
* @param int $id
*
* @return bool
*/
public function delete(int $id): bool
{
if ($id > 100) {
return true;
}
return false;
}
public function returnNull(): void
{
return ;
}
/**
* @return bool
* @throws \Exception
*/
public function error(): bool
{
throw new \Exception('error message', 324231);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing;
use Swoft\Rpc\Server\Annotation\Mapping\Service;
use SwoftTest\Rpc\Server\Testing\Lib\DemoInterface;
/**
* Class DemoServiceV2
*
* @since 2.0
*
* @Service(version="1.1")
*/
class DemoServiceV2 implements DemoInterface
{
/**
* @param int $uid
* @param string $type
*
* @return array
*/
public function getList(int $uid, string $type): array
{
return [
'name' => 'list',
'list' => [
'id' => $uid,
'type' => $type,
'name' => 'name'
],
'v'=> '1.1'
];
}
/**
* @param $uid
*
* @return array|mixed
*/
public function getInfo($uid)
{
return [
'name' => 'info',
'item' => [
'id' => $uid,
'name' => 'name'
],
'v'=> '1.1'
];
}
/**
* @return array
*/
public function notClassMd(): array
{
return [];
}
public function returnNull(): void
{
return ;
}
/**
* @param int $id
*
* @return bool
*/
public function delete(int $id): bool
{
if ($id > 100) {
return true;
}
return false;
}
/**
* @return bool
* @throws \Exception
*/
public function error(): bool
{
throw new \Exception('error message 1.1', 324231);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Lib;
/**
* Class DemoInterface
*
* @since 2.0
*/
interface DemoInterface
{
/**
* @param int $uid
* @param string $type
*
* @return array
*/
public function getList(int $uid, string $type): array;
/**
* @param $uid
*
* @return mixed
*/
public function getInfo($uid);
/**
* @return array
*/
public function notClassMd(): array;
/**
* @param int $id
*
* @return bool
*/
public function delete(int $id): bool;
/**
* @return bool
*/
public function error(): bool;
/**
* Return null
*/
public function returnNull(): void;
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Middleware;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
/**
* Class ClassMd
*
* @since 2.0
*
* @Bean()
*/
class ClassMd implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
$response = $requestHandler->handle($request);
$data = $response->getData();
$data['ClassMd'] = 'ClassMd';
return $response->setData($data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Middleware;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
/**
* Class ClassMd2
*
* @since 2.0
*
* @Bean()
*/
class ClassMd2 implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
$response = $requestHandler->handle($request);
$data = $response->getData();
$data['ClassMd2'] = 'ClassMd2';
return $response->setData($data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Middleware;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
/**
* Class ClassMd3
*
* @since 2.0
*
* @Bean()
*/
class ClassMd3 implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
$response = $requestHandler->handle($request);
$data = $response->getData();
$data['ClassMd3'] = 'ClassMd3';
return $response->setData($data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Middleware;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
/**
* Class MethodMd
*
* @since 2.0
*
* @Bean()
*/
class MethodMd implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
$response = $requestHandler->handle($request);
$data = $response->getData();
$data['MethodMd'] = 'MethodMd';
return $response->setData($data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Middleware;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
/**
* Class MethodMd2
*
* @since 2.0
*
* @Bean()
*/
class MethodMd2 implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
$response = $requestHandler->handle($request);
$data = $response->getData();
$data['MethodMd2'] = 'MethodMd2';
return $response->setData($data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Middleware;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
/**
* Class MethodMd3
*
* @since 2.0
*
* @Bean()
*/
class MethodMd3 implements MiddlewareInterface
{
/**
* @param RequestInterface $request
* @param RequestHandlerInterface $requestHandler
*
* @return ResponseInterface
*/
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
$response = $requestHandler->handle($request);
$data = $response->getData();
$data['MethodMd3'] = 'MethodMd3';
return $response->setData($data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing\Middleware;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Contract\MiddlewareInterface;
use Swoft\Rpc\Server\Contract\RequestHandlerInterface;
use Swoft\Rpc\Server\Contract\RequestInterface;
use Swoft\Rpc\Server\Contract\ResponseInterface;
/**
* Class UserMd
*
* @since 2.0
*
* @Bean()
*/
class UserMd implements MiddlewareInterface
{
public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface
{
if ($request->getVersion() != '1.3') {
return $requestHandler->handle($request);
}
$response = $requestHandler->handle($request);
$data = $response->getData();
$data['userMd'] = 'userMd';
return $response->setData($data);
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Rpc\Server\Request;
/**
* Class MockRequest
*
* @since 2.0
*
* @Bean(scope=Bean::PROTOTYPE)
*/
class MockRequest extends Request
{
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing;
use ReflectionException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Exception\RpcException;
use Swoft\Rpc\Packet;
use Swoft\Rpc\Server\Response;
use SwoftTest\Rpc\Server\Testing\Concern\RpcResponseAssertTrait;
/**
* Class MockResponse
*
* @since 2.0
*
* @Bean(scope=Bean::PROTOTYPE)
*/
class MockResponse extends Response
{
use RpcResponseAssertTrait;
/**
* @var \Swoft\Rpc\Response
*/
protected $returnResponse;
/**
* @return bool
* @throws RpcException
*/
public function send(): bool
{
/* @var Packet $packet */
$packet = \bean('rpcServerPacket');
$this->prepare();
$this->returnResponse = $packet->decodeResponse($this->content);
return true;
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Testing;
use ReflectionException;
use Swoft\Bean\BeanFactory;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Rpc\Exception\RpcException;
use Swoft\Rpc\Packet\JsonPacket;
use Swoft\Rpc\Protocol;
use Swoft\Rpc\Server\ServiceDispatcher;
/**
* Class MockRpcServer
*
* @since 2.0
*/
class MockRpcServer
{
/**
* @param string $interface
* @param string $method
* @param array $params
* @param array $ext
* @param string $v
*
* @return MockResponse
* @throws RpcException
*/
public function call(
string $interface,
string $method,
array $params = [],
array $ext = [],
string $v = Protocol::DEFAULT_VERSION
) {
/* @var JsonPacket $packet */
$packet = BeanFactory::getBean(JsonPacket::class);
$protocol = Protocol::new($v, $interface, $method, $params, $ext);
$data = $packet->encode($protocol);
$request = MockRequest::new(null, 1, 1, $data);
$response = MockResponse::new(null, 1, 1);
return $this->onReceive($request, $response);
}
/**
* @param MockRequest $request
* @param MockResponse $response
*
* @return MockResponse
*/
private function onReceive(MockRequest $request, MockResponse $response)
{
/* @var ServiceDispatcher $dispatcher */
$dispatcher = BeanFactory::getSingleton('serviceDispatcher');
$dispatcher->dispatch($request, $response);
return $response;
}
}
\ No newline at end of file
<?php
use Swoft\Rpc\Server\ServiceDispatcher;
use SwoftTest\Rpc\Server\Testing\Middleware\UserMd;
return [
'config' => [
'path' => __DIR__ . '/../config',
],
'serviceDispatcher' => [
'class' => ServiceDispatcher::class,
'middlewares' => [
UserMd::class
]
]
];
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Unit;
use SwoftTest\Rpc\Server\Testing\Lib\DemoInterface;
class RpcMdTest extends TestCase
{
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testAllMd()
{
$data = [
'name' => 'list',
'MethodMd2' => 'MethodMd2',
'MethodMd3' => 'MethodMd3',
'MethodMd' => 'MethodMd',
'ClassMd2' => 'ClassMd2',
'ClassMd3' => 'ClassMd3',
'ClassMd' => 'ClassMd',
'userMd' => 'userMd'
];
$response = $this->mockRpcServer->call(DemoInterface::class, 'getList', [12, 'type2'], [], '1.3');
$this->assertEquals($data, $response->getData());
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testOneMd()
{
$data = [
'name' => 'info',
'MethodMd2' => 'MethodMd2',
'ClassMd2' => 'ClassMd2',
'ClassMd3' => 'ClassMd3',
'ClassMd' => 'ClassMd',
'userMd' => 'userMd'
];
$response = $this->mockRpcServer->call(DemoInterface::class, 'getInfo', [12], [], '1.3');
$this->assertEquals($data, $response->getData());
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testNotClassMd()
{
$data = [
'name' => 'notClassMd',
'ClassMd2' => 'ClassMd2',
'ClassMd3' => 'ClassMd3',
'ClassMd' => 'ClassMd',
'userMd' => 'userMd'
];
$response = $this->mockRpcServer->call(DemoInterface::class, 'notClassMd', [12], [], '1.3');
$this->assertEquals($data, $response->getData());
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Unit;
use SwoftTest\Rpc\Server\Testing\Lib\DemoInterface;
/**
* Class RpcTest
*
* @since 2.0
*/
class RpcTest extends TestCase
{
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testGetList()
{
$list = [
'name' => 'list',
'list' => [
'id' => 12,
'type' => 'type2',
'name' => 'name'
]
];
$response = $this->mockRpcServer->call(DemoInterface::class, 'getList', [12, 'type2']);
$response->assertSuccess();
$response->assertEqualJsonResult($list);
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testGetInfo()
{
$info = [
'name' => 'info',
'item' => [
'id' => 12,
'name' => 'name'
]
];
$response = $this->mockRpcServer->call(DemoInterface::class, 'getInfo', [12]);
$response->assertSuccess();
$response->assertEqualJsonResult($info);
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testGetDelete()
{
$response = $this->mockRpcServer->call(DemoInterface::class, 'delete', [12]);
$response->assertSuccess();
$response->assertEqualResult(false);
$response = $this->mockRpcServer->call(DemoInterface::class, 'delete', [122]);
$response->assertSuccess();
$response->assertEqualResult(true);
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testCallErro()
{
$response = $this->mockRpcServer->call(DemoInterface::class, 'delete', []);
$response->assertFail();
$response->assertErrorCode(0);
$response->assertContainErrorMessage('Too few arguments to function');
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testReturnNull()
{
$response = $this->mockRpcServer->call(DemoInterface::class, 'returnNull', []);
$response->assertEqualResult(null);
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testException(){
$response = $this->mockRpcServer->call(DemoInterface::class, 'error', []);
$response->assertFail();
$response->assertErrorCode(324231);
$response->assertErrorMessage('error message');
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Unit;
use SwoftTest\Rpc\Server\Testing\Lib\DemoInterface;
class RpcV2Test extends TestCase
{
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testGetList()
{
$list = [
'name' => 'list',
'list' => [
'id' => 12,
'type' => 'type2',
'name' => 'name'
],
'v'=> '1.1'
];
$response = $this->mockRpcServer->call(DemoInterface::class, 'getList', [12, 'type2'], [], '1.1');
$response->assertSuccess();
$response->assertEqualJsonResult($list);
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testGetInfo()
{
$info = [
'name' => 'info',
'item' => [
'id' => 12,
'name' => 'name'
],
'v'=> '1.1'
];
$response = $this->mockRpcServer->call(DemoInterface::class, 'getInfo', [12], [], '1.1');
$response->assertSuccess();
$response->assertEqualJsonResult($info);
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testGetDelete()
{
$response = $this->mockRpcServer->call(DemoInterface::class, 'delete', [12], [], '1.1');
$response->assertSuccess();
$response->assertEqualResult(false);
$response = $this->mockRpcServer->call(DemoInterface::class, 'delete', [122], [], '1.1');
$response->assertSuccess();
$response->assertEqualResult(true);
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testCallErro()
{
$response = $this->mockRpcServer->call(DemoInterface::class, 'delete', [], [], '1.1');
$response->assertFail();
$response->assertErrorCode(0);
$response->assertContainErrorMessage('Too few arguments to function');
}
/**
* @throws \Swoft\Rpc\Exception\RpcException
*/
public function testException()
{
$response = $this->mockRpcServer->call(DemoInterface::class, 'error', [], [], '1.1');
$response->assertFail();
$response->assertErrorCode(324231);
$response->assertErrorMessage('error message 1.1');
}
}
\ No newline at end of file
<?php declare(strict_types=1);
namespace SwoftTest\Rpc\Server\Unit;
use SwoftTest\Rpc\Server\Testing\MockRpcServer;
/**
* Class TestCase
*
* @since 2.0
*/
abstract class TestCase extends \PHPUnit\Framework\TestCase
{
/**
* @var MockRpcServer
*/
protected $mockRpcServer;
/**
* Set up
*/
public function setUp()
{
$this->mockRpcServer = new MockRpcServer();
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment