Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 137 additions & 138 deletions README.md

Large diffs are not rendered by default.

14 changes: 5 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,11 @@
"require": {
"php": "8.2.*|8.3.*|8.4.*",
"ext-json": "*",
"bensampo/laravel-enum": "^5.0|^6.0",
"illuminate/support": "^9.0|^10.0|^11.0|^12.0",
"illuminate/http": "^9.0|^10.0|^11.0|^12.0",
"illuminate/container": "^9.0|^10.0|^11.0|^12.0",
"illuminate/pipeline": "^9.0|^10.0|^11.0|^12.0",
"illuminate/console": "^9.0|^10.0|^11.0|^12.0",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"spiral/attributes": "^2.8|^3.0",
"tochka-developers/jsonrpc-annotations": "^1.1",
"illuminate/support": "^10.0|^11.0|^12.0",
"illuminate/http": "^10.0|^11.0|^12.0",
"illuminate/container": "^10.0|^11.0|^12.0",
"illuminate/pipeline": "^10.0|^11.0|^12.0",
"illuminate/console": "^10.0|^11.0|^12.0",
"psr/simple-cache": "^1.0|^2.0"
},
"require-dev": {
Expand Down
37 changes: 7 additions & 30 deletions config/jsonrpc.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,9 @@
use Tochka\JsonRpc\Middleware\LogMiddleware;
use Tochka\JsonRpc\Middleware\ServiceValidationMiddleware;
use Tochka\JsonRpc\Middleware\TokenAuthMiddleware;
use Tochka\JsonRpc\Support\ServerConfig;

return [
'default' => [
// Точка входа в указанный сервер
'endpoint' => '/api/v1/public/jsonrpc',

/**
* Тип формирования точки входа (получать или нет из конечного URI группу методов и метод
*
* ServerConfig::DYNAMIC_ENDPOINT_NONE - точка входа статична, все контроллеры располагаются в одном пространстве имен
* Пример:
* uri: /api/v1/public/jsonrpc
* jsonrpc method: test_ping
* controller@method: \Default\Controller\Namespace\TestController@ping
*
* ServerConfig::DYNAMIC_ENDPOINT_CONTROLLER_NAMESPACE - все, что отличается в URI от указанной точки входа -
* является постфиксом к пространству имен контроллеров (group).
* Пример:
* uri: /api/v1/public/jsonrpc/foo/bar
* jsonrpc method: test_ping
* controller@method: \Default\Controller\Namespace\Foo\Bar\TestController@ping
*
* ServerConfig::DYNAMIC_ENDPOINT_FULL_CONTROLLER_NAME - последний элемент URI является именем контроллера (action),
* предыдущие элементы до указанной точки входа - постфикс к пространству имен контроллеров.
* Пример:
* uri: /api/v1/public/jsonrpc/foo/bar
* jsonrpc method: test_ping
* controller@method: \Default\Controller\Namespace\Foo\BarController@test_ping
*/
'dynamicEndpoint' => ServerConfig::DYNAMIC_ENDPOINT_NONE,

// Краткое описание сервера
'summary' => 'Основная точка входа',

Expand All @@ -55,8 +26,14 @@
// Разделитель для имен методов
'methodDelimiter' => '_',

// Использовать методы родителя при наследовании
// Использовать методы родителя при наследовании (Будет удалено)
'allowParentMethods' => false,

// Список дополнительный обработчиков типов. Будут выполняться перед встроенными.
// Должны наследовать AbstractPropertyCaster
'customCasters' => [

],

// Обработчики запросов
'middleware' => [
Expand Down
9 changes: 9 additions & 0 deletions src/Attributes/ApiDI.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Tochka\JsonRpc\Attributes;

#[\Attribute(\Attribute::TARGET_PARAMETER)]
class ApiDI
{

}
9 changes: 9 additions & 0 deletions src/Attributes/ApiMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Tochka\JsonRpc\Attributes;

#[\Attribute(\Attribute::TARGET_METHOD)]
class ApiMethod
{

}
9 changes: 9 additions & 0 deletions src/Attributes/ApiParams.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Tochka\JsonRpc\Attributes;

#[\Attribute(\Attribute::TARGET_PARAMETER)]
class ApiParams
{

}
113 changes: 113 additions & 0 deletions src/Casters/AbstractPropertyCaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

namespace Tochka\JsonRpc\Casters;

use Tochka\JsonRpc\Exceptions\JsonRpcInvalidParameterError;
use Tochka\JsonRpc\Exceptions\JsonRpcInvalidParameterException;
use Tochka\JsonRpc\Exceptions\JsonRpcInvalidParameterTypeException;
use Tochka\JsonRpc\Router\RouteParam;
use Tochka\JsonRpc\Support\VoidValue;

abstract class AbstractPropertyCaster
{
abstract public static function canCast(RouteParam $param): bool;

abstract public static function cast(RouteParam $param, object $input): mixed;


public static function getValue(RouteParam $param, object|array $input): mixed
{
if (is_array($input)) {
return $input[$param->name] ?? new VoidValue();
}

if (property_exists($input, $param->name)) {
return $input->{$param->name};
}

return new VoidValue();
}

/**
* @throws JsonRpcInvalidParameterException
*/
public static function typeCheck(RouteParam $param, mixed $value): bool
{
if ($value === null) {
if ($param->isNullable) {
return true;
}
throw new JsonRpcInvalidParameterException(
JsonRpcInvalidParameterError::PARAMETER_ERROR_NOT_NULLABLE,
$param->name,
);
}

if (empty($param->allowedTypes)) {
return false;
}

$type = self::getTypeNormalized($value);
if (\in_array($type, $param->allowedTypes, true)) {
return false;
}

throw new JsonRpcInvalidParameterTypeException($param->name, implode('|', $param->allowedTypes), $type);
}

/**
* @throws JsonRpcInvalidParameterException
*/
public static function optionalCheck(RouteParam $param, mixed $value): bool
{
if ($value instanceof VoidValue) {
if ($param->isOptional) {
return true;
} else {
throw new JsonRpcInvalidParameterException(
JsonRpcInvalidParameterError::PARAMETER_ERROR_REQUIRED, $param->name
);
}
}
return false;
}

/**
* @throws \ReflectionException
* @throws JsonRpcInvalidParameterException
*/
public static function createObjectWithoutConstructor(RouteParam $param, array|object $data): object
{
$dataAsArray = (array)$data;
$reflector = new \ReflectionClass($param->className);
$object = $reflector->newInstanceWithoutConstructor();
foreach ($dataAsArray as $propName => $propValue) {
try {
$reflector->getProperty($propName)?->setValue($object, $propValue);
} catch (\ReflectionException) {
// Property not exist, just skip
continue;
} catch (\TypeError) {
throw new JsonRpcInvalidParameterTypeException(
$propName,
$reflector->getProperty($propName)->getType(),
self::getTypeNormalized($propValue),
);
}
}

return $object;
}

public static function getTypeNormalized(mixed $value): string
{
$type = gettype($value);
return match ($type) {
'boolean' => 'bool',
'integer' => 'int',
'double' => 'float',
'NULL' => 'null',
default => $type,
};
}
}
40 changes: 0 additions & 40 deletions src/Casters/BackedEnumCaster.php

This file was deleted.

42 changes: 0 additions & 42 deletions src/Casters/BenSampoEnumCaster.php

This file was deleted.

24 changes: 24 additions & 0 deletions src/Casters/DICaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Tochka\JsonRpc\Casters;

use Illuminate\Container\Container;
use Illuminate\Contracts\Container\BindingResolutionException;
use Tochka\JsonRpc\Router\PropType;
use Tochka\JsonRpc\Router\RouteParam;

class DICaster extends AbstractPropertyCaster
{
public static function canCast(RouteParam $param): bool
{
return $param->propType === PropType::DI;
}

/**
* @throws BindingResolutionException
*/
public static function cast(RouteParam $param, object $input): mixed
{
return Container::getInstance()->make($param->className);
}
}
45 changes: 45 additions & 0 deletions src/Casters/EnumCaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Tochka\JsonRpc\Casters;

use BackedEnum;
use Tochka\JsonRpc\Exceptions\JsonRpcInvalidParameterException;
use Tochka\JsonRpc\Router\PropType;
use Tochka\JsonRpc\Router\RouteParam;
use Tochka\JsonRpc\Support\VoidValue;

class EnumCaster extends AbstractPropertyCaster
{
public static function canCast(RouteParam $param): bool
{
return $param->propType === PropType::Enum;
}

/**
* @throws JsonRpcInvalidParameterException
*/
public static function cast(RouteParam $param, object $input): VoidValue|BackedEnum|null
{
$value = self::getValue($param, $input);

if (self::optionalCheck($param, $value)) {
return $value;
}

/** @var \BackedEnum $className */
$className = $param->className;
try {
return $className::from($value);
} catch (\ValueError) {
throw new JsonRpcInvalidParameterException(
'incorrect_value',
sprintf(
'Invalid value for field. Expected: [%s], Actual: [%s]',
implode(',', array_map(fn($enum) => (string)$enum->value, $className::cases())),
$value
),
$param->name
);
}
}
}
20 changes: 20 additions & 0 deletions src/Casters/MixedCaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Tochka\JsonRpc\Casters;

use Tochka\JsonRpc\Router\PropType;
use Tochka\JsonRpc\Router\RouteParam;

class MixedCaster extends AbstractPropertyCaster
{
public static function canCast(RouteParam $param): bool
{
return $param->propType === PropType::Mixed;
}

public static function cast(RouteParam $param, object $input): mixed
{
// todo проверить на пустоту и на null
return self::getValue($param, $input);
}
}
Loading