Refactored JavascriptHelper::object(), improved JSON spec compliance, added sport for native json extension, improved tests

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@6379 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
nate 2008-01-15 05:21:07 +00:00
parent 14122fd1e6
commit 1e4549679a
2 changed files with 93 additions and 45 deletions

View file

@ -44,10 +44,16 @@ class JavascriptHelper extends AppHelper {
var $_cacheToFile = false;
var $_cacheAll = false;
var $_rules = array();
/**
* Determines whether native JSON extension is used for encoding. Set by object constructor.
*
* @var boolean
*/
var $useNative = false;
var $enabled = true;
var $safe = false;
/**
* html tags used by this helper.
* HTML tags used by this helper.
*
* @var array
*/
@ -57,7 +63,15 @@ class JavascriptHelper extends AppHelper {
'javascriptlink' => '<script type="text/javascript" src="%s"></script>',
'javascriptend' => '</script>',
);
/**
* Constructor. Checks for presence of native PHP JSON extension to use for object encoding
*
* @access public
*/
function __construct() {
$this->useNative = function_exists('json_encode');
return parent::__construct();
}
/**
* Returns a JavaScript script tag.
*
@ -384,26 +398,22 @@ class JavascriptHelper extends AppHelper {
}
$defaultOptions = array('block' => false, 'prefix' => '', 'postfix' => '', 'stringKeys' => array(), 'quoteKeys' => true, 'q' => '"');
$options = array_merge($defaultOptions, $options);
foreach($defaultOptions as $option => $value) {
if (isset($$option) && $$option !== null) {
$options[$option] = $$option;
}
}
$options = array_merge($defaultOptions, $options, array_filter(compact(array_keys($defaultOptions))));
if (is_object($data)) {
$data = get_object_vars($data);
}
$out = array();
$keys = array();
$out = $keys = array();
$numeric = true;
if ($this->useNative) {
$rt = json_encode($data);
} else {
if (is_array($data)) {
$keys = array_keys($data);
}
$numeric = true;
if (!empty($keys)) {
$numeric = (array_values($keys) === array_keys(array_values($keys)));
}
@ -412,21 +422,11 @@ class JavascriptHelper extends AppHelper {
if (is_array($val) || is_object($val)) {
$val = $this->object($val, $options);
} else {
if ((!count($options['stringKeys']) && !is_numeric($val) && !is_bool($val)) || ($options['quoteKeys'] && in_array($key, $options['stringKeys'], true)) || (!$options['quoteKeys'] && !in_array($key, $options['stringKeys'], true))) {
$val = $options['q'] . $this->escapeString($val) . $options['q'];
$val = $this->value($val, (!count($options['stringKeys']) || ($options['quoteKeys'] && in_array($key, $options['stringKeys'], true)) || (!$options['quoteKeys'] && !in_array($key, $options['stringKeys'], true))));
}
if ($val === null) {
$val = 'null';
}
if (is_bool($val)) {
$val = ife($val, 'true', 'false');
}
}
if (!$numeric) {
$val = $options['q'] . $key . $options['q'] . ':' . $val;
$val = $options['q'] . $this->value($key, false) . $options['q'] . ':' . $val;
}
$out[] = $val;
}
@ -435,6 +435,7 @@ class JavascriptHelper extends AppHelper {
} else {
$rt = '[' . join(',', $out) . ']';
}
}
$rt = $options['prefix'] . $rt . $options['postfix'];
if ($options['block']) {
@ -443,6 +444,39 @@ class JavascriptHelper extends AppHelper {
return $rt;
}
/**
* Converts a PHP-native variable of any type to a JSON-equivalent representation
*
* @param mixed $val A PHP variable to be converted to JSON
* @param boolean $quoteStrings If false, leaves string values unquoted
* @return string a JavaScript-safe/JSON representation of $val
*/
function value($val, $quoteStrings = true) {
switch (true) {
case (is_array($val) || is_object($val)):
$val = $this->object($val);
break;
case ($val === null):
$val = 'null';
break;
case (is_bool($val)):
$val = ife($val, 'true', 'false');
break;
case (is_int($val)):
$val = $val;
break;
case (is_float($val)):
$val = sprintf("%.11f", $val);
break;
default:
$val = $this->escapeString($val);
if ($quoteStrings) {
$val = '"' . $val . '"';
}
break;
}
return $val;
}
/**
* AfterRender callback. Writes any cached events to the view, or to a temp file.
*

View file

@ -99,15 +99,29 @@ class JavascriptTest extends UnitTestCase {
$result = $this->Javascript->object(array(
'2007' => array(
'Spring' => array('1' => array('id' => '1', 'name' => 'Josh'), '2' => array('id' => '2', 'name' => 'Becky')),
'Fall' => array('1' => array('id' => '1', 'name' => 'Josh'), '2' => array('id' => '2', 'name' => 'Becky'))
'Spring' => array('1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')),
'Fall' => array('1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky'))
), '2006' => array(
'Spring' => array('1' => array('id' => '1', 'name' => 'Josh'), '2' => array('id' => '2', 'name' => 'Becky')),
'Fall' => array('1' => array('id' => '1', 'name' => 'Josh'), '2' => array('id' => '2', 'name' => 'Becky')
'Spring' => array('1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')),
'Fall' => array('1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')
))
));
$expected = '{"2007":{"Spring":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}},"Fall":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}}},"2006":{"Spring":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}},"Fall":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}}}}';
$this->assertEqual($result, $expected);
$result = $this->Javascript->object(array('Object' => array(true, false, 1, '02101', 0, -1, 3.141592653589, "1")));
$expected = '{"Object":[true,false,1,"02101",0,-1,3.14159265359,"1"]}';
$this->assertEqual($result, $expected);
$result = $this->Javascript->object(array('Object' => array(true => true, false, -3.141592653589, -10)));
$expected = '{"Object":{"1":true,"2":false,"3":-3.14159265359,"4":-10}}';
$this->assertEqual($result, $expected);
if ($this->Javascript->useNative) {
$this->Javascript->useNative = false;
$this->testObjectGeneration();
$this->Javascript->useNative = true;
}
}
function testScriptBlock() {