summaryrefslogtreecommitdiffstats
path: root/inc/up_json_encode.php
blob: ac47ef51c432e791611318270ee2688962a9ee08 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
<?php

if (defined('JSON_PRETTY_PRINT'))
	define('JSON_NATIVE', true);
else
	define('JSON_NATIVE', false);

/**
 * api:		php
 * title:	upgrade.php
 * description:	Emulates functions from new PHP versions on older interpreters.
 * version:	19
 * license:	Public Domain
 * url:		http://freshmeat.net/projects/upgradephp
 * type:	functions
 * category:	library
 * priority:	auto
 * load_if:     (PHP_VERSION<5.2)
 * sort:	-255
 * provides:	upgrade-php, api:php5, json
 *
 *
 * By loading this library you get PHP version independence. It provides
 * downwards compatibility to older PHP interpreters by emulating missing
 * functions or constants using IDENTICAL NAMES. So this doesn't slow down
 * script execution on setups where the native functions already exist. It
 * is meant as quick drop-in solution. It spares you from rewriting code or
 * using cumbersome workarounds instead of the more powerful v5 functions.
 * 
 * It cannot mirror PHP5s extended OO-semantics and functionality into PHP4
 * however. A few features are added here that weren't part of PHP yet. And
 * some other function collections are separated out into the ext/ directory.
 * It doesn't produce many custom error messages (YAGNI), and instead leaves
 * reporting to invoked functions or for native PHP execution.
 * 
 * And further this is PUBLIC DOMAIN (no copyright, no license, no warranty)
 * so therefore compatible to ALL open source licenses. You could rip this
 * paragraph out to republish this instead only under more restrictive terms
 * or your favorite license (GNU LGPL/GPL, BSDL, MPL/CDDL, Artistic/PHPL, ..)
 *
 * Any contribution is appreciated. <milky*users#sf#net>
 *
 */
/**
 *                                   -------------------------- FUTURE ---
 * @group SVN
 * @since future
 *
 * Following functions aren't implemented in current PHP versions, but
 * might already be in CVS/SVN.
 *
 * @removed
 *    setcookie2
 *
 */
/**
 * Converts PHP variable or array into a "JSON" (JavaScript value expression
 * or "object notation") string.
 *
 * @compat
 *    Output seems identical to PECL versions. "Only" 20x slower than PECL version.
 * @bugs
 *    Doesn't take care with unicode too much - leaves UTF-8 sequences alone.
 *
 * @param  $var mixed  PHP variable/array/object
 * @return string      transformed into JSON equivalent
 */
if (!defined("JSON_HEX_TAG")) {
	define("JSON_HEX_TAG", 1);
	define("JSON_HEX_AMP", 2);
	define("JSON_HEX_APOS", 4);
	define("JSON_HEX_QUOT", 8);
	define("JSON_FORCE_OBJECT", 16);
}
if (!defined("JSON_NUMERIC_CHECK")) {
	define("JSON_NUMERIC_CHECK", 32);		// 5.3.3
}
if (!defined("JSON_UNESCAPED_SLASHES")) {
	define("JSON_UNESCAPED_SLASHES", 64);  // 5.4.0
	define("JSON_PRETTY_PRINT", 128);		// 5.4.0
	define("JSON_UNESCAPED_UNICODE", 256); // 5.4.0
}

function up_json_encode($var, $options = 0, $_indent = "")
{
	if (defined('JSON_NATIVE') && JSON_NATIVE)
		return json_encode($var, $options);
	global ${'.json_last_error'};
	${'.json_last_error'} = JSON_ERROR_NONE;

	#-- prepare JSON string
	list($_space, $_tab, $_nl) = ($options & JSON_PRETTY_PRINT) ? array(" ", "    $_indent", "\n") : array("", "", "");

	if (($options & JSON_NUMERIC_CHECK) and is_string($var) and is_numeric($var)) {
		$var = (strpos($var, ".") || strpos($var, "e")) ? floatval($var) : intval($var);
	}

	#-- add array entries
	if (is_array($var) || ($obj = is_object($var))) {
		$obj = is_object($var);
		#-- check if array is associative
		if (!$obj && !($options & JSON_FORCE_OBJECT)) {
			$keys = array_keys($var);
			sort($keys);
			for ($i = 0; $i < count($keys); ++$i) {
				if (!is_numeric($keys[$i]) || (int)$keys[$i] !== $i)
					$obj = true;
			}
		} else {
			$obj = true;
		}

		#-- concat individual entries
		$empty = 0;
		$json = "";
		foreach ((array) $var as $i => $v) {
			$json .= ($empty++ ? ",$_nl" : "")	 // comma separators
				. $_tab . ($obj ? (up_json_encode((string)$i, $options & ~JSON_NUMERIC_CHECK, $_tab) . ":$_space") : "")	// assoc prefix
				. (up_json_encode($v, $options, $_tab));	 // value
		}

		#-- enclose into braces or brackets
		$json = $obj ? "{" . "$_nl$json$_nl$_indent}" : "[$_nl$json$_nl$_indent]";
	}

	#-- strings need some care
	elseif (is_string($var)) {

		if (!empty($var) && mb_detect_encoding($var, 'UTF-8', true) === false) {
			trigger_error("up_json_encode: invalid UTF-8 encoding in string '$var', cannot proceed.", E_USER_WARNING);
			$var = NULL;
		}
		$rewrite = array(
			"\\" => "\\\\",
			"\"" => "\\\"",
			"\010" => "\\b",
			"\f" => "\\f",
			"\n" => "\\n",
			"\r" => "\\r",
			"\t" => "\\t",
			"/" => $options & JSON_UNESCAPED_SLASHES ? "/" : "\\/",
			"<" => $options & JSON_HEX_TAG ? "\\u003C" : "<",
			">" => $options & JSON_HEX_TAG ? "\\u003E" : ">",
			"'" => $options & JSON_HEX_APOS ? "\\u0027" : "'",
			"&" => $options & JSON_HEX_AMP ? "\\u0026" : "&",
		);
		$var = strtr($var, $rewrite);
		//@COMPAT control chars should probably be stripped beforehand, not escaped as here
		if (function_exists("iconv") && ($options & JSON_UNESCAPED_UNICODE) == 0) {
			$var = preg_replace("/[^\\x{0020}-\\x{007F}]/ue", "'\\u'.current(unpack('H*', iconv('UTF-8', 'UCS-2BE', '$0')))", $var);
		}
		$json = '"' . $var . '"';
	}

	#-- basic types
	elseif (is_bool($var)) {
		$json = $var ? "true" : "false";
	} elseif ($var === NULL) {
		$json = "null";
	} elseif (is_int($var)) {
		$json = "$var";
	} elseif (is_float($var)) {
		if (is_nan($var) || is_infinite($var)) {
			${'.json_last_error'} = JSON_ERROR_INF_OR_NAN;
			return false;
		} else {
			$json = "$var";
		}
	}

	#-- something went wrong
	else {
		trigger_error("up_json_encode: don't know what a '" . gettype($var) . "' is.", E_USER_WARNING);
		${'.json_last_error'} = JSON_ERROR_UNSUPPORTED_TYPE;
		return false;
	}

	#-- done
	return($json);
}