Skip to content

Commit

Permalink
Parameters: basic mapping parameters from URL to ApiRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
f3l1x committed Jan 30, 2017
1 parent 42ea7a6 commit 595cc59
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 9 deletions.
49 changes: 49 additions & 0 deletions src/Http/Request/ApiRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Contributte\Api\Http\Request;

use Contributte\Api\Exception\Logical\InvalidStateException;
use Contributte\Api\Http\Request\Param\AbstractParameter;
use Contributte\Api\Schema\Endpoint;
use Psr\Http\Message\ServerRequestInterface;

Expand All @@ -14,6 +16,9 @@ class ApiRequest
/** @var Endpoint */
protected $endpoint;

/** @var AbstractParameter[] */
protected $parameters = [];

/**
* PSR-7 *******************************************************************
*/
Expand Down Expand Up @@ -62,4 +67,48 @@ public function getEndpoint()
return $this->endpoint;
}

/**
* PARAMETERS **************************************************************
*/

/**
* @param string $name
* @param AbstractParameter $parameter
* @return void
*/
public function addParameter($name, AbstractParameter $parameter)
{
$this->parameters[$name] = $parameter;
}

/**
* @param string $name
* @return bool
*/
public function hasParameter($name)
{
return isset($this->parameters[$name]);
}

/**
* @param string $name
* @return AbstractParameter
*/
public function getParameter($name)
{
if (!$this->hasParameter($name)) {
throw new InvalidStateException('No parameter found');
}

return $this->parameters[$name];
}

/**
* @return AbstractParameter[]
*/
public function getParameters()
{
return $this->parameters;
}

}
41 changes: 41 additions & 0 deletions src/Http/Request/Param/AbstractParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Contributte\Api\Http\Request\Param;

abstract class AbstractParameter
{

/** @var mixed */
protected $value;

/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}

/**
* PARSING *****************************************************************
*/

/**
* @param mixed $value
* @return void
*/
abstract public function parse($value);

/**
* MAGIC *******************************************************************
*/

/**
* @return mixed
*/
public function __toString()
{
return $this->value;
}

}
35 changes: 35 additions & 0 deletions src/Http/Request/Param/ParametersMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Contributte\Api\Http\Request\Param;

use Contributte\Api\Exception\Logical\InvalidStateException;
use Contributte\Api\Schema\EndpointParam;

class ParametersMapper
{

/** @var array */
public static $mapping = [
EndpointParam::TYPE_SCALAR => ScalarParameter::class,
EndpointParam::TYPE_STRING => StringParameter::class,
];

/**
* @param int $type
* @param mixed $value
* @return AbstractParameter
*/
public static function parse($type, $value)
{
if (!isset(self::$mapping[$type])) {
throw new InvalidStateException('Unsupported mapping type');
}

/** @var AbstractParameter $param */
$param = new self::$mapping[$type]();
$param->parse($value);

return $param;
}

}
23 changes: 23 additions & 0 deletions src/Http/Request/Param/ScalarParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Contributte\Api\Http\Request\Param;

use Contributte\Api\Exception\Logical\InvalidTypeException;

class ScalarParameter extends AbstractParameter
{

/**
* @param mixed $value
* @return void
*/
public function parse($value)
{
if (!is_scalar($value)) {
throw new InvalidTypeException('Scalar value expected');
}

$this->value = $value;
}

}
27 changes: 27 additions & 0 deletions src/Http/Request/Param/StringParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Contributte\Api\Http\Request\Param;

use Contributte\Api\Exception\Logical\InvalidTypeException;

class StringParameter extends AbstractParameter
{

/**
* PARSING *****************************************************************
*/

/**
* @param mixed $value
* @return void
*/
public function parse($value)
{
if (!is_string($value)) {
throw new InvalidTypeException('String value expected');
}

parent::parse(strval($value));
}

}
23 changes: 20 additions & 3 deletions src/Router/ApiRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Contributte\Api\Router;

use Contributte\Api\Http\Request\ApiRequest;
use Contributte\Api\Router\Matcher\IMatcher;
use Contributte\Api\Router\Matcher\RegexMatcher;
use Contributte\Api\Schema\ApiSchema;

