Page MenuHomePhabricator

D13444.id32543.diff
No OneTemporary

D13444.id32543.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -435,6 +435,7 @@
'phutil_is_utf8_with_only_bmp_characters' => 'utils/utf8.php',
'phutil_is_windows' => 'utils/utils.php',
'phutil_json_decode' => 'utils/utils.php',
+ 'phutil_json_encode' => 'utils/utils.php',
'phutil_load_library' => 'moduleutils/core.php',
'phutil_loggable_string' => 'utils/utils.php',
'phutil_parse_bytes' => 'utils/viewutils.php',
@@ -461,6 +462,7 @@
'phutil_utf8v' => 'utils/utf8.php',
'phutil_utf8v_codepoints' => 'utils/utf8.php',
'phutil_utf8v_combined' => 'utils/utf8.php',
+ 'phutil_validate_json' => 'utils/utils.php',
'phutil_var_export' => 'utils/utils.php',
'ppull' => 'utils/utils.php',
'qsprintf' => 'xsprintf/qsprintf.php',
diff --git a/src/utils/__tests__/PhutilUtilsTestCase.php b/src/utils/__tests__/PhutilUtilsTestCase.php
--- a/src/utils/__tests__/PhutilUtilsTestCase.php
+++ b/src/utils/__tests__/PhutilUtilsTestCase.php
@@ -777,4 +777,19 @@
}
}
+ public function testJSONEncode() {
+ $in = array(
+ 'example' => "Not Valid UTF8: \x80",
+ );
+
+ $caught = null;
+ try {
+ $value = phutil_json_encode($in);
+ } catch (Exception $ex) {
+ $caught = $ex;
+ }
+
+ $this->assertTrue(($caught instanceof Exception));
+ }
+
}
diff --git a/src/utils/utils.php b/src/utils/utils.php
--- a/src/utils/utils.php
+++ b/src/utils/utils.php
@@ -1052,6 +1052,119 @@
/**
+ * Encode a value in JSON, raising an exception if it can not be encoded.
+ *
+ * @param wild A value to encode.
+ * @return string JSON representation of the value.
+ */
+function phutil_json_encode($value) {
+ $result = @json_encode($value);
+ if ($result === false) {
+ $reason = phutil_validate_json($value);
+ if (function_exists('json_last_error')) {
+ $err = json_last_error();
+ if (function_exists('json_last_error_msg')) {
+ $msg = json_last_error_msg();
+ $extra = pht('#%d: %s', $err, $msg);
+ } else {
+ $extra = pht('#%d', $err);
+ }
+ } else {
+ $extra = null;
+ }
+
+ if ($extra) {
+ $message = pht(
+ 'Failed to JSON encode value (%s): %s.',
+ $extra,
+ $reason);
+ } else {
+ $message = pht(
+ 'Failed to JSON encode value: %s.',
+ $reason);
+ }
+
+ throw new Exception($message);
+ }
+
+ return $result;
+}
+
+
+/**
+ * Produce a human-readable explanation why a value can not be JSON-encoded.
+ *
+ * @param wild Value to validate.
+ * @param string Path within the object to provide context.
+ * @return string|null Explanation of why it can't be encoded, or null.
+ */
+function phutil_validate_json($value, $path = '') {
+ if ($value === null) {
+ return;
+ }
+
+ if ($value === true) {
+ return;
+ }
+
+ if ($value === false) {
+ return;
+ }
+
+ if (is_int($value)) {
+ return;
+ }
+
+ if (is_float($value)) {
+ return;
+ }
+
+ if (is_array($value)) {
+ foreach ($value as $key => $subvalue) {
+ if (strlen($path)) {
+ $full_key = $path.' > ';
+ } else {
+ $full_key = '';
+ }
+
+ if (!phutil_is_utf8($key)) {
+ $full_key = $full_key.phutil_utf8ize($key);
+ return pht(
+ 'Dictionary key "%s" is not valid UTF8, and can not be JSON encoded.',
+ $full_key);
+ }
+
+ $full_key .= $key;
+ $result = phutil_validate_json($subvalue, $full_key);
+ if ($result !== null) {
+ return $result;
+ }
+ }
+ }
+
+ if (is_string($value)) {
+ if (!phutil_is_utf8($value)) {
+ $display = substr($value, 0, 256);
+ $display = phutil_utf8ize($display);
+ if (!strlen($path)) {
+ return pht(
+ 'String value is not valid UTF8, and can not be JSON encoded: %s',
+ $display);
+ } else {
+ return pht(
+ 'Dictionary value at key "%s" is not valid UTF8, and can not be '.
+ 'JSON encoded: %s',
+ $path,
+ $display);
+ }
+ }
+ }
+
+ return;
+}
+
+
+/**
* Decode an INI string.
*
* @param string

File Metadata

Mime Type
text/plain
Expires
Wed, Aug 6, 2:05 PM (16 h, 40 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
9027159
Default Alt Text
D13444.id32543.diff (4 KB)

Event Timeline