Skip to content

Commit

Permalink
Improved validation system.
Browse files Browse the repository at this point in the history
  • Loading branch information
cranetm committed Jan 17, 2017
1 parent 747efa9 commit 89b3d75
Show file tree
Hide file tree
Showing 12 changed files with 398 additions and 190 deletions.
103 changes: 61 additions & 42 deletions JsonRpc2/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace JsonRpc2;

use JsonRpc2\Validator\Value;
use Yii;
use yii\base\InlineAction;
use yii\base\InvalidParamException;
Expand Down Expand Up @@ -48,7 +49,7 @@ public function runAction($id, $params = [])
$resultData = null;
if (empty($requests)) {
$isBatch = false;
$resultData = [Helper::formatResponse(null, new Exception("Invalid Request", Exception::INVALID_REQUEST))];
$resultData = [$this->formatResponse(null, new Exception("Invalid Request", Exception::INVALID_REQUEST))];
} else {
foreach ($requests as $request) {
if($response = $this->getActionResponse($request))
Expand Down Expand Up @@ -81,15 +82,19 @@ private function getActionResponse($requestObject)
} catch (HttpException $e) {
throw $e;
} catch (Exception $e) {
$error = $e;
if ($e->getCode() === Exception::INVALID_PARAMS) {
$error = new Exception($e->getMessage(), Exception::INTERNAL_ERROR, $e->getData());
} else {
$error = $e;
}
} catch (\Exception $e) {
$error = new Exception("Internal error", Exception::INTERNAL_ERROR);
}

if (!isset($this->requestObject->id) && (empty($error) || !in_array($error->getCode(), [Exception::PARSE_ERROR, Exception::INVALID_REQUEST])))
return null;

return Helper::formatResponse($result, $error, isset($this->requestObject->id)? $this->requestObject->id : null);
return $this->formatResponse($result, $error, isset($this->requestObject->id)? $this->requestObject->id : null);
}

/**
Expand Down Expand Up @@ -255,13 +260,11 @@ private function validateActionParams()
if (!isset($this->methodInfo['params'][$name])) continue;
$paramInfo = $this->methodInfo['params'][$name];

$this->requestObject->params->$name = Helper::bringValueToType(
$this,
$paramInfo['type'],
$value,
$paramInfo['isNullable'],
$paramInfo['restrictions']
);
$paramValue = new Value($paramInfo['name'], $value, $this);
foreach ($paramInfo['validators'] as $type=>$params) {
$paramValue = Validator::run($type, $params, $paramValue);
}
$this->requestObject->params->$name = $paramValue->data;
}
}

Expand All @@ -288,7 +291,7 @@ private function getMethodFromAction($action)
private function getMethodParamsTypes()
{
return array_reduce($this->methodInfo['params'], function ($result, $item) {
$result[$item['name']] = $item['type'];
$result[$item['name']] = $item['validators']['var'];
return $result;
}, []);
}
Expand All @@ -300,13 +303,11 @@ private function getMethodParamsTypes()
private function validateResult($result)
{
if (!empty($this->methodInfo['return'])) {
$result = Helper::bringValueToType(
$this,
$this->methodInfo['return']['type'],
$result,
$this->methodInfo['return']['isNullable'],
$this->methodInfo['return']['restrictions']
);
$resultValue = new Value($this->methodInfo['return']['name'], $result, $this);
foreach ($this->methodInfo['return']['validators'] as $type=>$params) {
$resultValue = Validator::run($type, $params, $resultValue);
}
$result = $resultValue->data;
}

return $result;
Expand All @@ -322,9 +323,8 @@ private function parseMethodDocComment($method)
$variableRegex = '\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)';

$infoTpl = [
'type' => '',
'isNullable' => false,
'restrictions' => [],
'name' => '',
'validators' => [],
];

$this->methodInfo = [
Expand All @@ -339,35 +339,54 @@ private function parseMethodDocComment($method)
$subject = &$this->methodInfo['params'][$paramMatches[2]];
$subject = $infoTpl;
$subject['name'] = $paramMatches[2];
$subject['type'] = $paramMatches[1];
$subject['validators']['var'] = $paramMatches[1];
continue;
} else {
preg_match("/@return\s+([\w\\\\\[\]]+)/", $lines[$i], $paramMatches);
if (!empty($paramMatches)) {
$subject = &$this->methodInfo['return'];
$subject = $infoTpl;
$subject['type'] = $paramMatches[1];
$subject['name'] = "result";
$subject['validators']['var'] = $paramMatches[1];
continue;
}
}
if (!empty($subject)) {

//search in two next lines for @null or @inArray tags
for ($j=0; $j<2; $j++) {
preg_match("/@(null|inArray(\[(.*)\]))/", $lines[$i+1], $tagMatches);
if (empty($tagMatches)) break;

if (strpos($tagMatches[0], "@inArray") === 0 && in_array($subject['type'], ['string', 'int'])) {
eval("\$parsedData = {$tagMatches[2]};");
if (!is_array($parsedData))
throw new Exception(sprintf("Invalid syntax in %s in tag @inArray{$tagMatches[2]}", get_class($this)), Exception::INTERNAL_ERROR);
$subject['restrictions'] = $parsedData;
} elseif (strpos($tagMatches[0], "@null") === 0) {
$subject['isNullable'] = true;
}

$i++;
}
preg_match("/@([\w]+)[ ]?(.*)/", $lines[$i], $validatorMatches);
if (!empty($subject) && !empty($validatorMatches)) {
$subject['validators'][$validatorMatches[1]] = trim($validatorMatches[2]);
}
unset($subject);
}
}

/** @var array Use as 'result' when method returns null */
private static $defaultResult = [
"success" => true
];

