Skip to content

Commit

Permalink
Updating JavascriptHelper::escapeString to properly encode utf8 chara…
Browse files Browse the repository at this point in the history
…cters as per the JSON spec.

Test cases added, with comparisons to json_encode()
Fixes #6400

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@8198 3807eeeb-6ff5-0310-8944-8be069107fe0
  • Loading branch information
markstory committed Jun 17, 2009
1 parent 88e0cfa commit 1ea5f94
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 4 deletions.
100 changes: 98 additions & 2 deletions cake/libs/view/helpers/javascript.php
Expand Up @@ -319,8 +319,104 @@ function escapeScript($script) {
* @return string Escaped string.
*/
function escapeString($string) {
$escape = array('\n' => '\\\n', "\r\n" => '\n', "\r" => '\n', "\n" => '\n', '"' => '\"', "'" => "\\'");
return str_replace(array_keys($escape), array_values($escape), $string);
App::import('Core', 'Multibyte');
$escape = array("\r\n" => "\n", "\r" => "\n");
$string = str_replace(array_keys($escape), array_values($escape), $string);
return $this->_utf8ToHex($string);
}
/**
* Encode a string into JSON. Converts and escapes necessary characters.
*
* @return void
**/
function _utf8ToHex($string) {
$length = strlen($string);
$return = '';
for ($i = 0; $i < $length; ++$i) {
$ord = ord($string{$i});
switch (true) {
case $ord == 0x08:
$return .= '\b';
break;
case $ord == 0x09:
$return .= '\t';
break;
case $ord == 0x0A:
$return .= '\n';
break;
case $ord == 0x0C:
$return .= '\f';
break;
case $ord == 0x0D:
$return .= '\r';
break;
case $ord == 0x22:
case $ord == 0x2F:
case $ord == 0x5C:
case $ord == 0x27:
$return .= '\\' . $string{$i};
break;
case (($ord >= 0x20) && ($ord <= 0x7F)):
$return .= $string{$i};
break;
case (($ord & 0xE0) == 0xC0):
if ($i + 1 >= $length) {
$i += 1;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 1;
break;
case (($ord & 0xF0) == 0xE0):
if ($i + 2 >= $length) {
$i += 2;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 2;
break;
case (($ord & 0xF8) == 0xF0):
if ($i + 3 >= $length) {
$i += 3;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 3;
break;
case (($ord & 0xFC) == 0xF8):
if ($i + 4 >= $length) {
$i += 4;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 4;
break;
case (($ord & 0xFE) == 0xFC):
if ($i + 5 >= $length) {
$i += 5;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4} . $string{$i + 5};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 5;
break;
}
}
return $return;
}
/**
* Attach an event to an element. Used with the Prototype library.
Expand Down
57 changes: 55 additions & 2 deletions cake/tests/cases/libs/view/helpers/javascript.test.php
Expand Up @@ -402,6 +402,7 @@ function testObjectNonNative() {

$this->Javascript->useNative = $oldNative;
}

/**
* testScriptBlock method
*
Expand Down Expand Up @@ -653,11 +654,11 @@ function testEscapeString() {
$this->assertEqual($result, $expected);

$result = $this->Javascript->escapeString('CakePHP: \'Rapid Development Framework\'');
$expected = 'CakePHP: \\\'Rapid Development Framework\\\'';
$expected = "CakePHP: \\'Rapid Development Framework\\'";
$this->assertEqual($result, $expected);

$result = $this->Javascript->escapeString('my \\"string\\"');
$expected = 'my \\\"string\\\"';
$expected = 'my \\\\\\"string\\\\\\"';
$this->assertEqual($result, $expected);

$result = $this->Javascript->escapeString('my string\nanother line');
Expand All @@ -672,6 +673,58 @@ function testEscapeString() {
$expected = 'String with \\\n string that looks like newline';
$this->assertEqual($result, $expected);
}
/**
* test string escaping and compare to json_encode()
*
* @return void
**/
function testStringJsonEncodeCompliance() {
if (!function_exists('json_encode')) {
return;
}
$this->Javascript->useNative = false;
$data = array();
$data['mystring'] = "simple string";
$this->assertEqual(json_encode($data), $this->Javascript->object($data));

$data['mystring'] = "strïng with spécial chârs";
$this->assertEqual(json_encode($data), $this->Javascript->object($data));

$data['mystring'] = "a two lines\nstring";
$this->assertEqual(json_encode($data), $this->Javascript->object($data));

$data['mystring'] = "a \t tabbed \t string";
$this->assertEqual(json_encode($data), $this->Javascript->object($data));

$data['mystring'] = "a \"double-quoted\" string";
$this->assertEqual(json_encode($data), $this->Javascript->object($data));

$data['mystring'] = 'a \\"double-quoted\\" string';
$this->assertEqual(json_encode($data), $this->Javascript->object($data));
}
/**
* test that text encoded with Javascript::object decodes properly
*
* @return void
**/
function testObjectDecodeCompatibility() {
if (!function_exists('json_decode')) {
return;
}
$this->Javascript->useNative = false;

$data = array("simple string");
$result = $this->Javascript->object($data);
$this->assertEqual(json_decode($result), $data);

$data = array('my \"string\"');
$result = $this->Javascript->object($data);
$this->assertEqual(json_decode($result), $data);

$data = array('my \\"string\\"');
$result = $this->Javascript->object($data);
$this->assertEqual(json_decode($result), $data);
}
/**
* testAfterRender method
*
Expand Down

0 comments on commit 1ea5f94

Please sign in to comment.