api de gestion de ticket, basé sur php-crud-api. Le but est de décorrélé les outils de gestion des données, afin
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

ValidationMiddleware.php 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. namespace Tqdev\PhpCrudApi\Middleware;
  3. use Psr\Http\Message\ResponseInterface;
  4. use Psr\Http\Message\ServerRequestInterface;
  5. use Psr\Http\Server\RequestHandlerInterface;
  6. use Tqdev\PhpCrudApi\Column\ReflectionService;
  7. use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
  8. use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
  9. use Tqdev\PhpCrudApi\Controller\Responder;
  10. use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
  11. use Tqdev\PhpCrudApi\Middleware\Router\Router;
  12. use Tqdev\PhpCrudApi\Record\ErrorCode;
  13. use Tqdev\PhpCrudApi\RequestUtils;
  14. class ValidationMiddleware extends Middleware
  15. {
  16. private $reflection;
  17. public function __construct(Router $router, Responder $responder, array $properties, ReflectionService $reflection)
  18. {
  19. parent::__construct($router, $responder, $properties);
  20. $this->reflection = $reflection;
  21. }
  22. private function callHandler($handler, $record, string $operation, ReflectedTable $table) /*: ResponseInterface?*/
  23. {
  24. $context = (array) $record;
  25. $details = array();
  26. $tableName = $table->getName();
  27. foreach ($context as $columnName => $value) {
  28. if ($table->hasColumn($columnName)) {
  29. $column = $table->getColumn($columnName);
  30. $valid = call_user_func($handler, $operation, $tableName, $column->serialize(), $value, $context);
  31. if ($valid === true || $valid === '') {
  32. $valid = $this->validateType($table, $column, $value);
  33. }
  34. if ($valid !== true && $valid !== '') {
  35. $details[$columnName] = $valid;
  36. }
  37. }
  38. }
  39. if (count($details) > 0) {
  40. return $this->responder->error(ErrorCode::INPUT_VALIDATION_FAILED, $tableName, $details);
  41. }
  42. return null;
  43. }
  44. private function validateType(ReflectedTable $table, ReflectedColumn $column, $value)
  45. {
  46. $tables = $this->getArrayProperty('tables', 'all');
  47. $types = $this->getArrayProperty('types', 'all');
  48. if (
  49. (in_array('all', $tables) || in_array($table->getName(), $tables)) &&
  50. (in_array('all', $types) || in_array($column->getType(), $types))
  51. ) {
  52. if (is_null($value)) {
  53. return ($column->getNullable() ? true : "cannot be null");
  54. }
  55. if (is_string($value)) {
  56. // check for whitespace
  57. switch ($column->getType()) {
  58. case 'varchar':
  59. case 'clob':
  60. break;
  61. default:
  62. if (strlen(trim($value)) != strlen($value)) {
  63. return 'illegal whitespace';
  64. }
  65. break;
  66. }
  67. // try to parse
  68. switch ($column->getType()) {
  69. case 'integer':
  70. case 'bigint':
  71. if (
  72. filter_var($value, FILTER_SANITIZE_NUMBER_INT) !== $value ||
  73. filter_var($value, FILTER_VALIDATE_INT) === false
  74. ) {
  75. return 'invalid integer';
  76. }
  77. break;
  78. case 'decimal':
  79. if (strpos($value, '.') !== false) {
  80. list($whole, $decimals) = explode('.', $value, 2);
  81. } else {
  82. list($whole, $decimals) = array($value, '');
  83. }
  84. if (strlen($whole) > 0 && !ctype_digit($whole)) {
  85. return 'invalid decimal';
  86. }
  87. if (strlen($decimals) > 0 && !ctype_digit($decimals)) {
  88. return 'invalid decimal';
  89. }
  90. if (strlen($whole) > $column->getPrecision() - $column->getScale()) {
  91. return 'decimal too large';
  92. }
  93. if (strlen($decimals) > $column->getScale()) {
  94. return 'decimal too precise';
  95. }
  96. break;
  97. case 'float':
  98. case 'double':
  99. if (
  100. filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT) !== $value ||
  101. filter_var($value, FILTER_VALIDATE_FLOAT) === false
  102. ) {
  103. return 'invalid float';
  104. }
  105. break;
  106. case 'boolean':
  107. if (!in_array(strtolower($value), array('true', 'false'))) {
  108. return 'invalid boolean';
  109. }
  110. break;
  111. case 'date':
  112. if (date_create_from_format('Y-m-d', $value) === false) {
  113. return 'invalid date';
  114. }
  115. break;
  116. case 'time':
  117. if (date_create_from_format('H:i:s', $value) === false) {
  118. return 'invalid time';
  119. }
  120. break;
  121. case 'timestamp':
  122. if (date_create_from_format('Y-m-d H:i:s', $value) === false) {
  123. return 'invalid timestamp';
  124. }
  125. break;
  126. case 'clob':
  127. case 'varchar':
  128. if ($column->hasLength() && mb_strlen($value, 'UTF-8') > $column->getLength()) {
  129. return 'string too long';
  130. }
  131. break;
  132. case 'blob':
  133. case 'varbinary':
  134. if (base64_decode($value, true) === false) {
  135. return 'invalid base64';
  136. }
  137. if ($column->hasLength() && strlen(base64_decode($value)) > $column->getLength()) {
  138. return 'string too long';
  139. }
  140. break;
  141. case 'geometry':
  142. // no checks yet
  143. break;
  144. }
  145. } else { // check non-string types
  146. switch ($column->getType()) {
  147. case 'integer':
  148. case 'bigint':
  149. if (!is_int($value)) {
  150. return 'invalid integer';
  151. }
  152. break;
  153. case 'float':
  154. case 'double':
  155. if (!is_float($value) && !is_int($value)) {
  156. return 'invalid float';
  157. }
  158. break;
  159. case 'boolean':
  160. if (!is_bool($value) && ($value !== 0) && ($value !== 1)) {
  161. return 'invalid boolean';
  162. }
  163. break;
  164. default:
  165. return 'invalid ' . $column->getType();
  166. }
  167. }
  168. // extra checks
  169. switch ($column->getType()) {
  170. case 'integer': // 4 byte signed
  171. $value = filter_var($value, FILTER_VALIDATE_INT);
  172. if ($value > 2147483647 || $value < -2147483648) {
  173. return 'invalid integer';
  174. }
  175. break;
  176. }
  177. }
  178. return (true);
  179. }
  180. public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
  181. {
  182. $operation = RequestUtils::getOperation($request);
  183. if (in_array($operation, ['create', 'update', 'increment'])) {
  184. $tableName = RequestUtils::getPathSegment($request, 2);
  185. if ($this->reflection->hasTable($tableName)) {
  186. $record = $request->getParsedBody();
  187. if ($record !== null) {
  188. $handler = $this->getProperty('handler', '');
  189. if ($handler !== '') {
  190. $table = $this->reflection->getTable($tableName);
  191. if (is_array($record)) {
  192. foreach ($record as $r) {
  193. $response = $this->callHandler($handler, $r, $operation, $table);
  194. if ($response !== null) {
  195. return $response;
  196. }
  197. }
  198. } else {
  199. $response = $this->callHandler($handler, $record, $operation, $table);
  200. if ($response !== null) {
  201. return $response;
  202. }
  203. }
  204. }
  205. }
  206. }
  207. }
  208. return $next->handle($request);
  209. }
  210. }