Skip to content

Commit e4c4048

Browse files
committed
Implemented #504
1 parent c1be198 commit e4c4048

File tree

3 files changed

+158
-3
lines changed

3 files changed

+158
-3
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ You can enable the following middleware using the "middlewares" config parameter
161161
- "validation": Return input validation errors for custom rules
162162
- "sanitation": Apply input sanitation on create and update
163163
- "multiTenancy": Restricts tenants access in a multi-tenant scenario
164+
- "pageLimits": Restricts list operations to become too heavy
164165
- "customization": Provides handlers for request and response customization
165166

166167
The "middlewares" config parameter is a comma separated list of enabled middlewares.
@@ -197,6 +198,8 @@ You can tune the middleware behavior using middleware specific configuration par
197198
- "validation.handler": Handler to implement validation rules for input values ("")
198199
- "sanitation.handler": Handler to implement sanitation rules for input values ("")
199200
- "multiTenancy.handler": Handler to implement simple multi-tenancy rules ("")
201+
- "pageLimits.pages": The maximum page number that may be requested ("100")
202+
- "pageLimits.records": The maximum number of records that a page may contain ("1000")
200203
- "customization.beforeHandler": Handler to implement request customization ("")
201204
- "customization.afterHandler": Handler to implement response customization ("")
202205

api.php

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3463,6 +3463,65 @@ public function handle(Request $request): Response
34633463
}
34643464
}
34653465

3466+
// file: src/Tqdev/PhpCrudApi/Middleware/PageLimitsMiddleware.php
3467+
3468+
class PageLimitsMiddleware extends Middleware
3469+
{
3470+
private $reflection;
3471+
3472+
public function __construct(Router $router, Responder $responder, array $properties, ReflectionService $reflection)
3473+
{
3474+
parent::__construct($router, $responder, $properties);
3475+
$this->reflection = $reflection;
3476+
$this->utils = new RequestUtils($reflection);
3477+
}
3478+
3479+
private function getMissingOrderParam(ReflectedTable $table): String
3480+
{
3481+
$pk = $table->getPk();
3482+
if (!$pk) {
3483+
$columnNames = $table->getColumnNames();
3484+
if (!$columnNames) {
3485+
return '';
3486+
}
3487+
return $columnNames[0];
3488+
}
3489+
return $pk->getName();
3490+
}
3491+
3492+
public function handle(Request $request): Response
3493+
{
3494+
$operation = $this->utils->getOperation($request);
3495+
if ($operation == 'list') {
3496+
$tableName = $request->getPathSegment(2);
3497+
$table = $this->reflection->getTable($tableName);
3498+
if ($table) {
3499+
$params = $request->getParams();
3500+
if (!isset($params['order']) || !$params['order']) {
3501+
$params['order'] = array($this->getMissingOrderParam($table));
3502+
}
3503+
$maxPage = (int) $this->getProperty('pages', '100');
3504+
if (isset($params['page']) && $params['page']) {
3505+
if (strpos($params['page'][0], ',') === false) {
3506+
$params['page'] = array(min($params['page'][0], $maxPage));
3507+
} else {
3508+
list($page, $size) = explode(',', $params['page'][0], 2);
3509+
$params['page'] = array(min($page, $maxPage) . ',' . $size);
3510+
}
3511+
}
3512+
$maxSize = (int) $this->getProperty('records', '1000');
3513+
if (!isset($params['size']) || !$params['size']) {
3514+
$params['size'] = array($maxSize);
3515+
} else {
3516+
$params['size'] = array(min($params['size'][0], $maxSize));
3517+
}
3518+
$request->setParams($params);
3519+
}
3520+
}
3521+
return $this->next->handle($request);
3522+
}
3523+
}
3524+
34663525
// file: src/Tqdev/PhpCrudApi/Middleware/SanitationMiddleware.php
34673526

34683527
class SanitationMiddleware extends Middleware
@@ -4464,7 +4523,7 @@ public function getPageOffset(array $params): int
44644523
return $offset;
44654524
}
44664525

4467-
public function getPageSize(array $params): int
4526+
private function getPageSize(array $params): int
44684527
{
44694528
$pageSize = $this->DEFAULT_PAGE_SIZE;
44704529
if (isset($params['page'])) {
@@ -4489,6 +4548,23 @@ public function getResultSize(array $params): int
44894548
return $numberOfRows;
44904549
}
44914550

4551+
public function getPageLimit(array $params): int
4552+
{
4553+
$pageLimit = -1;
4554+
if ($this->hasPage($params)) {
4555+
$pageLimit = $this->getPageSize($params);
4556+
}
4557+
$resultSize = $this->getResultSize($params);
4558+
if ($resultSize >= 0) {
4559+
if ($pageLimit >= 0) {
4560+
$pageLimit = min($pageLimit, $resultSize);
4561+
} else {
4562+
$pageLimit = $resultSize;
4563+
}
4564+
}
4565+
return $pageLimit;
4566+
}
4567+
44924568
}
44934569

