Skip to content

Commit

Permalink
Merge branch 'master' of github.com:mevdschee/php-crud-api
Browse files Browse the repository at this point in the history
  • Loading branch information
mevdschee committed Oct 31, 2018
2 parents ba2c841 + 6d9d724 commit 60dcde4
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 145 deletions.
57 changes: 36 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ There are also proof-of-concept ports of this script that only support basic RES
- PostGIS 2.0 or higher for spatial features in PostgreSQL 9.1 or higher
- SQL Server 2012 or higher (2017 for Linux support)

## Known issues

- Seeing integers as strings? Make sure to enable the `nd_pdo_mysql` extension and disable `pdo_mysql`.

## Installation

This is a single file application! Upload "`api.php`" somewhere and enjoy!
Expand Down Expand Up @@ -105,7 +109,7 @@ These features match features in v1 (see branch "v1"):
- [x] Supports POST variables as input (x-www-form-urlencoded)
- [x] Supports a JSON object as input
- [x] Supports a JSON array as input (batch insert)
- [x] Supports file upload from web forms (multipart/form-data)
- [ ] ~~Supports file upload from web forms (multipart/form-data)~~
- [ ] ~~Condensed JSON output: first row contains field names~~
- [x] Sanitize and validate input using callbacks
- [x] Permission system for databases, tables, columns and records
Expand Down Expand Up @@ -734,26 +738,7 @@ The above example will add a header "X-Time-Taken" with the number of seconds th

### File uploads

The 'fileUpload' middleware allows you to upload a file using a web form (multipart/form-data) like this:

```
<form method="post" action="http://localhost/api.php/records/categories" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="icon">
<input type="submit">
</form>
```

Then this is handled as if you would have sent:

```
POST http://localhost/api.php/records/categories
{"icon_name":"not.gif","icon_type":"image\/gif","icon":"ZGF0YQ==","icon_error":0,"icon_size":4}
```

As you can see the "xxx_name", "xxx_type", "xxx_error" and "xxx_size" meta fields are added (where "xxx" is the name of the file field).

NB: You cannot edit a file using this method, because browsers do not support the "PUT" method in these forms.
File uploads are supported through the [FileReader API](https://caniuse.com/#feat=filereader).

## OpenAPI specification

Expand Down Expand Up @@ -877,6 +862,36 @@ To run the functional tests locally you may run the following command:
This runs the functional tests from the "tests" directory. It uses the database dumps (fixtures) and
database configuration (config) from the corresponding subdirectories.

## Nginx config example
```
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm index.nginx-debian.html;
server_name server_domain_or_IP;
location / {
try_files $uri $uri/ =404;
}
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
try_files $fastcgi_script_name =404;
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
```

### Docker

Install docker using the following commands and then logout and login for the changes to take effect:
Expand Down
78 changes: 29 additions & 49 deletions api.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function __construct(String $prefix, String $config)
$this->memcache->addServer($address, $port);
}

protected function create(): stdClass
protected function create() /*: \Memcache*/
{
return new \Memcache();
}
Expand All @@ -100,7 +100,7 @@ public function clear(): bool

class MemcachedCache extends MemcacheCache
{
protected function create(): stdClass
protected function create() /*: \Memcached*/
{
return new \Memcached();
}
Expand Down Expand Up @@ -3188,35 +3188,6 @@ public function handle(Request $request): Response
}
}

// file: src/Tqdev/PhpCrudApi/Middleware/FileUploadMiddleware.php

class FileUploadMiddleware extends Middleware
{
public function handle(Request $request): Response
{
$files = $request->getUploadedFiles();
if (!empty($files)) {
$body = $request->getBody();
foreach ($files as $fieldName => $file) {
if (isset($file['error']) && $file['error']) {
return $this->responder->error(ErrorCode::FILE_UPLOAD_FAILED, $fieldName);
}
foreach ($file as $key => $value) {
if ($key == 'tmp_name') {
$value = base64_encode(file_get_contents($value));
$key = $fieldName;
} else {
$key = $fieldName . '_' . $key;
}
$body->$key = $value;
}
}
$request->setBody($body);
}
return $this->next->handle($request);
}
}

// file: src/Tqdev/PhpCrudApi/Middleware/FirewallMiddleware.php

class FirewallMiddleware extends Middleware
Expand Down Expand Up @@ -3266,6 +3237,30 @@ public function handle(Request $request): Response
}
}

// file: src/Tqdev/PhpCrudApi/Middleware/FormMiddleware.php

class FormMiddleware extends Middleware
{
public function handle(Request $request): Response
{
$body = $request->getBody();
if (!$body) {
$body = file_get_contents('php://input');
if ($body) {
parse_str($body, $input);
foreach ($input as $key => $value) {
if (substr($key, -9) == '__is_null') {
$input[substr($key, 0, -9)] = null;
unset($input[$key]);
}
}
$request->setBody((object) $input);
}
}
return $this->next->handle($request);
}
}

// file: src/Tqdev/PhpCrudApi/Middleware/JwtAuthMiddleware.php

class JwtAuthMiddleware extends Middleware
Expand Down Expand Up @@ -5070,9 +5065,6 @@ public function __construct(Config $config)
case 'jwtAuth':
new JwtAuthMiddleware($router, $responder, $properties);
break;
case 'fileUpload':
new FileUploadMiddleware($router, $responder, $properties);
break;
case 'validation':
new ValidationMiddleware($router, $responder, $properties, $reflection);
break;
Expand Down Expand Up @@ -5405,17 +5397,10 @@ private function decodeBody(String $body) /*: ?object*/

