fuel / core

Fuel PHP Framework - The core of the Fuel v1 framework
http://fuelphp.com
813 stars 345 forks source link

is_json check wrong! #1606

Closed ve3 closed 10 years ago

ve3 commented 10 years ago

let's say i have data from db as string.

$phone = '021234567';

and some other data from db in other row as json.

$testjson = '{"1":"a2","a2":{"1":"a2.2","2":"a2.2"}}';

_isjson() return true on $phone and $testjson that is wrong! $phone is string. not integer, not number, not json. it should return false.

let's try it yourself.

$phone = '021234567';
var_dump(\Str::is_json($phone));
WanWizard commented 10 years ago

That returns false here (used 1.8/develop to test).

ve3 commented 10 years ago

i use 1.7.1

WanWizard commented 10 years ago

Returns false here too.

That method was added in August of 2012, and hasn't changed since.

WanWizard commented 10 years ago

What is the result of

$string = '021234567';
var_dump(json_decode($string), json_last_error(), json_last_error() === JSON_ERROR_NONE);

should be

null
4
false
ve3 commented 10 years ago
$string = '021234567';
var_dump(json_decode($string), json_last_error(), json_last_error() === JSON_ERROR_NONE);

i got

int 21234567

int 0

boolean true

what do i have to config in php.ini or something?

windows 7 wamp server php.ini default values except show all error. php 5.3.25

edit: my localhost my real domain http://yenped.com/checkjson.php and many many server i tested return true. i think your server must be special or something configured with not default value.

WanWizard commented 10 years ago

that is odd.

In PHP 5.2.x, that is the correct behaviour, but that changed in PHP 5.3.

What defines the json_decode() method in your installation? Is it compiled in, or is it using the PECL extension (i.e. loaded as a module)?

ve3 commented 10 years ago

@WanWizard i have no idea. i just install wamp and config php.ini to show all error+notice that's it. all other things are come with the package by default.

even on the real servers it return same result.

WanWizard commented 10 years ago

Been testing a bit with what I can find here:

So it's probably behaviour that has changed in PHP 5.5, versions before that seen to consider a string value valid, even if it is not enclosed in double quotes. In 5.5+, only $s = '"01234"'; will decode properly.

WanWizard commented 10 years ago

The PHP manual states:

PHP implements a superset of JSON - it will also encode and decode scalar types and NULL.
The JSON standard only supports these values when they are nested inside an array or an object. 

So this behaviour is correct, and as far as PHP is concerned, it is valid JSON. Which means that although it's not a problem for PHP, it might be a problem if your JSON has to be interpreted by something that does require a string according to the JSON spec.

I don't have an immediate solution (other then building a complete validator in), but I'll leave this open for the moment.

ve3 commented 10 years ago

My code to check valid json encoded format. If this is good for you, please add to FuelPHP. (You can rename function or just copy some of the code. it's totally free for all)

public static function isJsonFormat($str) 
{
    if (is_array($str) || is_object($str)) {
        return false;
    }

    $result = \Str::is_json($str);

    if ($result === true) {
        if (preg_match('/{([^}]*)}/', $str) === 1 || preg_match('/"([^"]+)"/', $str) === 1) {
            return true;
        } else {
            // if input value is null or boolean or boolean string (json encoded).
            if (
                is_null(json_decode($str)) || 
                (is_bool(json_decode($str)) || is_bool($str))
            ) {
                return true;
            }

            // if input value is number. make very sure that it is equal string length. (0123 is not equal to 123 which 0123 (string) is not valid json encoded)
            if (is_numeric($str) && mb_strlen($str) == mb_strlen(json_decode($str))) {
                return true;
            }

            return false;
        }
    }

    return $result;
}

Why i have to check again for the valid format of json encoded? Let see these cases. (from http://stackoverflow.com/questions/20888731/how-to-check-json-encoded-string)

integer value

$var = 021234567;
echo $var; // 4536695
echo json_encode($var); // 4536695
echo json_decode(4536695); // 4536695
echo json_decode($var); // 4536695

$var = 21234567;
echo $var; // 21234567
echo json_encode($var); // 21234567 (string)
echo json_decode(4536695); // 21234567 (integer)
echo json_decode($var); // 21234567 (integer)

If integer is zero leading when the json encode it, it returns other integer that cannot be decode back to original. If integer is not zero leading, json encode or decode change nothing.

string value

$var = '021234567';
echo $var; // 021234567
echo json_encode($var); // "021234567"
echo json_decode('"021234567"'); // 021234567
echo json_decode($var); // 21234567 (int)

// example if string is text with double quote in it.
$var = 'i"m sam';
echo $var; // i"m sam
echo json_encode($var); // "i\"m sam"
echo json_decode('"i\"m sam"'); // i"m sam
echo json_decode($var); // null

If string when json_encode it must have double quote " at the open and close value. If you json_decode the text string without double quote it should return null. If you json_decode the number only string ('021234567', '21234567') it should return equal string length after decode ('0123' != 123).

The string that has only number and without double quote at open and close is not valid json encoded format. If you have ever see any case that require [zero leading number in string type ('01234567')] to be a valid json encoded format, please show me an example.

array value

$var = array('1a', '2', 'hb' => 'bh', 'arr' => array('a', 'b', 'c'));
echo json_encode($var); // {"0":"1a","1":"2","hb":"bh","arr":["a","b","c"]}
echo json_decode('{"0":"1a","1":"2","hb":"bh","arr":["a","b","c"]}'); // stdClass Object ( [0] => 1a [1] => 2 [hb] => bh [arr] => Array ( [0] => a [1] => b [2] => c ) )

json encoded array must have curly bracket {} at the open and close value. json_decode can not decode php array value. If you check \Str::is_json($var); it may throw error. My function prevent this error by return false.

object value

$var = new stdClass(); $var->prop1 = 'val1'; $var->prop2 = 'val2'; $var->aa = new stdClass; $var->aa->prop1 = 'aa val1';
echo json_encode($var); // {"prop1":"val1","prop2":"val2","aa":{"prop1":"aa val1"}}
echo json_decode('{"prop1":"val1","prop2":"val2","aa":{"prop1":"aa val1"}}'); // stdClass Object ( [prop1] => val1 [prop2] => val2 [aa] => stdClass Object ( [prop1] => aa val1 ) ) 

In this case, json encoded object must have curly bracket {} at the open and close value. json_decode can not decode php object value. If you check \Str::is_json($var); it may throw error too. My function prevent this error by return false.

boolean (true, false) and null value

Both boolean and null check with \Str::is_json($var); returns true which is already correct.


All my test values for try it yourself.

//$var = false;
//$var = true;
//$var = null;
//$var = '021234567';
//$var = '21234567';
//$var = '"021234567"';
//$var = "'021234567'";
//$var = 021234567;
$var = 1234567;
//$var = 'i"m sam';
//$var = 'i m sam';
//$var = array('1a', '2', 'hb' => 'bh', 'arr' => array('a', 'b', 'c'));
//$var = '{"1":"a2","a2":{"1":"a2.2","2":"a2.2"}}';
//$var = new stdClass(); $var->prop1 = 'val1'; $var->prop2 = 'val2'; $var->aa = new stdClass; $var->aa->prop1 = 'aa val1';
//$var = '{"prop1":"val1","prop2":"val2","aa":{"prop1":"aa val1"}}';