Skip to content

Commit

Permalink
Release v3.0.0
Browse files Browse the repository at this point in the history
Migrate to guzzle http client
Update json rpc client - now it returns result and throw rpc/http errors via exceptions.
Update generator to use new json rpc client
  • Loading branch information
sergeyfast authored Apr 6, 2021
2 parents 7208658 + 6655c38 commit c9489b2
Show file tree
Hide file tree
Showing 18 changed files with 746 additions and 369 deletions.
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
eazy-jsonrpc
============
[![Latest Version](https://img.shields.io/github/release/sergeyfast/eazy-jsonrpc.svg?style=flat-square)](https://github.com/sergeyfast/eazy-jsonrpc/releases)
[![Total Downloads](https://img.shields.io/packagist/dt/sergeyfast/eazy-jsonrpc.svg?style=flat-square)](https://packagist.org/packages/sergeyfast/eazy-jsonrpc)

PHP JSON-RPC 2.0 Server/Client Implementation with Automatic Client Class Generation via SMD

Expand All @@ -11,21 +13,44 @@ SMD Schema available via /server.php?smd
__Public Namespace__

* Inherits your exposed class from BaseJsonRpcServer or create `new BaseJsonRpcServer( $instance );`
* `$server->execute();`
* `$server->Execute();`

__Multiple Namespaces__

* Create `new BaseJsonRpcServer();`
* Call `$server->RegisterInstance( $instance, $namespace )` as many times as you need
* `$server->execute();`
* `$server->Execute();`


Client
------

* Generate Client from SMD Schema from generator/ `php JsonRpcClientGenerator <smd-file> <class-name>`
* Create client instance `$client = <class-name>::GetInstance();` or `$client = new <class-name>( <url> );`
* Use it `$result = $client->Method()`; :)
* Generate Client from SMD Schema from generator/ `php JsonRpcClientGenerator.php <smd-file> <class-name>`
* Use it:
```
$client = <class-name>::GetInstance(<url>);
try {
$result = $client->Method();
} catch (BaseJsonRpcException $e) {
// work with exception
}
```

Client with typed returns by rpcgen
------

* Generate Client from SMD Schema with [rpcgen](https://github.com/vmkteam/rpcgen) and save it to `RpcClient.php`
* Use it:
```
$client = RpcClient::GetInstance(<url>);
try {
$result = $client->Method();
} catch (BaseJsonRpcException $e) {
// work with exception
}
```

Doc
------
Expand Down
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
},
"target-dir":"",
"require":{
"php":">=5.4.0",
"php":">=7.2.0",
"ext-curl": "*",
"ext-json": "*"
"ext-json": "*",
"netresearch/jsonmapper": "^4.0",
"guzzlehttp/guzzle": "^7.3"
},
"require-dev":{
"phpunit/phpunit": "4.8.*"
Expand Down
168 changes: 110 additions & 58 deletions generator/JsonRpcClientGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class JsonRpcClientGenerator {
* @param array $smd SMD Schema
* @param string $className
*/
public function __construct( $url, $smd, $className ) {
public function __construct( string $url, array $smd, string $className ) {
$this->url = $url;
$this->smd = $smd;
$this->className = $className;
Expand All @@ -56,12 +56,22 @@ private function getHeader() {
$date = date( 'd.m.Y G:i' );
$result = <<<php
<?php
/**
* PHP RPC Client by JsonRpcClientGenerator
* @date {$date}
*/
namespace JsonRpcClient;
use EazyJsonRpc\BaseJsonRpcClient;
use EazyJsonRpc\BaseJsonRpcException;
use GuzzleHttp\Exception\GuzzleException;
use JsonMapper_Exception;
/**
* {$description}
* @author JsonRpcClientGenerator
* @date {$date}
*/
class {$this->className} extends \EazyJsonRpc\BaseJsonRpcClient {
class {$this->className} extends BaseJsonRpcClient {
php;

return $result;
Expand All @@ -73,121 +83,163 @@ class {$this->className} extends \EazyJsonRpc\BaseJsonRpcClient {
* @param $methodData
* @return string
*/
public function getMethod( $methodName, $methodData ) {
$newDocLine = PHP_EOL . str_repeat( ' ', 9 ) . '*';
$description = !empty( $methodData['description'] ) ? $methodData['description'] : $methodName;
public function getMethod( $methodName, $methodData ): string {
$newDocLine = PHP_EOL . str_repeat( ' ', 8 ) . '*';
$description = sprintf( '<%s> RPC method', $methodName );
$description .= !empty( $methodData['description'] ) ? $newDocLine . ' ' . trim( $methodData['description'] ) : '';
$description = str_replace( "\n", PHP_EOL, $description );
$strDocParams = '';
$strParamsArr = [ ];
$callParamsArr = [ ];
$strParamsArr = [];
$callParamsArr = [];
$methodName = str_replace( '.', '_', $methodName );

// Add Default Parameter = IsNotification
$methodData['parameters'][] = [
'name' => 'isNotification',
'optional' => 'true',
'type' => 'bool',
'default' => false,
'description' => 'set to true if call is notification',
];

// params
if ( !empty( $methodData['parameters'] ) ) {
// Set Doc Params
foreach ( $methodData['parameters'] as $param ) {
$name = $param['name'];
$strParam = '$' . $name;
$strDocParamsArr = [ $newDocLine ];
$strDocParamsArr[] = '@param';

$name = $param['name'];
$strDocParam = $newDocLine;
$strDocParam .= " @param";
if ( !empty( $param['type'] ) ) {
$strDocParamsArr[] = $param['type'];
$strDocParam .= " " . $this->getPhpType( $param['type'] );
}
$strParam = $this->getPhpType( $param['type'] ) . ' $' . $name;
$optionalParam = !empty( $param['optional'] );
if ( $optionalParam ) {
$strDocParam .= '|null';
}

$strDocParamsArr[] = '$' . $name;
if ( !empty( $param['optional'] ) ) {
$strDocParamsArr[] = '[optional]';
$strDocParam .= ' $' . $name;
if ( $optionalParam ) {
$strDocParam .= ' [optional]';
}

if ( !empty( $param['description'] ) ) {
$strDocParamsArr[] = $param['description'];
$strDocParam .= " " . $param['description'];
}

if ( array_key_exists( 'default', $param ) ) {
$strParam .= sprintf( ' = %s', var_export( $param['default'], true ) );
} else {
if ( $optionalParam ) {
$strParam .= ' = null';
}
}

$strDocParams .= rtrim( implode( ' ', $strDocParamsArr ) );
$strDocParams .= rtrim( $strDocParam );
$strParamsArr[] = $strParam;
$callParamsArr[$name] = sprintf( "'%s' => $%s", $name, $name );
}
}

$strParams = ' ' . trim(
str_replace(
[ "\n", ',)', 'array (' ],
[ '', ')', 'array(' ],
implode( ', ', $strParamsArr )
), ', ' ) . ' ';

unset( $callParamsArr['isNotification'] );

$strDocParams .= $newDocLine . ' @param bool $isNotification [optional] set to true if call is notification';

$strParams = str_replace(
[ "\n", ',)', 'array (' ],
[ '', ')', 'array(' ],
implode( ', ', $strParamsArr )
);

$strParams .= ', $isNotification = false ';
$strParams = ' ' . trim( $strParams, ', ' ) . ' ';
$returnType = '';
$optionalReturn = '';
$strDocReturns = $newDocLine . ' @return mixed';
$strReturnType = '';
// returns
if ( !empty( $methodData['returns'] ) && !empty( $methodData['returns']['type'] ) && is_string( $methodData['returns']['type'] ) ) {
$strDocParams .= $newDocLine . ' @return \EazyJsonRpc\BaseJsonRpcCall (result: ' . $methodData['returns']['type'] . ')';
if ( !empty( $methodData['returns'] ) ) {
$strDocReturns = '';
if ( !empty( $methodData['returns']['type'] ) && is_string( $methodData['returns']['type'] ) ) {
$returnType = $this->getPhpType( $methodData['returns']['type'] );
$strDocReturns .= $newDocLine . ' @return ' . $returnType;
}
if ( !empty( $methodData['returns']['optional'] ) ) {
$optionalReturn = '?';
$strDocReturns .= '|null';
}
if ( !empty( $methodData['returns']['description'] ) ) {
$strDocReturns .= ' ' . $methodData['returns']['description'];
}
if ( $returnType != 'mixed' ) {
$strReturnType = sprintf( ': %s%s', $optionalReturn, $returnType );
}
}

$strDocParams .= $strDocReturns;
$callParamsStr = implode( ', ', $callParamsArr );
if ( !empty( $callParamsStr ) ) {
$callParamsStr = sprintf( ' %s ', $callParamsStr );
}


$result = <<<php
return <<<php
/**
* {$description}{$strDocParams}
*/
public function {$methodName}({$strParams}) {
return \$this->call( __FUNCTION__, array({$callParamsStr}), \$this->getRequestId( \$isNotification ) );
* {$description}{$strDocParams}
* @throws BaseJsonRpcException
* @throws GuzzleException
* @throws JsonMapper_Exception
*/
public function {$methodName}({$strParams})$strReturnType {
return \$this->call( __FUNCTION__, '$returnType', [{$callParamsStr}], \$this->getRequestId( \$isNotification ) );
}
php;
return $result;

}


/**
* Get Footer
*/
private function getFooter() {
$url = $this->url;
$urlInfo = parse_url( $url );
private function getFooter(): string {
$rpcUrl = $this->url;
$urlInfo = parse_url( $rpcUrl );
if ( !empty( $urlInfo ) ) {
$url = sprintf( '%s://%s%s', $urlInfo['scheme'], $urlInfo['host'], $this->smd['target'] );
$rpcUrl = sprintf( '%s://%s%s', $urlInfo['scheme'], $urlInfo['host'], $this->smd['target'] );
}

$result = <<<php
return <<<php
/**
* Get Instance
* @param \$url string
* @return {$this->className}
*/
public static function GetInstance() {
return new self( '{$url}' );
public static function GetInstance( string \$url ): {$this->className} {
return new self( \$url );
}
}
php;
}

return $result;

/**
* Return PHP type from SMD type
* @param string $smdType
* @return string
*/
private function getPhpType( string $smdType ): string {
switch ( $smdType ) {
case "string":
return "string";
case "object":
case "array":
return "array";
case "boolean":
return "bool";
case "float":
return "float";
case "integer":
return "int";
}
return "mixed";
}


/**
* Save to File
*/
public function Generate() {
public function Generate(): string {
$this->result = $this->getHeader();

foreach ( $this->smd['services'] as $methodName => $methodData ) {
Expand All @@ -205,7 +257,7 @@ public function Generate() {
* Save To File
* @return int
*/
public function SaveToFile() {
public function SaveToFile(): int {
return file_put_contents( $this->className . '.php', $this->Generate() );
}
}
Expand Down
Loading

0 comments on commit c9489b2

Please sign in to comment.