Why JSON_NUMERIC_CHECK is not a good idea

Not buggy, dangerous, though. Beware!

What is that?


When version 5.3.3 was released PHP introduced this new constant: JSON_NUMERIC_CHECK (btw, they also introduced JSON_ERROR_UTF8). Documentation is pretty clear "Encodes numeric strings as numbers" and it works as expected. So,



What's the problem?


In my previous post I told you about a weird json encode behavior. We saw json_encode() worked perfectly and JSON_NUMERIC_CHECK wasn't necessary.


Imagine this situation



  1. You have a DB and you have just 1 table "users" containing 3 fields (ID, name, postal_code). ID is an integer, name and postal_code are strings

  2. Real users in the system can login and edit their names and postal codes

  3. You are providing some "GET /users" request returning a json object listing all your users

  4. A client app (mobile, desktop, doesn't matter) requests your API periodically to render some stupid report.



Currently you have 2 users, Jose and Dani you run you request and get this JSON



[{"ID":"1","name":"Jose","postal_code":"34001"},{"ID":"2","name":"Dani","postal_code":"34002"}]



Look at this ID values: "1" and "2". They must be integers but they are strings.

Ok, you've read somewhere you can convert numeric string to numbers. Cool, go for it! In your API, the method now will return this



public function getUsers()


{



//Your code to get the users


return json_encode($users, JSON_NUMERIC_CHECK);



}



The result set is now:



[{"ID":1,"name":"Jose","postal_code":34001},{"ID":2,"name":"Dani","postal_code":34002}]



Well, definitely you did it. ID is now an integer! yes... but postal_code is an integer too! Ok, take it easy. You say the mobile team, "Hey folks, if you are expecting a string in postal code, why don't you cast this value to string?"


Things are not going as they should, you know it, you smell it and you are the public enemy no.1 among the mobile team. Anyway, they rock, they cast this value, happy days (?)


1 week later "Dani" the user #2 changes his postal code. Now his postal code is '04002'. Someone say, "It doesn't work". You look at your request and this is the response.



[{"ID":1,"name":"Jose","postal_code":34001},{"ID":2,"name":"Dani","postal_code":4002}]



Oh no! You actually need that 04002 string not the number 4002



JSON_NUMERIC_CHECK should be only used if it is ABSOLUTELY necessary, not a workaround to patch your bad practice. If you use this, NEVER apply it to user-typed data unless you are totally sure no dumb values could be there. - Jose


The quote above is just my opinion, not the php bible, but a good advice I think.



Ok, then, what do I do?


Well, json_encode() works perfectly if the array or object you provide has primitive types. If you are getting strings and you are forced to use this constant probably you are getting the wrong array. Look at the origin. Review my post about Mysql Native Driver or look what input data are you providing to json_encode(). Once the input array/object is well-formed your encoding will work as you are expecting, don't overcomplicate things.



Good tip!



Don't overcomplicate things