mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-12-02 11:48:08 +08:00
Merge branch 'master' into cache
This commit is contained in:
commit
a1d3e79b56
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
- [#321](https://github.com/hyperf-cloud/hyperf/pull/321) Added custom object support for controller parameters in http-server.
|
- [#321](https://github.com/hyperf-cloud/hyperf/pull/321) Added custom object support for controller parameters in http-server.
|
||||||
- [#324](https://github.com/hyperf-cloud/hyperf/pull/324) Added NodeRequestIdGenerator, an implementation of `Hyperf\Contract\IdGeneratorInterface`
|
- [#324](https://github.com/hyperf-cloud/hyperf/pull/324) Added NodeRequestIdGenerator, an implementation of `Hyperf\Contract\IdGeneratorInterface`
|
||||||
|
- [#336](https://github.com/hyperf-cloud/hyperf/pull/336) Added Proxy RPC Client.
|
||||||
- [#346](https://github.com/hyperf-cloud/hyperf/pull/346) [#348](https://github.com/hyperf-cloud/hyperf/pull/348) Added filesystem driver for `hyperf/cache`.
|
- [#346](https://github.com/hyperf-cloud/hyperf/pull/346) [#348](https://github.com/hyperf-cloud/hyperf/pull/348) Added filesystem driver for `hyperf/cache`.
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
@ -19,6 +20,7 @@
|
|||||||
- [#333](https://github.com/hyperf-cloud/hyperf/pull/333) Fixed Function Redis::delete() is deprecated.
|
- [#333](https://github.com/hyperf-cloud/hyperf/pull/333) Fixed Function Redis::delete() is deprecated.
|
||||||
- [#334](https://github.com/hyperf-cloud/hyperf/pull/334) Fixed configuration of aliyun acm is not work expected.
|
- [#334](https://github.com/hyperf-cloud/hyperf/pull/334) Fixed configuration of aliyun acm is not work expected.
|
||||||
- [#337](https://github.com/hyperf-cloud/hyperf/pull/337) Fixed 500 response when key of header is not string.
|
- [#337](https://github.com/hyperf-cloud/hyperf/pull/337) Fixed 500 response when key of header is not string.
|
||||||
|
- [#338](https://github.com/hyperf-cloud/hyperf/pull/338) Fixed `ProviderConfig::load` will convert array when dependencies has the same key.
|
||||||
- [#340](https://github.com/hyperf-cloud/hyperf/pull/340) Fixed function `make` not support index-based array as parameters.
|
- [#340](https://github.com/hyperf-cloud/hyperf/pull/340) Fixed function `make` not support index-based array as parameters.
|
||||||
|
|
||||||
# v1.0.9 - 2019-08-03
|
# v1.0.9 - 2019-08-03
|
||||||
|
@ -169,6 +169,7 @@
|
|||||||
"HyperfTest\\ConfigAliyunAcm\\": "src/config-aliyun-acm/tests/",
|
"HyperfTest\\ConfigAliyunAcm\\": "src/config-aliyun-acm/tests/",
|
||||||
"HyperfTest\\ConfigApollo\\": "src/config-apollo/tests/",
|
"HyperfTest\\ConfigApollo\\": "src/config-apollo/tests/",
|
||||||
"HyperfTest\\ConfigEtcd\\": "src/config-etcd/tests/",
|
"HyperfTest\\ConfigEtcd\\": "src/config-etcd/tests/",
|
||||||
|
"HyperfTest\\Config\\": "src/config/tests/",
|
||||||
"HyperfTest\\Constants\\": "src/constants/tests/",
|
"HyperfTest\\Constants\\": "src/constants/tests/",
|
||||||
"HyperfTest\\Consul\\": "src/consul/tests/",
|
"HyperfTest\\Consul\\": "src/consul/tests/",
|
||||||
"HyperfTest\\Crontab\\": "src/crontab/tests/",
|
"HyperfTest\\Crontab\\": "src/crontab/tests/",
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<directory suffix="Test.php">./src/amqp/tests</directory>
|
<directory suffix="Test.php">./src/amqp/tests</directory>
|
||||||
<directory suffix="Test.php">./src/async-queue/tests</directory>
|
<directory suffix="Test.php">./src/async-queue/tests</directory>
|
||||||
<directory suffix="Test.php">./src/cache/tests</directory>
|
<directory suffix="Test.php">./src/cache/tests</directory>
|
||||||
|
<directory suffix="Test.php">./src/config/tests</directory>
|
||||||
<directory suffix="Test.php">./src/constants/tests</directory>
|
<directory suffix="Test.php">./src/constants/tests</directory>
|
||||||
<directory suffix="Test.php">./src/consul/tests</directory>
|
<directory suffix="Test.php">./src/consul/tests</directory>
|
||||||
<directory suffix="Test.php">./src/database/tests</directory>
|
<directory suffix="Test.php">./src/database/tests</directory>
|
||||||
|
1
src/config/.gitattributes
vendored
Normal file
1
src/config/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/tests export-ignore
|
@ -44,6 +44,7 @@
|
|||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
"HyperfTest\\Config\\": "tests/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -35,23 +35,37 @@ class ProviderConfig
|
|||||||
public static function load(): array
|
public static function load(): array
|
||||||
{
|
{
|
||||||
if (! static::$privoderConfigs) {
|
if (! static::$privoderConfigs) {
|
||||||
$config = [];
|
$providers = Composer::getMergedExtra('hyperf')['config'] ?? [];
|
||||||
$providers = Composer::getMergedExtra('hyperf')['config'];
|
static::$privoderConfigs = static::loadProviders($providers);
|
||||||
foreach ($providers ?? [] as $provider) {
|
|
||||||
if (is_string($provider) && class_exists($provider) && method_exists($provider, '__invoke')) {
|
|
||||||
$providerConfig = (new $provider())();
|
|
||||||
$config = array_merge_recursive($config, $providerConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static::$privoderConfigs = $config;
|
|
||||||
unset($config, $providerConfig);
|
|
||||||
}
|
}
|
||||||
return static::$privoderConfigs;
|
return static::$privoderConfigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clear(): void
|
public static function clear(): void
|
||||||
{
|
{
|
||||||
static::$privoderConfigs = [];
|
static::$privoderConfigs = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static function loadProviders(array $providers): array
|
||||||
|
{
|
||||||
|
$providerConfigs = [];
|
||||||
|
foreach ($providers ?? [] as $provider) {
|
||||||
|
if (is_string($provider) && class_exists($provider) && method_exists($provider, '__invoke')) {
|
||||||
|
$providerConfigs[] = (new $provider())();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::merge(...$providerConfigs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function merge(...$arrays): array
|
||||||
|
{
|
||||||
|
$result = array_merge_recursive(...$arrays);
|
||||||
|
if (isset($result['dependencies'])) {
|
||||||
|
$dependencies = array_column($arrays, 'dependencies');
|
||||||
|
$result['dependencies'] = array_merge(...$dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
124
src/config/tests/ProviderConfigTest.php
Normal file
124
src/config/tests/ProviderConfigTest.php
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace HyperfTest\Config;
|
||||||
|
|
||||||
|
use Hyperf\Utils\Arr;
|
||||||
|
use HyperfTest\Config\Stub\FooConfigProvider;
|
||||||
|
use HyperfTest\Config\Stub\ProviderConfig;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class ProviderConfigTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testProviderConfigMerge()
|
||||||
|
{
|
||||||
|
$c1 = [
|
||||||
|
'listeners' => ['L1'],
|
||||||
|
'dependencies' => [
|
||||||
|
'D1' => 'D1',
|
||||||
|
'D2' => 'D2',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$c2 = [
|
||||||
|
'listeners' => ['L2'],
|
||||||
|
'dependencies' => [
|
||||||
|
'D1' => 'D1',
|
||||||
|
'D2' => 'D3',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$c3 = [
|
||||||
|
'listeners' => ['L2'],
|
||||||
|
'dependencies' => [
|
||||||
|
'D1' => 'D1',
|
||||||
|
'D3' => 'D3',
|
||||||
|
'D4' => 'D4',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ProviderConfig::merge($c1, $c2, $c3);
|
||||||
|
|
||||||
|
$this->assertSame(['D1' => 'D1', 'D2' => 'D3', 'D3' => 'D3', 'D4' => 'D4'], $result['dependencies']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProviderConfigNotHaveDependencies()
|
||||||
|
{
|
||||||
|
$c1 = [
|
||||||
|
'listeners' => ['L1'],
|
||||||
|
'dependencies' => [
|
||||||
|
'D1' => 'D1',
|
||||||
|
'D2' => 'D2',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$c2 = [
|
||||||
|
'listeners' => ['L2'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ProviderConfig::merge($c1, $c2);
|
||||||
|
$this->assertSame(['D1' => 'D1', 'D2' => 'D2'], $result['dependencies']);
|
||||||
|
$this->assertSame(['L1', 'L2'], $result['listeners']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProviderConfigHaveNull()
|
||||||
|
{
|
||||||
|
$c1 = [
|
||||||
|
'listeners' => ['L1'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$c2 = [
|
||||||
|
'listeners' => [value(function () {
|
||||||
|
return null;
|
||||||
|
})],
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = ProviderConfig::merge($c1, $c2);
|
||||||
|
$this->assertSame(['L1', null], $result['listeners']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProviderConfigLoadProviders()
|
||||||
|
{
|
||||||
|
$config = json_decode(file_get_contents(BASE_PATH . '/composer.json'), true);
|
||||||
|
|
||||||
|
$providers = $config['extra']['hyperf']['config'];
|
||||||
|
|
||||||
|
$res = ProviderConfig::loadProviders($providers);
|
||||||
|
|
||||||
|
$dependencies = $res['dependencies'];
|
||||||
|
$commands = $res['commands'];
|
||||||
|
$scanPaths = $res['scan']['paths'];
|
||||||
|
$publish = $res['publish'];
|
||||||
|
$listeners = $res['listeners'];
|
||||||
|
|
||||||
|
$this->assertFalse(Arr::isAssoc($commands));
|
||||||
|
$this->assertFalse(Arr::isAssoc($scanPaths));
|
||||||
|
$this->assertFalse(Arr::isAssoc($listeners));
|
||||||
|
$this->assertFalse(Arr::isAssoc($publish));
|
||||||
|
$this->assertTrue(Arr::isAssoc($dependencies));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProviderConfigLoadProvidersHasCallable()
|
||||||
|
{
|
||||||
|
$res = ProviderConfig::loadProviders([
|
||||||
|
FooConfigProvider::class,
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($res['dependencies'] as $dependency) {
|
||||||
|
$this->assertTrue(is_string($dependency) || is_callable($dependency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/config/tests/Stub/Foo.php
Normal file
28
src/config/tests/Stub/Foo.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace HyperfTest\Config\Stub;
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
public function __construct($id = 0)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function make()
|
||||||
|
{
|
||||||
|
return new self(2);
|
||||||
|
}
|
||||||
|
}
|
29
src/config/tests/Stub/FooConfigProvider.php
Normal file
29
src/config/tests/Stub/FooConfigProvider.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace HyperfTest\Config\Stub;
|
||||||
|
|
||||||
|
class FooConfigProvider
|
||||||
|
{
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'dependencies' => [
|
||||||
|
'Foo' => function () {
|
||||||
|
return new Foo(1);
|
||||||
|
},
|
||||||
|
'Foo2' => [Foo::class, 'make'],
|
||||||
|
'Foo3' => Foo::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
26
src/config/tests/Stub/ProviderConfig.php
Normal file
26
src/config/tests/Stub/ProviderConfig.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace HyperfTest\Config\Stub;
|
||||||
|
|
||||||
|
class ProviderConfig extends \Hyperf\Config\ProviderConfig
|
||||||
|
{
|
||||||
|
public static function loadProviders(array $providers): array
|
||||||
|
{
|
||||||
|
return parent::loadProviders($providers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function merge(...$arrays): array
|
||||||
|
{
|
||||||
|
return parent::merge(...$arrays);
|
||||||
|
}
|
||||||
|
}
|
@ -49,32 +49,7 @@ class InitProxyCommand extends Command
|
|||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$scanDirs = $this->getScanDir();
|
$this->createAopProxies();
|
||||||
|
|
||||||
$runtime = BASE_PATH . '/runtime/container/proxy/';
|
|
||||||
if (is_dir($runtime)) {
|
|
||||||
$this->clearRuntime($runtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
$classCollection = $this->scanner->scan($scanDirs);
|
|
||||||
|
|
||||||
foreach ($classCollection as $item) {
|
|
||||||
try {
|
|
||||||
$this->container->get($item);
|
|
||||||
} catch (\Throwable $ex) {
|
|
||||||
// Entry cannot be resoleved.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->container instanceof Container) {
|
|
||||||
foreach ($this->container->getDefinitionSource()->getDefinitions() as $key => $definition) {
|
|
||||||
try {
|
|
||||||
$this->container->get($key);
|
|
||||||
} catch (\Throwable $ex) {
|
|
||||||
// Entry cannot be resoleved.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->output->writeln('<info>Proxy class create success.</info>');
|
$this->output->writeln('<info>Proxy class create success.</info>');
|
||||||
}
|
}
|
||||||
@ -111,4 +86,34 @@ class InitProxyCommand extends Command
|
|||||||
|
|
||||||
return $scanDirs;
|
return $scanDirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function createAopProxies()
|
||||||
|
{
|
||||||
|
$scanDirs = $this->getScanDir();
|
||||||
|
|
||||||
|
$runtime = BASE_PATH . '/runtime/container/proxy/';
|
||||||
|
if (is_dir($runtime)) {
|
||||||
|
$this->clearRuntime($runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
$classCollection = $this->scanner->scan($scanDirs);
|
||||||
|
|
||||||
|
foreach ($classCollection as $item) {
|
||||||
|
try {
|
||||||
|
$this->container->get($item);
|
||||||
|
} catch (\Throwable $ex) {
|
||||||
|
// Entry cannot be resoleved.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->container instanceof Container) {
|
||||||
|
foreach ($this->container->getDefinitionSource()->getDefinitions() as $key => $definition) {
|
||||||
|
try {
|
||||||
|
$this->container->get($key);
|
||||||
|
} catch (\Throwable $ex) {
|
||||||
|
// Entry cannot be resoleved.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,9 +78,9 @@ class JsonRpcHttpTransporter implements TransporterInterface
|
|||||||
]);
|
]);
|
||||||
if ($response->getStatusCode() === 200) {
|
if ($response->getStatusCode() === 200) {
|
||||||
return $response->getBody()->getContents();
|
return $response->getBody()->getContents();
|
||||||
} else {
|
|
||||||
$this->loadBalancer->removeNode($node);
|
|
||||||
}
|
}
|
||||||
|
$this->loadBalancer->removeNode($node);
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
149
src/json-rpc/tests/RpcServiceClientTest.php
Normal file
149
src/json-rpc/tests/RpcServiceClientTest.php
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace HyperfTest\JsonRpc;
|
||||||
|
|
||||||
|
use Hyperf\Config\Config;
|
||||||
|
use Hyperf\Contract\ConfigInterface;
|
||||||
|
use Hyperf\Contract\NormalizerInterface;
|
||||||
|
use Hyperf\Contract\StdoutLoggerInterface;
|
||||||
|
use Hyperf\Di\Annotation\Scanner;
|
||||||
|
use Hyperf\Di\Container;
|
||||||
|
use Hyperf\Di\Definition\DefinitionSource;
|
||||||
|
use Hyperf\Di\MethodDefinitionCollector;
|
||||||
|
use Hyperf\Di\MethodDefinitionCollectorInterface;
|
||||||
|
use Hyperf\JsonRpc\DataFormatter;
|
||||||
|
use Hyperf\JsonRpc\JsonRpcTransporter;
|
||||||
|
use Hyperf\JsonRpc\NormalizeDataFormatter;
|
||||||
|
use Hyperf\JsonRpc\PathGenerator;
|
||||||
|
use Hyperf\Logger\Logger;
|
||||||
|
use Hyperf\RpcClient\ProxyFactory;
|
||||||
|
use Hyperf\Utils\ApplicationContext;
|
||||||
|
use Hyperf\Utils\Packer\JsonPacker;
|
||||||
|
use Hyperf\Utils\Serializer\SerializerFactory;
|
||||||
|
use Hyperf\Utils\Serializer\SymfonyNormalizer;
|
||||||
|
use HyperfTest\JsonRpc\Stub\CalculatorProxyServiceClient;
|
||||||
|
use HyperfTest\JsonRpc\Stub\CalculatorServiceInterface;
|
||||||
|
use HyperfTest\JsonRpc\Stub\IntegerValue;
|
||||||
|
use Mockery\MockInterface;
|
||||||
|
use Monolog\Handler\StreamHandler;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Serializer\Serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RpcServiceClientTest extends TestCase
|
||||||
|
{
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testServiceClient()
|
||||||
|
{
|
||||||
|
$container = $this->createContainer();
|
||||||
|
|
||||||
|
/** @var MockInterface $transporter */
|
||||||
|
$transporter = $container->get(JsonRpcTransporter::class);
|
||||||
|
$transporter->shouldReceive('setLoadBalancer')
|
||||||
|
->andReturnSelf();
|
||||||
|
$transporter->shouldReceive('send')
|
||||||
|
->andReturn(json_encode([
|
||||||
|
'result' => 3,
|
||||||
|
]));
|
||||||
|
$service = new CalculatorProxyServiceClient($container, CalculatorServiceInterface::class, 'jsonrpc');
|
||||||
|
$ret = $service->add(1, 2);
|
||||||
|
$this->assertEquals(3, $ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProxyFactory()
|
||||||
|
{
|
||||||
|
$container = $this->createContainer();
|
||||||
|
/** @var MockInterface $transporter */
|
||||||
|
$transporter = $container->get(JsonRpcTransporter::class);
|
||||||
|
$transporter->shouldReceive('setLoadBalancer')
|
||||||
|
->andReturnSelf();
|
||||||
|
$transporter->shouldReceive('send')
|
||||||
|
->andReturn(json_encode([
|
||||||
|
'result' => 3,
|
||||||
|
]));
|
||||||
|
$factory = new ProxyFactory();
|
||||||
|
$proxyClass = $factory->createProxy(CalculatorServiceInterface::class);
|
||||||
|
/** @var CalculatorServiceInterface $service */
|
||||||
|
$service = new $proxyClass($container, CalculatorServiceInterface::class, 'jsonrpc');
|
||||||
|
$ret = $service->add(1, 2);
|
||||||
|
$this->assertEquals(3, $ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProxyFactoryObjectParameter()
|
||||||
|
{
|
||||||
|
$container = $this->createContainer();
|
||||||
|
/** @var MockInterface $transporter */
|
||||||
|
$transporter = $container->get(JsonRpcTransporter::class);
|
||||||
|
$transporter->shouldReceive('setLoadBalancer')
|
||||||
|
->andReturnSelf();
|
||||||
|
$transporter->shouldReceive('send')
|
||||||
|
->andReturn(json_encode([
|
||||||
|
'result' => ['value' => 3],
|
||||||
|
]));
|
||||||
|
$factory = new ProxyFactory();
|
||||||
|
$proxyClass = $factory->createProxy(CalculatorServiceInterface::class);
|
||||||
|
/** @var CalculatorServiceInterface $service */
|
||||||
|
$service = new $proxyClass($container, CalculatorServiceInterface::class, 'jsonrpc');
|
||||||
|
$ret = $service->sum(IntegerValue::newInstance(1), IntegerValue::newInstance(2));
|
||||||
|
$this->assertInstanceOf(IntegerValue::class, $ret);
|
||||||
|
$this->assertEquals(3, $ret->getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createContainer()
|
||||||
|
{
|
||||||
|
$transporter = \Mockery::mock(JsonRpcTransporter::class);
|
||||||
|
$container = new Container(new DefinitionSource([
|
||||||
|
NormalizerInterface::class => SymfonyNormalizer::class,
|
||||||
|
Serializer::class => SerializerFactory::class,
|
||||||
|
DataFormatter::class => NormalizeDataFormatter::class,
|
||||||
|
MethodDefinitionCollectorInterface::class => MethodDefinitionCollector::class,
|
||||||
|
StdoutLoggerInterface::class => function () {
|
||||||
|
return new Logger('App', [new StreamHandler('php://stderr')]);
|
||||||
|
},
|
||||||
|
ConfigInterface::class => function () {
|
||||||
|
return new Config([
|
||||||
|
'services' => [
|
||||||
|
'consumers' => [
|
||||||
|
[
|
||||||
|
'name' => CalculatorServiceInterface::class,
|
||||||
|
'nodes' => [
|
||||||
|
['host' => '0.0.0.0', 'port' => 1234],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'protocols' => [
|
||||||
|
'jsonrpc' => [
|
||||||
|
'packer' => JsonPacker::class,
|
||||||
|
'transporter' => JsonRpcTransporter::class,
|
||||||
|
'path-generator' => PathGenerator::class,
|
||||||
|
'data-formatter' => DataFormatter::class,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
JsonRpcTransporter::class => function () use ($transporter) {
|
||||||
|
return $transporter;
|
||||||
|
},
|
||||||
|
], [], new Scanner()));
|
||||||
|
ApplicationContext::setContainer($container);
|
||||||
|
return $container;
|
||||||
|
}
|
||||||
|
}
|
33
src/json-rpc/tests/Stub/CalculatorProxyServiceClient.php
Normal file
33
src/json-rpc/tests/Stub/CalculatorProxyServiceClient.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace HyperfTest\JsonRpc\Stub;
|
||||||
|
|
||||||
|
use Hyperf\RpcClient\Proxy\AbstractProxyService;
|
||||||
|
|
||||||
|
class CalculatorProxyServiceClient extends AbstractProxyService implements CalculatorServiceInterface
|
||||||
|
{
|
||||||
|
public function add(int $a, int $b)
|
||||||
|
{
|
||||||
|
return $this->client->__call(__FUNCTION__, func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sum(IntegerValue $a, IntegerValue $b): IntegerValue
|
||||||
|
{
|
||||||
|
return $this->client->__call(__FUNCTION__, func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divide($value, $divider)
|
||||||
|
{
|
||||||
|
return $this->client->__call(__FUNCTION__, func_get_args());
|
||||||
|
}
|
||||||
|
}
|
@ -17,14 +17,13 @@ use Hyperf\Consul\Health;
|
|||||||
use Hyperf\Consul\HealthInterface;
|
use Hyperf\Consul\HealthInterface;
|
||||||
use Hyperf\Contract\ConfigInterface;
|
use Hyperf\Contract\ConfigInterface;
|
||||||
use Hyperf\Contract\IdGeneratorInterface;
|
use Hyperf\Contract\IdGeneratorInterface;
|
||||||
use Hyperf\Contract\PackerInterface;
|
|
||||||
use Hyperf\Guzzle\ClientFactory;
|
use Hyperf\Guzzle\ClientFactory;
|
||||||
use Hyperf\LoadBalancer\LoadBalancerInterface;
|
use Hyperf\LoadBalancer\LoadBalancerInterface;
|
||||||
use Hyperf\LoadBalancer\LoadBalancerManager;
|
use Hyperf\LoadBalancer\LoadBalancerManager;
|
||||||
use Hyperf\LoadBalancer\Node;
|
use Hyperf\LoadBalancer\Node;
|
||||||
use Hyperf\Rpc\Contract\DataFormatterInterface;
|
use Hyperf\Rpc\Contract\DataFormatterInterface;
|
||||||
use Hyperf\Rpc\Contract\PathGeneratorInterface;
|
use Hyperf\Rpc\Contract\PathGeneratorInterface;
|
||||||
use Hyperf\Rpc\Contract\TransporterInterface;
|
use Hyperf\Rpc\Protocol;
|
||||||
use Hyperf\Rpc\ProtocolManager;
|
use Hyperf\Rpc\ProtocolManager;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
@ -61,7 +60,7 @@ abstract class AbstractServiceClient
|
|||||||
protected $client;
|
protected $client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ContainerInterfaces
|
* @var ContainerInterface
|
||||||
*/
|
*/
|
||||||
protected $container;
|
protected $container;
|
||||||
|
|
||||||
@ -71,9 +70,9 @@ abstract class AbstractServiceClient
|
|||||||
protected $loadBalancerManager;
|
protected $loadBalancerManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Hyperf\Rpc\ProtocolManager
|
* @var null|\Hyperf\Contract\IdGeneratorInterface
|
||||||
*/
|
*/
|
||||||
protected $protocolManager;
|
protected $idGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var PathGeneratorInterface
|
* @var PathGeneratorInterface
|
||||||
@ -85,31 +84,21 @@ abstract class AbstractServiceClient
|
|||||||
*/
|
*/
|
||||||
protected $dataFormatter;
|
protected $dataFormatter;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Hyperf\Contract\ConfigInterface
|
|
||||||
*/
|
|
||||||
protected $config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var null|\Hyperf\Contract\IdGeneratorInterface
|
|
||||||
*/
|
|
||||||
protected $idGenerator;
|
|
||||||
|
|
||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
$this->container = $container;
|
$this->container = $container;
|
||||||
$this->loadBalancerManager = $container->get(LoadBalancerManager::class);
|
$this->loadBalancerManager = $container->get(LoadBalancerManager::class);
|
||||||
$this->protocolManager = $container->get(ProtocolManager::class);
|
$protocol = new Protocol($container, $container->get(ProtocolManager::class), $this->protocol);
|
||||||
$this->pathGenerator = $this->createPathGenerator();
|
|
||||||
$this->dataFormatter = $this->createDataFormatter();
|
|
||||||
$loadBalancer = $this->createLoadBalancer(...$this->createNodes());
|
$loadBalancer = $this->createLoadBalancer(...$this->createNodes());
|
||||||
$transporter = $this->createTransporter()->setLoadBalancer($loadBalancer);
|
$transporter = $protocol->getTransporter()->setLoadBalancer($loadBalancer);
|
||||||
$this->client = make(Client::class)
|
$this->client = make(Client::class)
|
||||||
->setPacker($this->createPacker())
|
->setPacker($protocol->getPacker())
|
||||||
->setTransporter($transporter);
|
->setTransporter($transporter);
|
||||||
if ($container->has(IdGeneratorInterface::class)) {
|
if ($container->has(IdGeneratorInterface::class)) {
|
||||||
$this->idGenerator = $container->get(IdGeneratorInterface::class);
|
$this->idGenerator = $container->get(IdGeneratorInterface::class);
|
||||||
}
|
}
|
||||||
|
$this->pathGenerator = $protocol->getPathGenerator();
|
||||||
|
$this->dataFormatter = $protocol->getDataFormatter();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function __request(string $method, array $params, ?string $id = null)
|
protected function __request(string $method, array $params, ?string $id = null)
|
||||||
@ -149,46 +138,6 @@ abstract class AbstractServiceClient
|
|||||||
return $loadBalancer;
|
return $loadBalancer;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createTransporter(): TransporterInterface
|
|
||||||
{
|
|
||||||
$transporter = $this->protocolManager->getTransporter($this->protocol);
|
|
||||||
if (! class_exists($transporter)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Transporter %s is not exists.', $transporter));
|
|
||||||
}
|
|
||||||
/* @var TransporterInterface $instance */
|
|
||||||
return make($transporter);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createPacker(): PackerInterface
|
|
||||||
{
|
|
||||||
$packer = $this->protocolManager->getPacker($this->protocol);
|
|
||||||
if (! class_exists($packer)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Packer %s is not exists.', $packer));
|
|
||||||
}
|
|
||||||
/* @var PackerInterface $packer */
|
|
||||||
return $this->container->get($packer);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createPathGenerator(): PathGeneratorInterface
|
|
||||||
{
|
|
||||||
$pathGenerator = $this->protocolManager->getPathGenerator($this->protocol);
|
|
||||||
if (! class_exists($pathGenerator)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Path Generator %s is not exists.', $pathGenerator));
|
|
||||||
}
|
|
||||||
/* @var PathGeneratorInterface $pathGenerator */
|
|
||||||
return $this->container->get($pathGenerator);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createDataFormatter(): DataFormatterInterface
|
|
||||||
{
|
|
||||||
$dataFormatter = $this->protocolManager->getDataFormatter($this->protocol);
|
|
||||||
if (! class_exists($dataFormatter)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Data Formatter %s is not exists.', $dataFormatter));
|
|
||||||
}
|
|
||||||
/* @var DataFormatterInterface $dataFormatter */
|
|
||||||
return $this->container->get($dataFormatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create nodes the first time.
|
* Create nodes the first time.
|
||||||
*
|
*
|
||||||
|
@ -12,6 +12,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Hyperf\RpcClient;
|
namespace Hyperf\RpcClient;
|
||||||
|
|
||||||
|
use Hyperf\RpcClient\Listener\AddConsumerDefinitionListener;
|
||||||
|
|
||||||
class ConfigProvider
|
class ConfigProvider
|
||||||
{
|
{
|
||||||
public function __invoke(): array
|
public function __invoke(): array
|
||||||
@ -21,6 +23,9 @@ class ConfigProvider
|
|||||||
],
|
],
|
||||||
'commands' => [
|
'commands' => [
|
||||||
],
|
],
|
||||||
|
'listeners' => [
|
||||||
|
AddConsumerDefinitionListener::class,
|
||||||
|
],
|
||||||
'scan' => [
|
'scan' => [
|
||||||
'paths' => [
|
'paths' => [
|
||||||
__DIR__,
|
__DIR__,
|
||||||
|
17
src/rpc-client/src/Exception/RequestException.php
Normal file
17
src/rpc-client/src/Exception/RequestException.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Hyperf\RpcClient\Exception;
|
||||||
|
|
||||||
|
class RequestException extends \RuntimeException
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Hyperf\RpcClient\Listener;
|
||||||
|
|
||||||
|
use Hyperf\Contract\ConfigInterface;
|
||||||
|
use Hyperf\Di\Container;
|
||||||
|
use Hyperf\Event\Contract\ListenerInterface;
|
||||||
|
use Hyperf\Framework\Event\BootApplication;
|
||||||
|
use Hyperf\RpcClient\ProxyFactory;
|
||||||
|
use Hyperf\Utils\Arr;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class AddConsumerDefinitionListener implements ListenerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listen(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
BootApplication::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatic create proxy service definitions from services.consumers.
|
||||||
|
*
|
||||||
|
* @param BootApplication $event
|
||||||
|
*/
|
||||||
|
public function process(object $event)
|
||||||
|
{
|
||||||
|
/** @var Container $container */
|
||||||
|
$container = $this->container;
|
||||||
|
if ($container instanceof Container) {
|
||||||
|
$consumers = $container->get(ConfigInterface::class)->get('services.consumers', []);
|
||||||
|
$serviceFactory = $container->get(ProxyFactory::class);
|
||||||
|
$definitions = $container->getDefinitionSource();
|
||||||
|
foreach ($consumers as $consumer) {
|
||||||
|
if (empty($consumer['name'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$serviceClass = $consumer['service'] ?? $consumer['name'];
|
||||||
|
if (! interface_exists($serviceClass)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$definitions->addDefinition(
|
||||||
|
$consumer['id'] ?? $consumer['name'],
|
||||||
|
function (ContainerInterface $container) use ($serviceFactory, $consumer, $serviceClass) {
|
||||||
|
$proxyClass = $serviceFactory->createProxy($serviceClass);
|
||||||
|
|
||||||
|
return new $proxyClass(
|
||||||
|
$container,
|
||||||
|
$consumer['name'],
|
||||||
|
$consumer['protocol'] ?? 'jsonrpc-http',
|
||||||
|
Arr::only($consumer, ['load_balancer'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,6 @@ namespace Hyperf\RpcClient\Pool;
|
|||||||
|
|
||||||
use Hyperf\Di\Container;
|
use Hyperf\Di\Container;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Swoole\Coroutine\Channel;
|
|
||||||
|
|
||||||
class PoolFactory
|
class PoolFactory
|
||||||
{
|
{
|
||||||
@ -24,7 +23,7 @@ class PoolFactory
|
|||||||
protected $container;
|
protected $container;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Channel[]
|
* @var RpcClientPool[]
|
||||||
*/
|
*/
|
||||||
protected $pools = [];
|
protected $pools = [];
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ class PoolFactory
|
|||||||
$this->container = $container;
|
$this->container = $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPool(string $name): RedisPool
|
public function getPool(string $name): RpcClientPool
|
||||||
{
|
{
|
||||||
if (isset($this->pools[$name])) {
|
if (isset($this->pools[$name])) {
|
||||||
return $this->pools[$name];
|
return $this->pools[$name];
|
||||||
|
34
src/rpc-client/src/Proxy/AbstractProxyService.php
Normal file
34
src/rpc-client/src/Proxy/AbstractProxyService.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Hyperf\RpcClient\Proxy;
|
||||||
|
|
||||||
|
use Hyperf\RpcClient\ServiceClient;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
abstract class AbstractProxyService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ServiceClient
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container, string $serviceName, string $protocol, array $options = [])
|
||||||
|
{
|
||||||
|
$this->client = make(ServiceClient::class, [
|
||||||
|
'container' => $container,
|
||||||
|
'serviceName' => $serviceName,
|
||||||
|
'protocol' => $protocol,
|
||||||
|
'options' => $options,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
66
src/rpc-client/src/Proxy/Ast.php
Normal file
66
src/rpc-client/src/Proxy/Ast.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Hyperf\RpcClient\Proxy;
|
||||||
|
|
||||||
|
use Hyperf\Utils\Composer;
|
||||||
|
use PhpParser\NodeTraverser;
|
||||||
|
use PhpParser\ParserFactory;
|
||||||
|
use PhpParser\PrettyPrinter\Standard;
|
||||||
|
use PhpParser\PrettyPrinterAbstract;
|
||||||
|
|
||||||
|
class Ast
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \PhpParser\Parser
|
||||||
|
*/
|
||||||
|
private $astParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PrettyPrinterAbstract
|
||||||
|
*/
|
||||||
|
private $printer;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$parserFactory = new ParserFactory();
|
||||||
|
$this->astParser = $parserFactory->create(ParserFactory::ONLY_PHP7);
|
||||||
|
$this->printer = new Standard();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function proxy(string $className, string $proxyClassName)
|
||||||
|
{
|
||||||
|
if (! interface_exists($className)) {
|
||||||
|
throw new \InvalidArgumentException("'{$className}' should be an interface name");
|
||||||
|
}
|
||||||
|
if (strpos($proxyClassName, '\\') !== false) {
|
||||||
|
$exploded = explode('\\', $proxyClassName);
|
||||||
|
$proxyClassName = end($exploded);
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = $this->getCodeByClassName($className);
|
||||||
|
$stmts = $this->astParser->parse($code);
|
||||||
|
$traverser = new NodeTraverser();
|
||||||
|
$traverser->addVisitor(new ProxyCallVisitor($proxyClassName));
|
||||||
|
$modifiedStmts = $traverser->traverse($stmts);
|
||||||
|
return $this->printer->prettyPrintFile($modifiedStmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCodeByClassName(string $className): string
|
||||||
|
{
|
||||||
|
$file = Composer::getLoader()->findFile($className);
|
||||||
|
if (! $file) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return file_get_contents($file);
|
||||||
|
}
|
||||||
|
}
|
57
src/rpc-client/src/Proxy/ProxyCallVisitor.php
Normal file
57
src/rpc-client/src/Proxy/ProxyCallVisitor.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Hyperf\RpcClient\Proxy;
|
||||||
|
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Stmt\Interface_;
|
||||||
|
use PhpParser\NodeVisitorAbstract;
|
||||||
|
|
||||||
|
class ProxyCallVisitor extends NodeVisitorAbstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $classname;
|
||||||
|
|
||||||
|
public function __construct(string $classname)
|
||||||
|
{
|
||||||
|
$this->classname = $classname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function leaveNode(Node $node)
|
||||||
|
{
|
||||||
|
if ($node instanceof Interface_) {
|
||||||
|
return new Node\Stmt\Class_($this->classname, [
|
||||||
|
'stmts' => $node->stmts,
|
||||||
|
'extends' => new Node\Name\FullyQualified(AbstractProxyService::class),
|
||||||
|
'implements' => [
|
||||||
|
$node->name,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($node instanceof Node\Stmt\ClassMethod) {
|
||||||
|
$node->stmts = [
|
||||||
|
new Node\Stmt\Return_(new Node\Expr\MethodCall(
|
||||||
|
new Node\Expr\PropertyFetch(new Node\Expr\Variable('this'), new Node\Identifier('client')),
|
||||||
|
new Node\Identifier('__call'),
|
||||||
|
[
|
||||||
|
new Node\Scalar\MagicConst\Function_(),
|
||||||
|
new Node\Expr\FuncCall(new Node\Name('func_get_args')),
|
||||||
|
]
|
||||||
|
)),
|
||||||
|
];
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
return parent::leaveNode($node); // TODO: Change the autogenerated stub
|
||||||
|
}
|
||||||
|
}
|
60
src/rpc-client/src/ProxyFactory.php
Normal file
60
src/rpc-client/src/ProxyFactory.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Hyperf\RpcClient;
|
||||||
|
|
||||||
|
use Hyperf\RpcClient\Proxy\Ast;
|
||||||
|
use Hyperf\Utils\Coroutine\Locker;
|
||||||
|
use Hyperf\Utils\Traits\Container;
|
||||||
|
|
||||||
|
class ProxyFactory
|
||||||
|
{
|
||||||
|
use Container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Ast
|
||||||
|
*/
|
||||||
|
private $ast;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->ast = new Ast();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createProxy($serviceClass): string
|
||||||
|
{
|
||||||
|
if (self::has($serviceClass)) {
|
||||||
|
return (string) self::get($serviceClass);
|
||||||
|
}
|
||||||
|
$dir = BASE_PATH . '/runtime/container/proxy/';
|
||||||
|
if (! file_exists($dir)) {
|
||||||
|
mkdir($dir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$proxyFileName = str_replace('\\', '_', $serviceClass);
|
||||||
|
$proxyClassName = $serviceClass . '_' . md5($this->ast->getCodeByClassName($serviceClass));
|
||||||
|
$path = $dir . $proxyFileName . '.proxy.php';
|
||||||
|
|
||||||
|
$key = md5($path);
|
||||||
|
// If the proxy file does not exist, then try to acquire the coroutine lock.
|
||||||
|
if (! file_exists($path) && Locker::lock($key)) {
|
||||||
|
$targetPath = $path . '.' . uniqid();
|
||||||
|
$code = $this->ast->proxy($serviceClass, $proxyClassName);
|
||||||
|
file_put_contents($targetPath, $code);
|
||||||
|
rename($targetPath, $path);
|
||||||
|
Locker::unlock($key);
|
||||||
|
}
|
||||||
|
include_once $path;
|
||||||
|
self::set($serviceClass, $proxyClassName);
|
||||||
|
return $proxyClassName;
|
||||||
|
}
|
||||||
|
}
|
88
src/rpc-client/src/ServiceClient.php
Normal file
88
src/rpc-client/src/ServiceClient.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Hyperf\RpcClient;
|
||||||
|
|
||||||
|
use Hyperf\Contract\IdGeneratorInterface;
|
||||||
|
use Hyperf\Contract\NormalizerInterface;
|
||||||
|
use Hyperf\Di\MethodDefinitionCollectorInterface;
|
||||||
|
use Hyperf\RpcClient\Exception\RequestException;
|
||||||
|
use Hyperf\Utils\Arr;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class ServiceClient extends AbstractServiceClient
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var MethodDefinitionCollectorInterface
|
||||||
|
*/
|
||||||
|
protected $methodDefinitionCollector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var NormalizerInterface
|
||||||
|
*/
|
||||||
|
private $normalizer;
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container, string $serviceName, string $protocol = 'jsonrpc-http', array $options = [])
|
||||||
|
{
|
||||||
|
$this->serviceName = $serviceName;
|
||||||
|
$this->protocol = $protocol;
|
||||||
|
$this->setOptions($options);
|
||||||
|
parent::__construct($container);
|
||||||
|
$this->normalizer = $container->get(NormalizerInterface::class);
|
||||||
|
$this->methodDefinitionCollector = $container->get(MethodDefinitionCollectorInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function __request(string $method, array $params, ?string $id = null)
|
||||||
|
{
|
||||||
|
if ($this->idGenerator instanceof IdGeneratorInterface && ! $id) {
|
||||||
|
$id = $this->idGenerator->generate();
|
||||||
|
}
|
||||||
|
$response = $this->client->send($this->__generateData($method, $params, $id));
|
||||||
|
if (! is_array($response)) {
|
||||||
|
throw new RequestException('Invalid response.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($response['result'])) {
|
||||||
|
$type = $this->methodDefinitionCollector->getReturnType($this->serviceName, $method);
|
||||||
|
return $this->normalizer->denormalize($response['result'], $type->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code = $response['error']['code'] ?? null) {
|
||||||
|
$error = $response['error'];
|
||||||
|
// Denormalize exception.
|
||||||
|
$class = Arr::get($error, 'data.class');
|
||||||
|
$attributes = Arr::get($error, 'data.attributes', []);
|
||||||
|
if (isset($class) && class_exists($class) && $e = $this->normalizer->denormalize($attributes, $class)) {
|
||||||
|
if ($e instanceof \Exception) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw RequestException when denormalize exception failed.
|
||||||
|
throw new RequestException($error['message'] ?? '', $error['code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RequestException('Invalid response.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call(string $method, array $params)
|
||||||
|
{
|
||||||
|
return $this->__request($method, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setOptions(array $options): void
|
||||||
|
{
|
||||||
|
if (isset($options['load_balancer'])) {
|
||||||
|
$this->loadBalancer = $options['load_balancer'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -62,7 +62,7 @@ class Protocol
|
|||||||
if (! $this->container->has($transporter)) {
|
if (! $this->container->has($transporter)) {
|
||||||
throw new \InvalidArgumentException("Transporter {$transporter} for {$this->name} does not exist");
|
throw new \InvalidArgumentException("Transporter {$transporter} for {$this->name} does not exist");
|
||||||
}
|
}
|
||||||
return $this->container->get($transporter);
|
return make($transporter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPathGenerator(): PathGeneratorInterface
|
public function getPathGenerator(): PathGeneratorInterface
|
||||||
|
41
src/utils/tests/Traits/ContainerTest.php
Normal file
41
src/utils/tests/Traits/ContainerTest.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://doc.hyperf.io
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace HyperfTest\Utils\Traits;
|
||||||
|
|
||||||
|
use Hyperf\Utils\Traits\Container;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class ContainerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGet()
|
||||||
|
{
|
||||||
|
Foo::set('foo', 1);
|
||||||
|
$this->assertNull(Bar::get('foo'));
|
||||||
|
Bar::set('foo', 2);
|
||||||
|
$this->assertEquals(1, Foo::get('foo'));
|
||||||
|
$this->assertEquals(2, Bar::get('foo'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
use Container;
|
||||||
|
}
|
||||||
|
class Bar
|
||||||
|
{
|
||||||
|
use Container;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user