Is there any reasons why PHP's json_encode function does not escape all JSON control characters in a string?
For example let's take a string which spans two rows and has control characters (\r \n " / \) in it:
<?php
$s = <<<END
First row.
Second row w/ "double quotes" and backslash: \.
END;
$s = json_encode($s);
echo $s;
// Will output: "First row.\r\nSecond row w\/ \"double quotes\" and backslash: \\."
?>
Note that carriage return and newline chars are unescaped. Why?
I'm using jQuery as my JS library and it's $.getJSON() function will do fine when you fully, 100% trust incoming data. Otherwise I use JSON.org's library json2.js like everybody else.
But if you try to parse that encoded string it throws an error:
<script type="text/javascript">
JSON.parse(<?php echo $s ?>); // Will throw SyntaxError
</script>
And you can't get the data! If you remove or escape \r \n " and \ in that string then JSON.parse() will not throw error.
Is there any existing, good PHP function for escaping control characters. Simple str_replace with search and replace arrays will not work.
function escapeJsonString($value) {
# list from www.json.org: (\b backspace, \f formfeed)
$escapers = array("\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c");
$replacements = array("\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b");
$result = str_replace($escapers, $replacements, $value);
return $result;
}
I'm using the above function which escapes a backslash (must be first in the arrays) and should deal with formfeeds and backspaces (I don't think \f and \b are supported in PHP).
D'oh - you need to double-encode: JSON.parse is expecting a string of course:
<script type="text/javascript">
JSON.parse(<?php echo json_encode($s) ?>);
</script>
I still haven't figured out any solution without str_replace..
Try this code.
$json_encoded_string = json_encode(...);
$json_encoded_string = str_replace("\r", '\r', $json_encoded_string);
$json_encoded_string = str_replace("\n", '\n', $json_encoded_string);
Hope that helps...
$search = array("\n", "\r", "\u", "\t", "\f", "\b", "/", '"');
$replace = array("\\n", "\\r", "\\u", "\\t", "\\f", "\\b", "\/", "\"");
$encoded_string = str_replace($search, $replace, $json);
This is the correct way
Converting to and fro from PHP should not be an issue.
PHP's json_encode does proper encoding but reinterpreting that inside java script can cause issues. Like
1) original string - [string with nnn newline in it] (where nnn is actual newline character)
2) json_encode will convert this to
[string with "\\n" newline in it] (control character converted to "\\n" - Literal "\n"
3) However when you print this again in a literal string using php echo then "\\n" is interpreted as "\n" and that causes heartache. Because JSON.parse will understand a literal printed "\n" as newline - a control character (nnn)
so to work around this: -
A)
First encode the json object in php using json_enocde and get a string. Then run it through a filter that makes it safe to be used inside html and java script.
B)
use the JSON string coming from PHP as a "literal" and put it inside single quotes instead of double quotes.
<?php
function form_safe_json($json) {
$json = empty($json) ? '[]' : $json ;
$search = array('\\',"\n","\r","\f","\t","\b","'") ;
$replace = array('\\\\',"\\n", "\\r","\\f","\\t","\\b", "'");
$json = str_replace($search,$replace,$json);
return $json;
}
$title = "Tiger's /new \\found \/freedom " ;
$description = <<<END
Tiger was caged
in a Zoo
And now he is in jungle
with freedom
END;
$book = new \stdClass ;
$book->title = $title ;
$book->description = $description ;
$strBook = json_encode($book);
$strBook = form_safe_json($strBook);
?>
<!DOCTYPE html>
<html>
<head>
<title> title</title>
<meta charset="utf-8">
<script type="text/javascript" src="/3p/jquery/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var strBookObj = '<?php echo $strBook; ?>' ;
try{
bookObj = JSON.parse(strBookObj) ;
console.log(bookObj.title);
console.log(bookObj.description);
$("#title").html(bookObj.title);
$("#description").html(bookObj.description);
} catch(ex) {
console.log("Error parsing book object json");
}
});
</script>
</head>
<body>
<h2> Json parsing test page </h2>
<div id="title"> </div>
<div id="description"> </div>
</body>
</html>
Put the string inside single quote in java script. Putting JSON string inside double quotes would cause the parser to fail at attribute markers (something like { "id" : "value" } ). No other escaping should be required if you put the string as "literal" and let JSON parser do the work.
I don't fully understand how var_export works, so I will update if I run into trouble, but this seems to be working for me:
<script>
window.things = JSON.parse(<?php var_export(json_encode($s)); ?>);
</script>
Maybe I'm blind, but in your example they ARE escaped. What about
<script type="text/javascript">
JSON.parse("<?php echo $s ?>"); // Will throw SyntaxError
</script>
(note different quotes)
Just an addition to Greg's response: the output of json_encode() is already contained in double-quotes ("), so there is no need to surround them with quotes again:
<script type="text/javascript">
JSON.parse(<?php echo $s ?>);
</script>
Control characters have no special meaning in HTML except for new line in textarea.value . JSON_encode on PHP > 5.2 will do it like you expected.
If you just want to show text you don't need to go after JSON. JSON is for arrays and objects in JavaScript (and indexed and associative array for PHP).
If you need a line feed for the texarea-tag:
$s=preg_replace('/\r */','',$s);
echo preg_replace('/ *\n */','
',$s);
This is what I use personally and it's never not worked. Had similar problems originally.
Source script (ajax) will take an array and json_encode it. Example:
$return['value'] = 'test';
$return['value2'] = 'derp';
echo json_encode($return);
My javascript will make an AJAX call and get the echoed "json_encode($return)" as its input, and in the script I'll use the following:
myVar = jQuery.parseJSON(msg.replace(/"/ig,'"'));
with "msg" being the returned value. So, for you, something like...
var msg = '<?php echo $s ?>';
myVar = jQuery.parseJSON(msg.replace(/"/ig,'"'));
...might work for you.
There are 2 solutions unless AJAX is used:
Write data into input like and read it in JS:
<input type="hidden" value="<?= htmlencode(json_encode($data)) ?>"/>
Use addslashes
var json = '<?= addslashes(json_encode($data)) ?>';
When using any form of Ajax, detailed documentation for the format of responses received from the CGI server seems to be lacking on the Web. Some Notes here and entries at stackoverflow.com point out that newlines in returned text or json data must be escaped to prevent infinite loops (hangs) in JSON conversion (possibly created by throwing an uncaught exception), whether done automatically by jQuery or manually using Javascript system or library JSON parsing calls.
In each case where programmers post this problem, inadequate solutions are presented (most often replacing \n by \\n on the sending side) and the matter is dropped. Their inadequacy is revealed when passing string values that accidentally embed control escape sequences, such as Windows pathnames. An example is "C:\Chris\Roberts.php", which contains the control characters ^c and ^r, which can cause JSON conversion of the string {"file":"C:\Chris\Roberts.php"} to loop forever. One way of generating such values is deliberately to attempt to pass PHP warning and error messages from server to client, a reasonable idea.
By definition, Ajax uses HTTP connections behind the scenes. Such connections pass data using GET and POST, both of which require encoding sent data to avoid incorrect syntax, including control characters.
This gives enough of a hint to construct what seems to be a solution (it needs more testing): to use rawurlencode on the PHP (sending) side to encode the data, and unescape on the Javascript (receiving) side to decode the data. In some cases, you will apply these to entire text strings, in other cases you will apply them only to values inside JSON.
If this idea turns out to be correct, simple examples can be constructed to help programmers at all levels solve this problem once and for all.
Related
This is my code:
<?php
$a = array('chào','thân','ái','và','quyết','thắng');
?>
<script>
var abc = '<?php echo json_encode($a); ?>';
</script>
When run and i got abc variable is:
var abc = '["ch\u00e0o","th\u00e2n","\u00e1i","v\u00e0","quy\u1ebft","th\u1eafng"]';
So, how to convert it's as before? i want to it's become
var abc = '["chào","thân","ái","và","quyết","thắng"]';
There might be you're answer here :
How to decode Unicode escape sequences like "\u00ed" to proper UTF-8 encoded characters?
But why would you translate those escaped sequences, which should be well displayed and correctly translated by php (json_decode) and javascript (JSON.parse) ?
If your php is >= 5.4, you can use the JSON_UNESCAPED_UNICODE flag to encode unicode characters as-is:
var abc = '<?php echo json_encode($a, JSON_UNESCAPED_UNICODE); ?>';
However, the escaped form works just fine and is less error-prone, so I'd suggest that you keep using it.
I'm using textarea to get data that I insert into a database.
I'm using htmlspecialchars() to get rid of the single quotes and double quotes but it doesn't convert new lines into something so I'm left with a very long piece of code that doesn't have new lines and looks messy.
I've checked the manual but I can't find how to convert it.
How would I do this?
EDIT:
My intended output is the same as what the user inputted.
So if they inputted into the textarea...
Hi
This is another line
This is another line
It would store into the database like...
Hi\r\nThis is another line\r\n This is another line.
or something like that.
Then when I echo it again then it should be fine.
Anthony,
If you are referring to when you get it back out and you want it to look nice, and you aren't putting it back into a textarea, you can use the mythical function nl2br() to convert new line characters into HTML characters.
$data = 'Testing\r\nThis\r\nagain!\r\n';
echo nl2br($data);
This results in:
Testing
This
again!
I believe what you are looking for is
nl2br($string);
That will convert the returns to <br> tags
I will also give you this script that has worked well for me in the past when nl2br does not.
$remove = array("\r\n", "\n", "\r", "chr(13)", "\t", "\0", "\x0B");
$string = str_replace($order, "<br />", $string);
It should be:
<?php
addslashes( strip_tags( nl2br( $data ) ) );
?>
addslashes : will escape quotes to prevent sql injection
strip_tags : will remove any html tags if any
nl2br : will convert newline into <br />
I am trying to pass a string to a javascript function which opens that string in an editable text area. If the string does not contain a new line character, it is passed successfully. But when there is a new line character it fails.
My code in PHP looks like
$show_txt = sprintf("showEditTextarea('%s')", $test_string);
$output[] = '<a href="#" id="link-'.$data['test'].'" onclick="'.$show_txt.';return false;">';
And the javascript function looks like -
$output[] = '<script type="text/javascript">
var showEditTextarea = function(test_string) {
alert(test_string);
}
</script>';
The string that was successfully passed was "This is a test" and it failed for "This is a first test
This is a second test"
Javascript does not allow newline characters in strings. You need to replace them by \n before the sprintf() call.
You are getting this error because there is nothing escaping your javascript variables... json_encode is useful here. addslashes will also have to be used in the context to escape the double quotes.
$show_txt = sprintf("showEditTextarea(%s)", json_encode($test_string));
$output[] = '<a href="#" id="link-'.$data['test'].'" onclick="'.htmlspecialchars($show_txt).';return false;">';
Why don't you try replacing all spaces in the php string with \r\n before you pass it to the JavaScript function? See if that works.
If that does not work then try this:
str_replace($test, "\n", "\n");
Replacing with two \ may work as it will encapsulate.
I would avoid storing HTML or JS in PHP variables as much as possible, but if you do need to store the HTML in a PHP variable then you will need to escape the new line characters.
try
$test_string = str_replace("\n", "\\\n", $test_string);
Be sure to use double quotes in the str_replace otherwise the \n will be interpreted as literally \n instead of a new line character.
Try this code, that deletes new lines:
$show_txt = sprintf("showEditTextarea('%s')", str_replace(PHP_EOL, '', $test_string));
Or replaces with: \n.
$show_txt = sprintf("showEditTextarea('%s')", str_replace(PHP_EOL, '\n', $test_string));
I want to convert special characters to HTML entities, and then back to the original characters.
I have used htmlentities() and then html_entity_decode(). It worked fine for me, but { and } do not come back to the original characters, they remain { and }.
What can I do now?
My code looks like:
$custom_header = htmlentities($custom_header);
$custom_header = html_entity_decode($custom_header);
Even though nobody can replicate your problem, here's a direct way to solve it by a simple str_replace.
$input = '<p><script type="text/javascript"> $(document).ready(function() { $("#left_side_custom_image").click(function() { alert("HELLO"); }); }); </script></p> ';
$output = str_replace( array( '{', '}'), array( '{', '}'), $input);
Demo (click the 'Source' link in the top right)
Edit: I see the problem now. If your input string is:
"{hello}"
A call to htmlentities encodes the & into &, which gives you the string
"{hello}"
The & is then later decoded back into &, to output this:
"{hello}"
The fix is to send the string through html_entity_decode again, which will properly decode your entities.
$custom_header = "{hello}";
$custom_header = htmlentities($custom_header);
$custom_header = html_entity_decode($custom_header);
echo html_entity_decode( $custom_header); // Outputs {hello}
A double quote even if escaped is throwing parse error.
look at the code below
//parse the json in javascript
var testJson = '{"result": ["lunch", "\"Show\""] }';
var tags = JSON.parse(testJson);
alert (tags.result[1]);
This is throwing parse error because of the double quotes (which are already escaped).
Even eval() won't work here.
But if i escape it with double slashes like this:
var result = '{"result": ["lunch", "\\"Show\\""] }';
var tags = JSON.parse(result);
alert (tags.result[1]);
then it works fine.
Why do we need to use double slash here in javascript?
The problem is that PHP json_encode() function escapes a double quote with a single slash (like this: \"show\") which JSON.parse won't be able to parse. How do i handle this situation?
Javascript unescapes its strings and json unescapes them as well.
the first string ( '{"result": ["lunch", "\"Show\""] }' ) is seen by the json parser as
{"result": ["lunch", ""Show""] }, because \" in javascript means ", but doesn't exit the double quoted string.
The second string '{"result": ["lunch", "\\\"Show\\\""] }' gets first unescaped to {"result": ["lunch", "\"Show\""] } (and that is correctly unescaped by json).
I think, that '{"result": ["lunch", "\\"Show\\""] }' should work too.
Well, finally, JSON's parse uses the same eval, so there's no difference when you give them smth. with incorrect syntax. In this case you have to escape correctly your quotes in php, and then escape them and their escaping slashes with json_encode
<?php
$json = '{"result": ["lunch", "\"Show\""] }';
echo json_encode($json);
?>
OUTPUT: "{\"result\": [\"lunch\", \"\\\"Show\\\"\"] }"
This should work on client-side JS (if I've made no typos).
This problem is caused by the two-folded string escaping mechanism: one comes from JS and one comes from JSON.
A combination of the backslash character combined with another following character is used to represent one character that is not otherwise representable within the string.
''\\'' stands for '\' etc.
This escaping mechanism takes place before JSON.parse() works.
For Example,
var parsedObj = JSON.parse('{"sentence": "It is one backslash(\\\\)"}');
console.log(parsedObj.sentence);
>>>"It is one backslash(\)"
From the string generator's perspective, it passes four backlashes '\' into the JavaScript interpretor.
From the JavaScript interpretor's perspective, it inteprets there are two backlashes(\) as each '\\' sequence will be interpreted as one '\'.
From the JSON parser's perspective, it receives two backlashes(\\) and the JSON string escape rules will parses it as one single '\' which is the output result.
Explain you first code:
var testJson = '{"result": ["lunch", "\"Show\""] }';
//The real string after sequence escaping in to JS is
//'{"result": ["lunch", ""Show""] }'
//which is passed into the JSON.parse.
//Thus, it breaks the JSON grammar and generates an error
var tags = JSON.parse(testJson);
alert (tags.result[1]);
From the docs
JSON_HEX_APOS (integer) All ' are converted to \u0027
JSON_HEX_QUOT (integer) All " are converted to \u0022
json_encode() takes two args, the value and options. So try
json_encode($result, JSON_HEX_QUOT); // or
json_encode($result, JSON_HEX_QUOT | JSON_HEX_APOS);
I haven't tried this though.
php to javascript Object (php >= 5.3.0)
var storesLocations = JSON.parse('<?php echo addslashes(json_encode($storesLocations,JSON_HEX_APOS | JSON_HEX_QUOT)) ?>');
Turn off magic_quotes_gpc in php.ini.
If the standard C escapes are added, then JSON.parse will convert sequences like \" into ", \\ into \, \n into a line-feed, etc.
'foo\\bar\nbaz"' === JSON.parse('"foo\\\\bar\\nbaz\\""')
In our project's case:
original string ""{\"lat\":28.4594965,\"lng\":77.0266383}""
After passing to JSON.parse()
"{"lat":28.4594965,"lng":77.0266383}"
On 2nd pass to JSON.parse()
{lat: 28.4594965, lng: 77.0266383}
Notice that JSON.parse() removed escaping characters instead of converting string to object.
After the escaping characters were removed, the string to object conversion worked.
Here is the demo:
while (typeof p1 != 'object') {
p1 = JSON.parse(p1);
pass++;
console.log('On pass ', pass, p1);
}
This might help:
<?php
$json = '{"result": ["lunch", "\"Show\""] }';
echo addslashes(json_encode($json));
Try This. and then try commented
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<pre><?php ($_POST)?print_r($_POST):'' ?></pre>
<form method="POST">
<input type="text" name="name"><br>
<input type="email" name="email"><br>
<input type="time" name="time"><br>
<input type="date" name="date"><br>
<input type="hidden" name="id"><br>
<textarea name="detail"></textarea>
<input type="submit" value="Submit"><br>
</form>
<?php
/* data */
$data = [
'name'=>'vinay"'."'\\\\",
'email'=>'imvsrajput#demo.demo',
'time'=>date('H:i:00'),
'date'=>date('Y-m-d'),
'detail'=>'Try this var_dump(0=="ZERO") \\ \\"'." ' \\\\ ",
'id'=>123,
];
?>
<span style="display: none;" class="ajax-data"><?=json_encode($data)?></span>
<script type="text/javascript">
/* Error */
// var json = JSON.parse('<?=json_encode($data)?>');
/* Error solved */
var json = JSON.parse($('.ajax-data').html());
console.log(json)
/* automatically assigned value by name attr */
for(x in json){
$('[name="'+x+'"]').val(json[x]);
}
</script>