Expand All @@ -12,6 +13,9 @@ final class ApiRouter implements IRouter
/** @var ApiSchema */
private $schema;

/** @var IMatcher */
private $matcher;

/**
* @param ApiSchema $schema
*/
Expand All @@ -20,13 +24,25 @@ public function __construct(ApiSchema $schema)
$this->schema = $schema;
}

/**
* @return IMatcher
*/
public function getMatcher()
{
if (!$this->matcher) {
$this->matcher = new RegexMatcher();
}

return $this->matcher;
}

/**
* @param ApiRequest $request
* @return ApiRequest|NULL
*/
public function match(ApiRequest $request)
{
$matcher = new RegexMatcher();
$matcher = $this->getMatcher();

// Iterate over all endpoints
foreach ($this->schema->getEndpoints() as $endpoint) {
Expand All @@ -42,8 +58,9 @@ public function match(ApiRequest $request)
if ($matched === NULL) continue;

// If matched is not NULL, returns given ApiRequest
// with all parsed arguments and data
$matched = $request->withEndpoint($endpoint);
// with all parsed arguments and data,
// also append given Endpoint
$matched = $matched->withEndpoint($endpoint);

return $matched;
}
Expand Down
18 changes: 18 additions & 0 deletions src/Router/Matcher/IMatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Contributte\Api\Router\Matcher;

use Contributte\Api\Http\Request\ApiRequest;
use Contributte\Api\Schema\Endpoint;

interface IMatcher
{

/**
* @param Endpoint $endpoint
* @param ApiRequest $request
* @return ApiRequest
*/
public function match(Endpoint $endpoint, ApiRequest $request);

}
18 changes: 13 additions & 5 deletions src/Router/Matcher/RegexMatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

use Contributte\Api\Bridges\Middlewares\ApiMiddleware;
use Contributte\Api\Http\Request\ApiRequest;
use Contributte\Api\Http\Request\Param\ParametersMapper;
use Contributte\Api\Schema\Endpoint;
use Nette\Utils\Strings;
use Contributte\Api\Utils\Regex;

final class RegexMatcher
final class RegexMatcher implements IMatcher
{

/**
Expand All @@ -33,12 +34,19 @@ public function match(Endpoint $endpoint, ApiRequest $request)
$url = sprintf('/%s', trim($url, '/'));

// Try to match againts the pattern
$match = Strings::match($url, $endpoint->getPattern());
$match = Regex::match($url, $endpoint->getPattern());

// Skip no-match
// Skip if there's no match
if ($match === NULL) return NULL;

// @todo add parameters!
// Fill request parameters from matched URL
foreach ($endpoint->getParams() as $param) {
$request->addParameter(
$param->getName(),
ParametersMapper::parse($param->getType(), $match[$param->getName()])
);
}

return $request;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Schema/EndpointParam.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class EndpointParam
private $name;

/** @var int */
private $type;
private $type = self::TYPE_SCALAR;

/**
* @return string
Expand Down
33 changes: 33 additions & 0 deletions tests/cases/Router/Matcher/RegexMatcher.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/**
* Test: Router/Matcher/RegexMatcher
*/

require_once __DIR__ . '/../../../bootstrap.php';

use Contributte\Api\Http\Request\ApiRequest;
use Contributte\Api\Router\Matcher\RegexMatcher;
use Contributte\Api\Schema\Endpoint;
use Contributte\Api\Schema\EndpointParam;
use Contributte\Psr7\Psr7ServerRequest;
use Tester\Assert;

test(function () {
$endpoint = new Endpoint();
$endpoint->setPattern('#^/users/(?P<id>[^/]+)#');

$id = new EndpointParam();
$id->setName('id');
$endpoint->addParam($id);

$psr7 = new Psr7ServerRequest('GET', 'http://example.com/users/22/');
$request = (new ApiRequest())->withPsr7($psr7);

$matcher = new RegexMatcher();
$matched = $matcher->match($endpoint, $request);

Assert::type(ApiRequest::class, $matched);
Assert::same(TRUE, $matched->hasParameter('id'));
Assert::same('22', $matched->getParameter('id')->getValue());
});

0 comments on commit 595cc59

Please sign in to comment.