0

I've a very nasty problem sending data from PHP to Javascript.

Long story short... I get some data from a DB then convert all to json and send all to a javascript function.

One of the column from the DB contain data that look like:

val01\test\val04

I assign these data to an array then using json_encode convert everything to json, like in this example:

$result = 'val01\test\val04'; //these are the data that are get from DB
$example = ['testData' => $result];
json_encode($example);

At the end I've these data:

{"testData":"val01\\test\\val04\\"}

now, when i use these result with parse.json like this:

var json = '{"testData":"val01\\test\\val04\\"}';
obj = JSON.parse(json);

I receive an error:

Error: Unexpected token v in JSON at position 24

seem that the slashes are escaped incorrectly... To be sure that is not my fault with something else.. I test my example also on MDN sample page (https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) that return same result.

How can I solve this problem?

I won't create my own escape function.. is too dangerous from my point of view.. I never know what kind of data I will can find in this columns so.. there is a clean way to handle this problem?

thank you

Marco
  • 487
  • 2
  • 6
  • 25
  • 1
    The code you share has problems of its own that I suspect are not in the actual code you run. For a start, in JavaScript string literals you need to escape backslashes too, so the correct expression would be `var json = '{"testData":"val01\\\\test\\\\val04\\\\"}';`. Are you able to edit the question and compose a *runnable* snippet that reproduces the issue? – Álvaro González Aug 24 '19 at 16:22
  • You can do this: `JSON.parse(json.replace(/\\/g, '\\'))` to get the desired result, but storing data in a db using a backslash as a delimiter is probably just a bad idea. – Jared Smith Aug 24 '19 at 16:26
  • 1
    @ÁlvaroGonzález thank you for you help but the code is perfectly working (formally) and is perfect example of real code. The problem is exactly as you stated.. the slashes count are wrong but this is the result from json_encode php function – Marco Aug 24 '19 at 16:31
  • Run the strings through [`addslashes`](https://www.php.net/manual/en/function.addslashes.php) prior to encoding. Both `\v` and `\t` have special meaning but php and js interpret them differently. – msg Aug 24 '19 at 16:35
  • @JaredSmith I now that the slashes are not a good idea.. but you can't force user to follow best practice :D. Your example dont' work in some other cases like if you have an odd number of slashes like "val01\test\\\val04" as source – Marco Aug 24 '19 at 16:36
  • @msg not in single-quoted strings, only in double-quoted strings. `echo '\break\test\now';` will print literally that string, no special character replacements happen. – Mike 'Pomax' Kamermans Aug 24 '19 at 16:36

1 Answers1

1

You seem to be doing two things:

  1. Generate JSON.
  2. Insert such JSON inside a JavaScript string literal.

Remember that JSON is not JavaScript. JSON is just a plain text data format while JavaScript is a full-fledged programming language. They're often confused because the former was inspired in the syntax of certain data structures from the latter, but that's where the similarity ends.

Dealing with #1 is easy: PHP has a function specifically designed for it, json_encode(), and in my experience it's rock solid.

Dealing with #2 is not as straightforward, though. PHP has functions (better or worse) to escape literal data in HTML, URLs or SQL, but it's never really had a specific function to do that same job in JavaScript strings.

Shall we write our own function? Luckily not. Here's where the similarity between JSON and JavaScript comes to help us. It happens that we can also use json_encode() to escape raw input in JavaScript strings because JSON syntax is a subset of JavaScript string syntax. But... JSON has to be an object or an array, doesn't it? That's true. Luckily, PHP comes to the rescue because:

PHP implements a superset of JSON as specified in the original RFC 7159.
[...]
Like the reference JSON encoder, json_encode() will generate JSON that is a simple value (that is, neither an object nor an array) if given a string, integer, float or boolean as an input value. While most decoders will accept these values as valid JSON, some may not, as the specification is ambiguous on this point.

Source

In other words, json_encode() also produces JSON fragments. So you can do this:

<?php
$result = 'val01\test\val04'; //these are the data that are get from DB
$example = ['testData' => $result];
$json = json_encode($example);
$javascript = json_encode($json);
?>
<script>
var json = <?php echo $javascript; ?>;
var obj = JSON.parse(json);
</script>

Which renders:

<script>
var json = "{\"testData\":\"val01\\\\test\\\\val04\"}"
var obj = JSON.parse(json);
</script>

And works as expected once in the browser:

var json = "{\"testData\":\"val01\\\\test\\\\val04\"}"
var obj = JSON.parse(json);
console.log(obj.testData);

As noted in comments, using JSON as proxy to pass data from PHP to JavaScript is completely redundant. Remember: JSON syntax is a subject of JavaScript syntax. That means that any valid JSON string also happens to be a valid JavaScript object or array literal. In this use case it doesn't make sense to encode twice in PHP and decode afterwards in JavaScript.

Álvaro González
  • 142,137
  • 41
  • 261
  • 360
  • This seems bad code - `json_encode` should generate correct JSON, and as single quotes are not part of the JSON spec, you should be able to say `let json = '';`. Can you explain _why_ it needs to be called twice? – Mike 'Pomax' Kamermans Aug 24 '19 at 16:43
  • no, but `var json = '{"x": 1}'` is 100% valid json: you use single-quotes around it, because that's what JSON was designed to allow for. And even more relevant in modern JS, you use backticks instead of quote symbols. – Mike 'Pomax' Kamermans Aug 24 '19 at 16:45
  • Sorry, I misread your comment. If you want to use single quotes then you cannot use json_encode() for #2. You'd need to escape everything by yourself. – Álvaro González Aug 24 '19 at 16:45
  • Right, so: why? Why does `json_encode` not actually generate the correct JSON after the first step? (JS's `JSON.stringify` does this just fine. As does Python's `json.dumps`, so the fact that `json_encode` does _not_ generate correct JSON unless you manually intervene, or run it twice, needs an explanation) – Mike 'Pomax' Kamermans Aug 24 '19 at 16:46
  • It does generate perfectly valid JSON, you can use any JSON linter to verify that. But the OP is doing afterwards an entirely different task: he's injecting JSON inside a JavaScript single-quoted string literal. For example, `O'Brian` is a perfectly valid name. But if you want it inside a JavaScript string literal you need to escape the single quote. – Álvaro González Aug 24 '19 at 16:49
  • So then what is needed is `addslashes` (or file writing/reading), not `json_encode` twice? – Mike 'Pomax' Kamermans Aug 24 '19 at 16:53
  • It's possible that `addslahes()` is safe enough here because `json_encode()` has already deactivated line feeds and other characters that are unsafe in JS strings and `addslashes()` doesn't encode—I don't really know. I tend to use use `json_enconde()` always since it covers all situations. – Álvaro González Aug 24 '19 at 16:59
  • @ÁlvaroGonzález thank you for you reply, but really I can't understand WHY this solution will work.. seem a trick that can work in this specific case.. but are you sure that this will work every time also in other situations? I need to understand why the standard json_encode don't work as expected.. is a bug or a feature or something else that i miss? thank you again – Marco Aug 24 '19 at 22:28
  • @Marco I must be missing something. `json_encode()` **does work** fine. But after you generate JSON you do a second different thing. Image you insert that JSON in MySQL; if you inject the raw JSON string into your SQL code your code will be vulnerable to SQL injection. Does that mean that `json_encode()` doesn't work? No, it just means it isn't its job. Or perhaps it's just the classical confusion between JSON and JavaScript—may people think they're the same thing but they aren't. JSON syntax is inspired by JavaScript syntax but it isn't the same thing. – Álvaro González Aug 25 '19 at 07:48
  • I've edited my answer to try to improve the explanation. If it's still unclear, I'm out of ideas. – Álvaro González Aug 25 '19 at 08:09
  • Uhm, why not simply omit the JSON-in-string-literals and `JSON.parse` entirely and just do `var obj = = json_encode(...); ?>;`…?! – deceze Aug 25 '19 at 08:18
  • @deceze Yes, that's an important detail. I've added it to the answer, thank you. – Álvaro González Aug 25 '19 at 12:57