private function parseBody(String $body = null) /*: void*/
{
if ($body) {
$object = $this->decodeBody($body);
} else {
if (!empty($_FILES)) {
$object = (object) $_POST;
} else {
$input = file_get_contents('php://input');
$object = $this->decodeBody($input);
}
if (!$body) {
$body = file_get_contents('php://input');
}
$this->body = $object;
$this->body = $this->decodeBody($body);
}

public function getMethod(): String
Expand Down Expand Up @@ -5493,11 +5478,6 @@ public static function fromString(String $request): Request
}
return new Request($method, $path, $query, $headers, $body);
}

public function getUploadedFiles(): array
{
return $_FILES;
}
}

// file: src/Tqdev/PhpCrudApi/Response.php
Expand Down
12 changes: 0 additions & 12 deletions examples/clients/upload/form.html

This file was deleted.

4 changes: 0 additions & 4 deletions src/Tqdev/PhpCrudApi/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use Tqdev\PhpCrudApi\Middleware\BasicAuthMiddleware;
use Tqdev\PhpCrudApi\Middleware\CorsMiddleware;
use Tqdev\PhpCrudApi\Middleware\CustomizationMiddleware;
use Tqdev\PhpCrudApi\Middleware\FileUploadMiddleware;
use Tqdev\PhpCrudApi\Middleware\FirewallMiddleware;
use Tqdev\PhpCrudApi\Middleware\JwtAuthMiddleware;
use Tqdev\PhpCrudApi\Middleware\MultiTenancyMiddleware;
Expand Down Expand Up @@ -60,9 +59,6 @@ public function __construct(Config $config)
case 'jwtAuth':
new JwtAuthMiddleware($router, $responder, $properties);
break;
case 'fileUpload':
new FileUploadMiddleware($router, $responder, $properties);
break;
case 'validation':
new ValidationMiddleware($router, $responder, $properties, $reflection);
break;
Expand Down
2 changes: 1 addition & 1 deletion src/Tqdev/PhpCrudApi/Cache/MemcacheCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function __construct(String $prefix, String $config)
$this->memcache->addServer($address, $port);
}

protected function create(): stdClass
protected function create() /*: \Memcache*/
{
return new \Memcache();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Tqdev/PhpCrudApi/Cache/MemcachedCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class MemcachedCache extends MemcacheCache
{
protected function create(): stdClass
protected function create() /*: \Memcached*/
{
return new \Memcached();
}
Expand Down
33 changes: 0 additions & 33 deletions src/Tqdev/PhpCrudApi/Middleware/FileUploadMiddleware.php

This file was deleted.

19 changes: 10 additions & 9 deletions src/Tqdev/PhpCrudApi/Record/Condition/Condition.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public static function fromString(ReflectedTable $table, String $value): Conditi
if (count($parts) < 2) {
return null;
}
if (count($parts) < 3) {
$parts[2] = '';
}
$field = $table->getColumn($parts[0]);
$command = $parts[1];
$negate = false;
Expand All @@ -47,15 +50,13 @@ public static function fromString(ReflectedTable $table, String $value): Conditi
$command = substr($command, 1);
}
}
if (count($parts) == 3 || (count($parts) == 2 && in_array($command, ['ic', 'is', 'iv']))) {
if ($spatial) {
if (in_array($command, ['co', 'cr', 'di', 'eq', 'in', 'ov', 'to', 'wi', 'ic', 'is', 'iv'])) {
$condition = new SpatialCondition($field, $command, $parts[2]);
}
} else {
if (in_array($command, ['cs', 'sw', 'ew', 'eq', 'lt', 'le', 'ge', 'gt', 'bt', 'in', 'is'])) {
$condition = new ColumnCondition($field, $command, $parts[2]);
}
if ($spatial) {
if (in_array($command, ['co', 'cr', 'di', 'eq', 'in', 'ov', 'to', 'wi', 'ic', 'is', 'iv'])) {
$condition = new SpatialCondition($field, $command, $parts[2]);
}
} else {
if (in_array($command, ['cs', 'sw', 'ew', 'eq', 'lt', 'le', 'ge', 'gt', 'bt', 'in', 'is'])) {
$condition = new ColumnCondition($field, $command, $parts[2]);
}
}
if ($negate) {
Expand Down
18 changes: 3 additions & 15 deletions src/Tqdev/PhpCrudApi/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,10 @@ private function decodeBody(String $body) /*: ?object*/

private function parseBody(String $body = null) /*: void*/
{
if ($body) {
$object = $this->decodeBody($body);
} else {
if (!empty($_FILES)) {
$object = (object) $_POST;
} else {
$input = file_get_contents('php://input');
$object = $this->decodeBody($input);
}
if (!$body) {
$body = file_get_contents('php://input');
}
$this->body = $object;
$this->body = $this->decodeBody($body);
}

public function getMethod(): String
Expand Down Expand Up @@ -187,9 +180,4 @@ public static function fromString(String $request): Request
}
return new Request($method, $path, $query, $headers, $body);
}

public function getUploadedFiles(): array
{
return $_FILES;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ Content-Type: application/json
Content-Length: 94

{"records":[{"id":1,"name":"announcement","icon":null},{"id":2,"name":"article","icon":null}]}
===
GET /records/categories?filter=icon,is
===
200
Content-Type: application/json
Content-Length: 94

{"records":[{"id":1,"name":"announcement","icon":null},{"id":2,"name":"article","icon":null}]}

0 comments on commit 60dcde4

Please sign in to comment.