Skip to content

Commit 4134485

Browse files
author
MarcFRICOU
committed
feat(scan): basic scan
1 parent c473a55 commit 4134485

File tree

5 files changed

+308
-0
lines changed

5 files changed

+308
-0
lines changed

README.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,124 @@
11
# php-swaggerize-fastroute-library
22
A library to automatically create FastRoute routes based on swagger JSON documentation
3+
# Install
4+
To install with composer:
5+
```
6+
composer require iadvize/php-swaggerize-fastroute-library
7+
```
8+
# Dispatch swagger app
9+
10+
```
11+
<?php
12+
13+
require '/path/to/vendor/autoload.php';
14+
15+
$lumenOperationParser = new \Iadvize\SwaggerizeFastRoute\OperationParser\LumenControllerOperationParser('Controllers\\Namespace\\');
16+
17+
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
18+
\Iadvize\SwaggerizeFastRoute\scan('https://raw.githubusercontent.com/wordnik/swagger-spec/master/examples/v2.0/json/petstore.json', $r, $lumenOperationParser);
19+
});
20+
21+
// Fetch method and URI from somewhere
22+
$httpMethod = $_SERVER['REQUEST_METHOD'];
23+
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
24+
25+
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
26+
switch ($routeInfo[0]) {
27+
case FastRoute\Dispatcher::NOT_FOUND:
28+
// ... 404 Not Found
29+
break;
30+
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
31+
$allowedMethods = $routeInfo[1];
32+
// ... 405 Method Not Allowed
33+
break;
34+
case FastRoute\Dispatcher::FOUND:
35+
$handler = $routeInfo[1];
36+
$vars = $routeInfo[2];
37+
// ... call $handler with $vars
38+
break;
39+
}
40+
```
41+
# Use a cache dispatcher
42+
43+
Parse swagger JSON and create route dynamically at each API call can be heavy. If you need performance, use FastRoute cachedDispatcher
44+
45+
```
46+
<?php
47+
48+
require '/path/to/vendor/autoload.php';
49+
50+
$lumenOperationParser = new \Iadvize\SwaggerizeFastRoute\OperationParser\LumenControllerOperationParser('Controllers\\Namespace\\');
51+
52+
$dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $r, ['cacheFile' => 'path/to/cache/file']) {
53+
\Iadvize\SwaggerizeFastRoute\scan('https://raw.githubusercontent.com/wordnik/swagger-spec/master/examples/v2.0/json/petstore.json', $r, $lumenOperationParser);
54+
});
55+
56+
// Fetch method and URI from somewhere
57+
$httpMethod = $_SERVER['REQUEST_METHOD'];
58+
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
59+
60+
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
61+
switch ($routeInfo[0]) {
62+
case FastRoute\Dispatcher::NOT_FOUND:
63+
// ... 404 Not Found
64+
break;
65+
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
66+
$allowedMethods = $routeInfo[1];
67+
// ... 405 Method Not Allowed
68+
break;
69+
case FastRoute\Dispatcher::FOUND:
70+
$handler = $routeInfo[1];
71+
$vars = $routeInfo[2];
72+
// ... call $handler with $vars
73+
break;
74+
}
75+
```
76+
77+
# How handler is formed
78+
79+
Handlers are formed from route defined in swagger as [Lumen](http://lumen.laravel.com/docs/routing#named-routes) define it for controller class : `Controller@method`
80+
### Controller class generation
81+
82+
Controller class is determined from path route with first character uppercased and with Controller at the end of file name
83+
84+
This swagger JSON :
85+
86+
```JSON
87+
{
88+
// ...
89+
"paths": {
90+
"/pets": {
91+
"get": {
92+
// ...
93+
}
94+
"put": {
95+
// ...
96+
}
97+
}
98+
"/store": {
99+
"post": {
100+
// ...
101+
}
102+
}
103+
}
104+
// ...
105+
}
106+
```
107+
108+
will generates respectively this handlers:
109+
110+
* `PetsController@get`
111+
* `PetsController@update`
112+
* `StoreController@create`
113+
114+
### Method generation
115+
116+
Controller method is mapped from HTTP method :
117+
* `GET` => `get`,
118+
* `POST` => `create`,
119+
* `PUT` => `update`,
120+
* `HEAD` => `head`,
121+
* `OPTIONS` => `options`,
122+
* `PATCH` => `patch`,
123+
* `DELETE` => `delete`,
124+
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace Iadvize\SwaggerizeFastRoute\OperationParser;
4+
5+
use Swagger\OperationReference;
6+
7+
/**
8+
* Class LumenControllerOperationParser
9+
*
10+
* @package Iadvize\SwaggerizeFastRoute\OperationParser
11+
*/
12+
class LumenControllerOperationParser implements OperationParserInterface
13+
{
14+
15+
/** @var string */
16+
protected $namespace;
17+
18+
/** @var array */
19+
private $httpVerbToControllerMethod = [
20+
'GET' => 'get',
21+
'POST' => 'create',
22+
'PUT' => 'update',
23+
'HEAD' => 'head',
24+
'OPTIONS' => 'options',
25+
'PATCH' => 'patch',
26+
'DELETE' => 'delete',
27+
];
28+
29+
/**
30+
* Constructor
31+
*
32+
* @param string $controllerNamespace
33+
*/
34+
public function __construct($controllerNamespace)
35+
{
36+
if (strpos($controllerNamespace, '\\') === strlen($controllerNamespace) - 1) {
37+
$controllerNamespace = mb_substr($controllerNamespace, 0, -1);
38+
}
39+
40+
$this->namespace = $controllerNamespace;
41+
}
42+
43+
/**
44+
* Get Handler
45+
*
46+
* @param OperationReference $operation
47+
*
48+
* @return array
49+
*/
50+
public function getHandler(OperationReference $operation)
51+
{
52+
// remove route parameters
53+
$path = preg_replace('/\/\{.*\}/', '', $operation->getPath());
54+
55+
// lowerCamelCase to UpperCamelCase
56+
$paths = explode('/', $path);
57+
// path start with a /
58+
unset($paths[0]);
59+
foreach ($paths as &$path) {
60+
$path[0] = strtoupper($path[0]);
61+
}
62+
// path to 'relative' namespace
63+
$path = implode('\\', $paths);
64+
65+
$controller = $this->namespace . $path . 'Controller';
66+
67+
return ['uses' => $controller . '@' . $this->httpVerbToControllerMethod[strtoupper($operation->getMethod())], 'as' => $operation->getOperationId()];
68+
}
69+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Iadvize\SwaggerizeFastRoute\OperationParser;
4+
5+
use Swagger\OperationReference;
6+
7+
/**
8+
* Interface OperationParserInterface
9+
*
10+
* @package Iadvize\SwaggerizeFastRoute\OperationParser
11+
*/
12+
interface OperationParserInterface
13+
{
14+
/**
15+
* Get Handler
16+
*
17+
* @param OperationReference $operation
18+
*
19+
* @return array
20+
*/
21+
public function getHandler(OperationReference $operation);
22+
}

src/functions.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Iadvize\SwaggerizeFastRoute;
4+
5+
use FastRoute\RouteCollector;
6+
use Iadvize\SwaggerizeFastRoute\OperationParser\OperationParserInterface;
7+
use Swagger\Document;
8+
use Swagger\OperationReference;
9+
10+
/**
11+
* Scan the json file and build routes
12+
*
13+
* @param string $file The file path
14+
* @param RouteCollector $routeCollector Route collector
15+
* @param OperationParserInterface $operationParser Operation parser
16+
*/
17+
function scan($file, RouteCollector $routeCollector, OperationParserInterface $operationParser)
18+
{
19+
$json = file_get_contents($file);
20+
21+
$swagger = json_decode($json);
22+
23+
$document = new Document($swagger);
24+
25+
/** @var OperationReference[] $operations */
26+
$operations = $document->getOperationsById();
27+
28+
foreach ($operations as $operation) {
29+
$routeCollector->addRoute($operation->getMethod(), $operation->getPath(), $operationParser->getHandler($operation));
30+
}
31+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace IadvizeTest\OperationParser;
4+
5+
use Iadvize\SwaggerizeFastRoute\OperationParser\LumenControllerOperationParser;
6+
use Iadvize\SwaggerizeFastRoute\OperationParser\OperationParserInterface;
7+
use Swagger\OperationReference;
8+
9+
/**
10+
* Test LumenControllerOperationParser
11+
*
12+
*/
13+
class LumenControllerOperationParserTest extends \PHPUnit_Framework_TestCase
14+
{
15+
/** @var OperationParserInterface */
16+
protected $operationParser;
17+
18+
/**
19+
* Setup
20+
*/
21+
protected function setUp()
22+
{
23+
$this->operationParser = new LumenControllerOperationParser('Iadvize\Test\\');
24+
}
25+
26+
/**
27+
* Test: Get handler should return an Lumen compatible Array with controller@method
28+
*/
29+
public function testGetHandler()
30+
{
31+
$operationMock = \Mockery::mock(OperationReference::class);
32+
$operationMock->shouldReceive('getPath')->andReturn('/marco');
33+
$operationMock->shouldReceive('getMethod')->andReturn('GET');
34+
$operationMock->shouldReceive('getOperationId')->andReturn('operationID');
35+
36+
$this->assertEquals(['uses' => 'Iadvize\Test\MarcoController@get', 'as' => 'operationID'], $this->operationParser->getHandler($operationMock));
37+
}
38+
39+
/**
40+
* Test: Get handler should return an Lumen compatible Array with controller@method
41+
*/
42+
public function testGetHandlerWithRouteParameter()
43+
{
44+
$operationMock = \Mockery::mock(OperationReference::class);
45+
$operationMock->shouldReceive('getPath')->andReturn('/marco/{id}');
46+
$operationMock->shouldReceive('getMethod')->andReturn('PUT');
47+
$operationMock->shouldReceive('getOperationId')->andReturn('operationID');
48+
49+
$this->assertEquals(['uses' => 'Iadvize\Test\MarcoController@update', 'as' => 'operationID'], $this->operationParser->getHandler($operationMock));
50+
}
51+
52+
/**
53+
* Test: Get handler should return an Lumen compatible Array with controller@method
54+
*/
55+
public function testGetHandlerWithDeepPath()
56+
{
57+
$operationMock = \Mockery::mock(OperationReference::class);
58+
$operationMock->shouldReceive('getPath')->andReturn('/marco/{id}/name/first');
59+
$operationMock->shouldReceive('getMethod')->andReturn('POST');
60+
$operationMock->shouldReceive('getOperationId')->andReturn('operationID');
61+
62+
$this->assertEquals(['uses' => 'Iadvize\Test\Marco\Name\FirstController@create', 'as' => 'operationID'], $this->operationParser->getHandler($operationMock));
63+
}
64+
}

0 commit comments

Comments
 (0)