From 5fd27b7cbbff43f05d81ab9e8ce2087a5b4214f3 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 1 May 2013 14:18:23 -0400 Subject: [PATCH] Added Json and JsonExpression. --- framework/assets/yii.activeForm.js | 22 +++--- framework/helpers/Json.php | 18 +++++ framework/helpers/JsonExpression.php | 45 +++++++++++++ framework/helpers/base/Json.php | 107 ++++++++++++++++++++++++++++++ tests/unit/framework/helpers/JsonTest.php | 60 +++++++++++++++++ 5 files changed, 242 insertions(+), 10 deletions(-) create mode 100644 framework/helpers/Json.php create mode 100644 framework/helpers/JsonExpression.php create mode 100644 framework/helpers/base/Json.php create mode 100644 tests/unit/framework/helpers/JsonTest.php diff --git a/framework/assets/yii.activeForm.js b/framework/assets/yii.activeForm.js index 12bbbf9..0834a14 100644 --- a/framework/assets/yii.activeForm.js +++ b/framework/assets/yii.activeForm.js @@ -23,6 +23,12 @@ }; var defaults = { + // the container CSS class representing the corresponding attribute has validation error + errorCssClass: 'error', + // the container CSS class representing the corresponding attribute passes validation + successCssClass: 'success', + // the container CSS class representing the corresponding attribute is being validated + validatingCssClass: 'validating', // whether it is waiting for ajax submission result submitting: false }; @@ -32,21 +38,17 @@ * Initializes the plugin. * @param attributes array attribute configurations. Each attribute may contain the following options: * - * - id: 'ModelClass_attribute', // the unique attribute ID - * - model: 'ModelClass', // the model class name - * - name: 'name', // attribute name - * - inputID: 'input-tag-id', - * - errorID: 'error-tag-id', - * - value: undefined, - * - status: 0, // 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating + * - name: string, attribute name or expression (e.g. "[0]content" for tabular input) + * - container: string, the jQuery selector of the container of the input field + * - input: string, the jQuery selector of the input field + * - error: string, the jQuery selector of the error tag + * - value: string|array, the value of the input + * - status: integer, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating * - validationDelay: 200, * - validateOnChange: true, * - validateOnType: false, * - hideErrorMessage: false, * - inputContainer: undefined, - * - errorCssClass: 'error', - * - successCssClass: 'success', - * - validatingCssClass: 'validating', * - enableAjaxValidation: true, * - enableClientValidation: true, * - clientValidation: undefined, // function (value, messages, attribute) | client-side validation diff --git a/framework/helpers/Json.php b/framework/helpers/Json.php new file mode 100644 index 0000000..2a20f3c --- /dev/null +++ b/framework/helpers/Json.php @@ -0,0 +1,18 @@ + + * @since 2.0 + */ +class Json extends base\Json +{ + +} \ No newline at end of file diff --git a/framework/helpers/JsonExpression.php b/framework/helpers/JsonExpression.php new file mode 100644 index 0000000..eee57a1 --- /dev/null +++ b/framework/helpers/JsonExpression.php @@ -0,0 +1,45 @@ + + * @since 2.0 + */ +class JsonExpression extends Object +{ + /** + * @var string the JavaScript expression represented by this object + */ + public $expression; + + /** + * Constructor. + * @param string $expression the JavaScript expression represented by this object + * @param array $config additional configurations for this object + */ + public function __construct($expression, $config = array()) + { + $this->expression = $expression; + parent::__construct($config); + } + + /** + * The PHP magic function converting an object into a string. + * @return string the JavaScript expression. + */ + public function __toString() + { + return $this->expression; + } +} \ No newline at end of file diff --git a/framework/helpers/base/Json.php b/framework/helpers/base/Json.php new file mode 100644 index 0000000..d39b2b3 --- /dev/null +++ b/framework/helpers/base/Json.php @@ -0,0 +1,107 @@ + + * @since 2.0 + */ +class Json +{ + /** + * Encodes the given value into a JSON string. + * The method enhances `json_encode()` by supporting JavaScript expressions. + * In particular, the method will not encode a JavaScript expression that is + * represented in terms of a [[JsonExpression]] object. + * @param mixed $value the data to be encoded + * @param integer $options the encoding options. For more details please refer to + * [[http://www.php.net/manual/en/function.json-encode.php]] + * @return string the encoding result + */ + public static function encode($value, $options = 0) + { + $expressions = array(); + $value = static::processData($value, $expressions); + $json = json_encode($value, $options); + return $expressions === array() ? $json : strtr($json, $expressions); + } + + /** + * Decodes the given JSON string into a PHP data structure. + * @param string $json the JSON string to be decoded + * @param boolean $asArray whether to return objects in terms of associative arrays. + * @return mixed the PHP data + * @throws InvalidParamException if there is any decoding error + */ + public static function decode($json, $asArray = true) + { + if (is_array($json)) { + throw new InvalidParamException('Invalid JSON data.'); + } + $decode = json_decode((string)$json, $asArray); + switch (json_last_error()) { + case JSON_ERROR_NONE: + break; + case JSON_ERROR_DEPTH: + throw new InvalidParamException('The maximum stack depth has been exceeded.'); + case JSON_ERROR_CTRL_CHAR: + throw new InvalidParamException('Control character error, possibly incorrectly encoded.'); + case JSON_ERROR_SYNTAX: + throw new InvalidParamException('Syntax error.'); + case JSON_ERROR_STATE_MISMATCH: + throw new InvalidParamException('Invalid or malformed JSON.'); + case JSON_ERROR_UTF8: + throw new InvalidParamException('Malformed UTF-8 characters, possibly incorrectly encoded.'); + default: + throw new InvalidParamException('Unknown JSON decoding error.'); + } + + return $decode; + } + + /** + * Pre-processes the data before sending it to `json_encode()`. + * @param mixed $data the data to be processed + * @param array $expressions collection of JavaScript expressions + * @return mixed the processed data + */ + protected static function processData($data, &$expressions) + { + if (is_array($data)) { + foreach ($data as $key => $value) { + if (is_array($value) || is_object($value)) { + $data[$key] = static::processData($value, $expressions); + } + } + return $data; + } elseif (is_object($data)) { + if ($data instanceof JsonExpression) { + $token = '!{[' . count($expressions) . ']}!'; + $expressions['"' . $token . '"'] = $data->expression; + return $token; + } + $result = array(); + foreach ($data as $key => $value) { + if (is_array($value) || is_object($value)) { + $result[$key] = static::processData($value, $expressions); + } else { + $result[$key] = $value; + } + } + return $result; + } else { + return $data; + } + } +} \ No newline at end of file diff --git a/tests/unit/framework/helpers/JsonTest.php b/tests/unit/framework/helpers/JsonTest.php new file mode 100644 index 0000000..3459a54 --- /dev/null +++ b/tests/unit/framework/helpers/JsonTest.php @@ -0,0 +1,60 @@ +assertSame('"1"', Json::encode($data)); + + // simple array encoding + $data = array(1, 2); + $this->assertSame('[1,2]', Json::encode($data)); + $data = array('a' => 1, 'b' => 2); + $this->assertSame('{"a":1,"b":2}', Json::encode($data)); + + // simple object encoding + $data = new \stdClass(); + $data->a = 1; $data->b = 2; + $this->assertSame('{"a":1,"b":2}', Json::encode($data)); + + // expression encoding + $expression = 'function () {}'; + $data = new JsonExpression($expression); + $this->assertSame($expression, Json::encode($data)); + + // complex data + $expression1 = 'function (a) {}'; + $expression2 = 'function (b) {}'; + $data = array( + 'a' => array( + 1, new JsonExpression($expression1) + ), + 'b' => new JsonExpression($expression2), + ); + $this->assertSame("{\"a\":[1,$expression1],\"b\":$expression2}", Json::encode($data)); + } + + public function testDecode() + { + // basic data decoding + $json = '"1"'; + $this->assertSame('1', Json::decode($json)); + + // array decoding + $json = '{"a":1,"b":2}'; + $this->assertSame(array('a' => 1, 'b' => 2), Json::decode($json)); + + // exception + $json = '{"a":1,"b":2'; + $this->setExpectedException('yii\base\InvalidParamException'); + Json::decode($json); + } +} \ No newline at end of file