Symptom:
----------
Trying to create an object sending a seemingly correct JSON request in a
POST message - was giving a 404 not found error. When looking at the cause
in the browser devtools - the body of the response contained "Not found (input)"
Cause:
-------
We were trying to validate parameters - even when the POST body
contained invalid JSON. We call json_decode($data) in retrieveInputs
The JSON request was malformed and it had omitted the double quotes.
example: { fieldName : "fieldValue" }
Expected behavior and fix:
---------------------------
A POST request with malformed JSON should get a 400 "bad request"
response. Even more helpful would be an error code indicating why the
JSON was bad.
If we call json_decode(), afterwards, we immediately call
json_last_error(), and if the return value does not match
JSON_ERROR_NONE, we send it across bundled in a 400 response, so that
the user may rectify the cause by looking at the exact error message.
The possible error messages right now are:
CODE CONSTANT-ERROR MESSAGE
0 JSON_ERROR_NONE-No error has occurred
1 JSON_ERROR_DEPTH-The maximum stack depth has been exceeded
2 JSON_ERROR_STATE_MISMATCH-Invalid or malformed JSON
3 JSON_ERROR_CTRL_CHAR-Control character error, possibly incorrectly encoded
4 JSON_ERROR_SYNTAX-Syntax error
5 JSON_ERROR_UTF8-Malformed UTF-8 characters, possibly incorrectly encoded
6 JSON_ERROR_RECURSION-One or more recursive references in the value to be encoded
7 JSON_ERROR_INF_OR_NAN-One or more NAN or INF values in the value to be encoded
8 JSON_ERROR_UNSUPPORTED_TYPE-A value of a type that cannot be encoded was given
9 JSON_ERROR_INVALID_PROPERTY_NAME-A property name that cannot be encoded was given
10 JSON_ERROR_UTF16-Malformed UTF-16 characters, possibly incorrectly encoded
After the fix:
-------------
On malformed JSON in the POST body - the following is returned:
The request could not be understood by the server due to malformed
syntax. The client SHOULD NOT repeat the request without modifications.
(Error decoding input JSON. json_last_error code: 4)
If REQUEST_METHOD is not set - we throw an exception that says something
like:
'Bad request (Error decoding input JSON. json_last_error code: 4)'
In order to accommodate this change in behavior - in the test suite, we
need a new method expectPattern (as the error code in the end of the
string may change, but the starting pattern stays the same - (/^Bad
request.*$/)
Issue:
When sending JSON in the body of a POST
request from an HTML form - for creating
a new record, a new record was being created,
but with null as the value for all the fields,
instead of the values supplied.
Cause:
The template text in the textarea field, in the
HTML form had some leading whitespace. On looking
through the source, json_decode is being called
only if the first character of the $data variable
is a '{' or a '['.
JSON Specification RFC4627
https://tools.ietf.org/html/rfc4627#section-2
says that insignificant
whitespace is allowed before or after any of the
six structural characters - '{','[',']','}',':',','
where whitespace is defined as:
ws = *(
%x20 / ; Space
%x09 / ; Horizontal tab
%x0A / ; Line feed or New line
%x0D ; Carriage return
)
Fix:
trim the above characters from the beginning and ending
of the received data before checking that the first
character is a '[' or '{'
Following our conversation [210](https://github.com/mevdschee/php-crud-api/pull/210)
The first issue is in the "applyBeforeHandler":
```php
$callback($action,$database,$table,$id,$inputs[$i]);
```
$id shoud be $ids[$i], you've probally missed that one :
```php
$callback($action,$database,$table,$ids[$i],$inputs[$i]);
```
The second issue will rise if you do 2 or more (soft)deletes: Like
```php
DELETE http://localhost/api.php/categories/1,2
```
In the second [loop](https://github.com/mevdschee/php-crud-api/blob/master/api.php#L1140-L1144) of the applyBeforeHandler, the $action is overwritten by the first loop.. so the in the second before call, the action is update.. so the 2nd one wil never be deleted
first:
```php
'before'=>function(&$cmd, $db, $tab, $id, &$in) {
$cmd;//delete
// then we do this
if ($cmd = 'delete') {
$cmd = 'update'; // change command to update
$in = (object) array(date('Y-m-d H:i:s', time()));
}
}
```
In the second call cmd is changed to update by the first call:
```php
'before'=>function(&$cmd, $db, $tab, $id, &$in) {
$cmd;//update
// so the code below never gets executed
if ($cmd = 'delete') {
$cmd = 'update'; // change command to update
$in = (object) array(date('Y-m-d H:i:s', time()));
}
}
```
I fixed this by storing the $action in a temp variable $origaction; and reset the $action in every start of the loop
```php
protected function applyBeforeHandler(&$action,&$database,&$table,&$ids,&$callback,&$inputs) {
if (is_callable($callback,true)) {
$max = count($ids)?:count($inputs);
$origaction = $action;
for ($i=0;$i<$max;$i++) {
$action = $origaction;
if (!isset($ids[$i])) $ids[$i] = false;
if (!isset($inputs[$i])) $inputs[$i] = false;
$callback($action,$database,$table,$ids[$i],$inputs[$i]);
}
}
}
```
Then the last error, which I allready pointed out before.
The call to the [applyBeforeHandler](https://github.com/mevdschee/php-crud-api/blob/master/api.php#L1888-L1890)
should be placed AFTER the [foreach](https://github.com/mevdschee/php-crud-api/blob/master/api.php#L1892-L1901)
Right now the [filterInputByFields ](https://github.com/mevdschee/php-crud-api/blob/master/api.php#L1893) filteres out the "delete" column which I just inserted in my "before" statement.
We may presume that the backend dev kwows what consequences his action have by changing input, so there's no need to call the applyInputTenancy, applyInputSanitizer, applyInputValidator after the applyBeforeHandler is called.
So we need to change [this](https://github.com/mevdschee/php-crud-api/blob/master/api.php#L1888-L1901) to:
```php
foreach ($inputs as $k=>$context) {
$input = $this->filterInputByFields($context,$fields[$tables[0]]);
if ($tenancy_function) $this->applyInputTenancy($tenancy_function,$action,$database,$tables[0],$input,$fields[$tables[0]]);
if ($input_sanitizer) $this->applyInputSanitizer($input_sanitizer,$action,$database,$tables[0],$input,$fields[$tables[0]]);
if ($input_validator) $this->applyInputValidator($input_validator,$action,$database,$tables[0],$input,$fields[$tables[0]],$context);
$this->convertInputs($input,$fields[$tables[0]]);
$inputs[$k] = $input;
}
if ($before) {
$this->applyBeforeHandler($action,$database,$tables[0],$key[0],$before,$inputs);
}
```
Barry
Hi Maurits,
I needed a callback function that got triggered on a new db entry (Create).
For example I wanted to send an e-mail to a newly registered member.
In this example I have a "members" table with the columns: id , firstName, lastName, email
the yourEmailFunction method will send a welcome message to the newly entered member.
```php
$api = new PHP_CRUD_API(array(
'after_create'=>function($db,$tab,$row) {
if ($tab == 'members')
yourEmailFunction($row['firstName'],$row['lastName'],$row['email']);
}
}
));
$api->executeCommand();
```
Tell me you thoughts... maybe if you like it and willing to accept the pull request I can also create an after_delete and after_update method.