/**
* Formats and returns
* @param null $result
* @param \JsonRpc2\Exception|null $error
* @param null $id
* @return array
*/
public function formatResponse($result = null, Exception $error = null, $id = null)
{
$resultKey = 'result';

if (!empty($error)) {
$resultKey = 'error';
$resultValue = $error->toArray();
}
else if (null === $result)
$resultValue = self::$defaultResult;
else
$resultValue = $result;

return [
'jsonrpc' => '2.0',
$resultKey => $resultValue,
'id' => $id,
];
}
}
30 changes: 7 additions & 23 deletions JsonRpc2/Dto.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace JsonRpc2;


use JsonRpc2\Validator\Value;
use ReflectionProperty;

class Dto {
Expand All @@ -15,29 +16,12 @@ protected function setDataFromArray($data)
foreach (get_object_vars($this) as $name=>$defaultValue) {
$property = new ReflectionProperty(get_class($this), $name);
if (!$property->isPublic()) continue;

preg_match("/@var[ ]+([\w\\\\\[\]]+)/", $property->getDocComment(), $matches);
$type = !empty($matches) ? $matches[1] : false;
if (empty($type)) continue;

preg_match("/@null/", $property->getDocComment(), $matches);
$isNullable = !empty($matches);

preg_match("/@required/", $property->getDocComment(), $matches);
$isRequired = !empty($matches);

$restrictions = [];
preg_match("/@(inArray(\[(.*)\]))/", $property->getDocComment(), $matches);
if (!empty($matches) && in_array($type, ['string', 'int'])) {
eval("\$parsedData = {$matches[2]};");
if (!is_array($parsedData))
throw new Exception(get_class($this).": Invalid syntax in {$name} tag @inArray{$matches[2]}", Exception::INTERNAL_ERROR);
$restrictions = $parsedData;
preg_match_all("/@([\w]+)[ ]?(.*)/", $property->getDocComment(), $matches);
$propValue = new Value($name, isset($data[$name]) ? $data[$name] : $defaultValue, $this);
foreach ($matches[1] as $key=>$value) {
$propValue = Validator::run($matches[1][$key], trim($matches[2][$key]), $propValue);
}

if ($isRequired && !isset($data[$name]))
throw new Exception("Field {$name} is required", Exception::INVALID_PARAMS);
$this->$name = Helper::bringValueToType($this, $type, isset($data[$name]) ? $data[$name] : $defaultValue, $isNullable, $restrictions);
$this->$name = $propValue->data;
}
}
}
}
9 changes: 7 additions & 2 deletions JsonRpc2/Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ public function __construct($message, $code, $data = null)
parent::__construct($message, $code);
}

public function getData()
{
return $this->data;
}

public function toArray() {
return [
"code" => $this->getCode(),
"message" => $this->getMessage(),
"data" => $this->data,
"data" => $this->getData(),
];
}
}
}
123 changes: 0 additions & 123 deletions JsonRpc2/Helper.php

This file was deleted.

Loading

0 comments on commit 89b3d75

Please sign in to comment.