44944570
// file: src/Tqdev/PhpCrudApi/Record/PathTree.php
@@ -4675,11 +4751,11 @@ public function _list(String $tableName, array $params): ListDocument
46754751
$columnOrdering = $this->ordering->getColumnOrdering($table, $params);
46764752
if (!$this->pagination->hasPage($params)) {
46774753
$offset = 0;
4678-
$limit = $this->pagination->getResultSize($params);
4754+
$limit = $this->pagination->getPageLimit($params);
46794755
$count = 0;
46804756
} else {
46814757
$offset = $this->pagination->getPageOffset($params);
4682-
$limit = $this->pagination->getPageSize($params);
4758+
$limit = $this->pagination->getPageLimit($params);
46834759
$count = $this->db->selectCount($table, $condition);
46844760
}
46854761
$records = $this->db->selectAll($table, $columnNames, $condition, $columnOrdering, $offset, $limit);
@@ -5082,6 +5158,9 @@ public function __construct(Config $config)
50825158
case 'xsrf':
50835159
new XsrfMiddleware($router, $responder, $properties);
50845160
break;
5161+
case 'pageLimits':
5162+
new PageLimitsMiddleware($router, $responder, $properties, $reflection);
5163+
break;
50855164
case 'customization':
50865165
new CustomizationMiddleware($router, $responder, $properties, $reflection);
50875166
break;
@@ -5428,6 +5507,11 @@ public function getParams(): array
54285507
return $this->params;
54295508
}
54305509

5510+
public function setParams(array $params) /*: void*/
5511+
{
5512+
$this->params = $params;
5513+
}
5514+
54315515
public function getBody() /*: ?array*/
54325516
{
54335517
return $this->body;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
namespace Tqdev\PhpCrudApi\Middleware;
3+
4+
use Tqdev\PhpCrudApi\Column\ReflectionService;
5+
use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
6+
use Tqdev\PhpCrudApi\Controller\Responder;
7+
use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
8+
use Tqdev\PhpCrudApi\Middleware\Router\Router;
9+
use Tqdev\PhpCrudApi\Record\RequestUtils;
10+
use Tqdev\PhpCrudApi\Request;
11+
use Tqdev\PhpCrudApi\Response;
12+
13+
class PageLimitsMiddleware extends Middleware
14+
{
15+
private $reflection;
16+
17+
public function __construct(Router $router, Responder $responder, array $properties, ReflectionService $reflection)
18+
{
19+
parent::__construct($router, $responder, $properties);
20+
$this->reflection = $reflection;
21+
$this->utils = new RequestUtils($reflection);
22+
}
23+
24+
private function getMissingOrderParam(ReflectedTable $table): String
25+
{
26+
$pk = $table->getPk();
27+
if (!$pk) {
28+
$columnNames = $table->getColumnNames();
29+
if (!$columnNames) {
30+
return '';
31+
}
32+
return $columnNames[0];
33+
}
34+
return $pk->getName();
35+
}
36+
37+
public function handle(Request $request): Response
38+
{
39+
$operation = $this->utils->getOperation($request);
40+
if ($operation == 'list') {
41+
$tableName = $request->getPathSegment(2);
42+
$table = $this->reflection->getTable($tableName);
43+
if ($table) {
44+
$params = $request->getParams();
45+
if (!isset($params['order']) || !$params['order']) {
46+
$params['order'] = array($this->getMissingOrderParam($table));
47+
}
48+
$maxPage = (int) $this->getProperty('pages', '100');
49+
if (isset($params['page']) && $params['page']) {
50+
if (strpos($params['page'][0], ',') === false) {
51+
$params['page'] = array(min($params['page'][0], $maxPage));
52+
} else {
53+
list($page, $size) = explode(',', $params['page'][0], 2);
54+
$params['page'] = array(min($page, $maxPage) . ',' . $size);
55+
}
56+
}
57+
$maxSize = (int) $this->getProperty('records', '1000');
58+
if (!isset($params['size']) || !$params['size']) {
59+
$params['size'] = array($maxSize);
60+
} else {
61+
$params['size'] = array(min($params['size'][0], $maxSize));
62+
}
63+
$request->setParams($params);
64+
}
65+
}
66+
return $this->next->handle($request);
67+
}
68+
}

0 commit comments

Comments
 (0)