<?php
/**
* api: php
* title: upgrade.php
* description: Emulates functions from new PHP versions on older interpreters.
* version: 20
* license: Public Domain
* url: http://freshmeat.net/projects/upgradephp
* type: functions
* category: library
* priority: auto
* load_if: (PHP_VERSION<5.3)
* 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 woth native support exists. It's just meant
* as quick drop-in solution. It spares you from rewriting code or using
* cumbersome workarounds instead of the richer 5.3+ function set.
*
* 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
*
*/
/**
* ------------------------ OVERRIDE ---
*
*/
if (defined("UPGRADEPHP_OVERRIDE") and function_exists("runkit_function_remove")) {
ini_set("runkit.internal_override", 1);
runkit_function_rename("json_encode", "php::json_encode");
runkit_function_rename("json_decode", "php::json_decode");
}
/**
* ----------------------------- 5.5 ---
* @group 5_5
* @since 5.5
*
* Extensions in PHP 5.5
*
* @emulated
* boolval
* array_column
* json_last_error_msg
*
* @missing
* date_create_immutable
* date_create_immutable_from_format
* openssl_pbkdf2
* hash_pbkdf2
*
*/
/**
* Creates a new array from excerpting columns from a list of arrays. Optionally
* uses a key column from there for indexing.
*
* Full reimplementation at https://github.com/ramsey/array_column/blob/master/src/array_column.php
*
*/
if (!function_exists("array_column")) {
function array_column($array, $column, $key=NULL) {
$result = array();
assert('isset($column) /*array_column() expects two params*/')
and assert('is_scalar($column) && is_scalar($key) /*array_column() key and column should be ints/strings*/')
and assert('is_array($array) /*array_column() input array isn\'t one*/');
// traverse array
foreach ($array as $row) {
if (isset($row[$column])) {
// fetch as ordered list
if (($key === NULL) || !isset($row[$key])) { // this is odd in the native implementation, if the $key column is absent, it just appends
$result[] = $row[$column];
}
// or retain key value from another column for indexing
else {
//isset($result[$row[$key]]) and trigger_error("array_column(): a key occured twice, value was overwritten", E_USER_NOTICE);
$result[ $row[$key] ] = $row[$column];
}
}
}
return $result;
}
}
/**
* That's basically just a typecast. Returns the PHP-interpreted booleanish equivalent of values.
* Just exists for parity with intval/floatval/etc.
*
*/
if (!function_exists("boolval")) {
function boolval($var) {
return (bool)$var;
}
}
/**
* Convert json_last_error() numbers into readable string:
*
*/
if (!function_exists("json_last_error_msg")) {
function json_last_error_msg($num) {
$msgs = array(
JSON_ERROR_NONE => "No error has occurred",
JSON_ERROR_DEPTH => "The maximum stack depth has been exceeded",
JSON_ERROR_STATE_MISMATCH => "Invalid or malformed JSON",
JSON_ERROR_CTRL_CHAR => "Control character error, possibly incorrectly encoded",
JSON_ERROR_SYNTAX => "Syntax error",
JSON_ERROR_UTF8 => "Malformed UTF-8 characters, possibly incorrectly encoded",
JSON_ERROR_RECURSION => "One or more recursive references in the value to be encoded",
JSON_ERROR_INF_OR_NAN => "One or more NAN or INF values in the value to be encoded",
JSON_ERROR_UNSUPPORTED_TYPE => "A value of a type that cannot be encoded was given",
);
return $msgs[$num];
}
}
/**
* ----------------------------- 5.4 ---
* @group 5_4
* @since 5.4
*
* Extensions in PHP 5.4
*
* @emulated
* gzdecode
* hex2bin
* session_status -> basic probing
*
* @stub
* class_uses
* trait_exists
* get_declared_traits
*
* @missing
* libxml_set_external_entity_loader
* zlib_encode -> PHP_ZLIB_ENCODE_FUNC(zlib_encode, 0);
* zlib_decode -> PHP_ZLIB_DECODE_FUNC(zlib_decode, PHP_ZLIB_ENCODING_ANY);
* session_register_shutdown
* socket_import_stream
* getimagesizefromstring
* header_register_callback
* http_response_code
* stream_set_chunk_size
* CallbackFilterIterator
* RecursiveCallbackFilterIterator
* SessionHandler
* ReflectionZendExtension
*
* @unimplementable
* imageantialias
* imagelayereffect
*
*/
/**
* Simple convenience function for pack H*,
* Converts a hextuplet string into its binary representation.
*
*/
if (!function_exists("hex2bin")) {
function hex2bin($hex) {
return pack("H*", $hex);
}
}
/**
* Set or get HTTP status code.
*
*/
if (!function_exists("http_response_code")) {
function http_response_code($which=NULL) {
$cgi = ini_get("cgi.rfc2616_headers");
$headers = preg_grep("#^Status:|^HTTP/\d\.\d#i", headers_list());
# override
if ($which >= 100 and $which <= 999) {
if ($headers) {
header_remove(current($headers));
}
# implicit notices for headers_sent()
header($cgi ? "HTTP/1.0 $which n/a" : "Status: $which n/a");
}
# get current
elseif ($which == NULL) {
if (!$headers || !preg_match("/\d\d\d/", current($headers), $m)) {
return 200; #default
}
return intval($m[0]);
}
else trigger_error("invalid status number", E_USER_WARNING);
}
}
/**
* Sends a Location: header. Unlike the PHP-builtin, it won't complete relative URLs.
* So it's RFC6616 compliant, not anal about the original HTTP/1.1 revision RFC2616.
* The <meta> fallback is also extraneous.
* Belongs to http extensions actually. (Will be grouped out in later upgradephp revision.)
*
*/
if (!function_exists("http_redirect")) {
function http_redirect($url, $params=array(), $session=false, $status=0) {
if ($session) {
$params[session_name()] = session_id();
}
if ($params) {
$url .= strstr($url, "?") ? "&" : "?";
$url .= http_build_query($params);
}
header("Location: $url"); #, $status ? $status : 301);
$url = htmlspecialchars($url, ENT_QUOTES, "UTF-8");
print "Redirecting to <a href=\"$url\">$url</a>\n";
print "<meta http-equiv=\"Location\" content=\"$url\" />\n";
exit; // built-in exit
}
}
/**
* Sends a Content-Type: header
* Belongs to http extensions actually. (Will be grouped out in later upgradephp revision.)
*
*/
if (!function_exists("http_send_content_type")) {
function http_send_content_type($content_type="application/x-octetstream") {
return header("Content-Type: $content_type");
}
}
/**
* @stub
* Traits (partial classes, elaborate syntactic and academic workaround, because MI is
* hard to implement in *compiled* languages) cannot be emulated in older interpreters.
*
*/
if (!function_exists("class_uses")) {
function class_uses($trait) {
return false;
}
}
if (!function_exists("trait_exists")) {
function trait_exists($trait) {
return false;
}
}
if (!function_exists("get_declared_traits")) {
function get_declared_traits($trait) {
return (array)NULL;
}
}
/**
* Long predicted, officially available @since 5.4.
*
* Inflates a string enriched with gzip headers. Counterpart to gzencode().
*
*/
if (!function_exists("gzdecode")) {
function gzdecode($gzdata, $maxlen=NULL) {
#-- decode header
$len = strlen($gzdata);
if ($len < 20) {
return;
}
$head = substr($gzdata, 0, 10);
$head = unpack("n1id/C1cm/C1flg/V1mtime/C1xfl/C1os", $head);
list($ID, $CM, $FLG, $MTIME, $XFL, $OS) = array_values($head);
$FTEXT = 1<<0;
$FHCRC = 1<<1;
$FEXTRA = 1<<2;
$FNAME = 1<<3;
$FCOMMENT = 1<<4;
$head = unpack("V1crc/V1isize", substr($gzdata, $len-8, 8));
list($CRC32, $ISIZE) = array_values($head);
#-- check gzip stream identifier
if ($ID != 0x1f8b) {
trigger_error("gzdecode: not in gzip format", E_USER_WARNING);
return;
}
#-- check for deflate algorithm
if ($CM != 8) {
trigger_error("gzdecode: cannot decode anything but deflated streams", E_USER_WARNING);
return;
}
#-- start of data, skip bonus fields
$s = 10;
if ($FLG & $FEXTRA) {
$s += $XFL;
}
if ($FLG & $FNAME) {
$s = strpos($gzdata, "\000", $s) + 1;
}
if ($FLG & $FCOMMENT) {
$s = strpos($gzdata, "\000", $s) + 1;
}
if ($FLG & $FHCRC) {
$s += 2; // cannot check
}
#-- get data, uncompress
$gzdata = substr($gzdata, $s, $len-$s);
if ($maxlen) {
$gzdata = gzinflate($gzdata, $maxlen);
return($gzdata); // no checks(?!)
}
else {
$gzdata = gzinflate($gzdata);
}
#-- check+fin
$chk = crc32($gzdata);
if ($CRC32 != $chk) {
trigger_error("gzdecode: checksum failed (real$chk != comp$CRC32)", E_USER_WARNING);
}
elseif ($ISIZE != strlen($gzdata)) {
trigger_error("gzdecode: stream size mismatch", E_USER_WARNING);
}
else {
return($gzdata);
}
}
}
/**
* Probes for format(?) before decoding with one of the gz* functions.
*
*/
if (!function_exists("zlib_decode")) {
function zlib_decode($data) {
if (!strncmp($data, "\x1F\x8B", 2)) {
return gzdecode($data);
}
elseif (!strncmp($data, "\x78\x9C", 2)) {
return gzuncompress($data);
}
else {
return gzinflate($data);
}
}
}
/**
* Weird constants, not documented in the manual yet, but that's how the function declaration looks.
*
*/
if (!function_exists("zlib_encode")) {
if (!defined("ZLIB_ENCODING_DEFLATE")) {
define("ZLIB_ENCODING_DEFLATE", 15);
define("ZLIB_ENCODING_RAW", -15);
define("ZLIB_ENCODING_GZIP", 31);
}
function zlib_encode($data, $method) {
if ($method == ZLIB_ENCODING_RAW) {
return gzdeflate($data);
}
elseif ($method == ZLIB_ENCODING_DEFLATE) {
return gzcompress($data);
}
elseif ($method == ZLIB_ENCODING_GZIP) {
return gzencode($data);
}
else trigger_error("encoding mode must be either ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE", E_USER_WARNING);
}
}
/**
* @stub Tests whether a session is established.
*
*/
if (!function_exists("session_status")) {
if (!defined("PHP_SESSION_DISABLED")) {
define("PHP_SESSION_DISABLED", 0);
define("PHP_SESSION_NONE", 1);
define("PHP_SESSION_ACTIVE", 2);
}
function session_status() {
return (ini_get("session.name") != "") ? PHP_SESSION_DISABLED :
(isset($_SESSION) ? PHP_SESSION_ACTIVE : PHP_SESSION_NONE);
}
}
/**
* ----------------------------- 5.3 ---
* @group 5_3
* @since 5.3
*
* Known additions of PHP 5.3
*
* @emulated
* ob_get_headers (stub)
* preg_filter
* lcfirst
* class_alias
* header_remove
* parse_ini_string
* array_replace
* array_replace_recursive
* str_getcsv
* forward_static_call
* forward_static_call_array
* quoted_printable_encode
* E_DEPRECATED
* E_USER_DEPRECATED
*
* @missing
* get_called_class
* stream_context_get_params
* stream_context_set_default
* stream_supports_lock
* hash_copy
* date_create_from_format
* date_parse_from_format
* date_get_last_errors
* date_add
* date_sub
* date_diff
* date_timestamp_set
* date_timestamp_get
* timezone_location_get
* date_interval_create_from_date_string
* date_interval_format
*
*/
/**
* @since PHP 5.3.0
*/
if (!defined('E_DEPRECATED')) { define('E_DEPRECATED', 8192); }
if (!defined('E_USER_DEPRECATED')) { define('E_USER_DEPRECATED', 16384); }
/**
* preg_replace() variant, which filters out any unmatched $subject.
*
*/
if (!function_exists("preg_filter")) {
function preg_filter($pattern, $replacement, $subject, $limit=-1, $count=NULL) {
// just do the replacing first, and eventually filter later
$r = preg_replace($pattern, $replacement, $subject, $limit, $count);
// look at subject lines one-by-one, remove from result per index
foreach ((array)$subject as $si=>$s) {
$any = 0;
foreach ((array)$pattern as $p) {
$any = $any ||preg_match($p, $s);
}
// remove if NONE of the patterns matched
if (!$any) {
if (is_array($r)) {
unset($r[$si]); // del from result array
}
else {
return NULL; // subject was a str
}
}
}
return $r; // is already string if $subject was too
}
}
/**
* Lowercase first character.
*
* @param string
* @return string
*/
if (!function_exists("lcfirst")) {
function lcfirst($str) {
return strlen($str) ? strtolower($str[0]) . substr($str, 1) : "";
}
}
/**
* @stub cannot be emulated, because output buffering functions
* already swallow up any sent http header
* @since 5.3.?
*
* get all ob_ soaked headers(),
*
*/
if (!function_exists("ob_get_headers")) {
function ob_get_headers() {
return (array)NULL;
}
}
/**
* @stub Cannot be emulated correctly, but let's try.
*
*/
if (!function_exists("header_remove")) {
function header_remove($name="") {
if (strlen($name) and ($name = preg_replace("/[^-_.\w\d]+/", "", $name))) header("$name: \t");
// Apache1.3? removed duplettes, empty header overrides previous.
// ONLY if case was identical to previous header() call. (Very uncertain for applications which need to resort to such code smell.)
}
}
/**
* WTF?
* At least an explaning reference was available on the php.net manual.
* Why the parameters are supposed to be optional is a mystery.
*
*/
if (!function_exists("class_alias")) {
function class_alias($original, $alias) {
$abstract = "";
if (class_exists("ReflectionClass")) {
$oc = new ReflectionClass($original);
$abstract = $oc->isAbstract() ? "abstract" : "";
}
eval("$abstract class $alias extends $original { /* identical subclass */ }");
return get_parent_class($alias) == $original;
}
}
/**
* Hey, reimplementin is fun.
* (Could have used a data: wrapper for parse_ini_file, but that wouldn't work for php<5.2, and the data:// (!) wrapper is flaky anyway.)
*
*/
if (!function_exists("parse_ini_string")) {
function parse_ini_string($ini, $sectioned=false, $raw=0) {
$r = array();
$map = array("true"=>1, "yes"=>1, "1"=>1, "null"=>"", "false"=>"", "no"=>"", "0"=>0);
$section = "";
foreach (explode("\n", $ini) as $line) {
if (!strlen($line)) {
}
// handle [sections]
elseif (($line[0] == "[") and preg_match("/\[([-_\w ]+)\]/", $line, $uu)) {
$section = $uu[1];
}
elseif (/*deprecated*/($line[0] != "#") && ($line[0] != ";") && ($i = strpos($line, "="))) {
// key=value split
$n = trim(substr($line, 0, $i));
$v = trim(substr($line, $i+1));
// replace special values
if (!$raw) {
$v=trim($v, '"'); // should actually use regex, to handle key="..\n.." multiline values
$v=trim($v, "'");
if (isset($map[$v])) {
$v=$map[$v];
}
}
// special array[]= keys allowed
if ($i = strpos($n, "[")) {
$r[$section][substr($n, 0, $i)][] = $v;
}
else {
$r[$section][$n] = $v;
}
}
}
return $sectioned ? $r : call_user_func_array("array_merge", $r);
}
}
/**
* Inject values from supplemental arrays into $target, according to its keys.
*
* @param array $targt
* @param+ array $supplements
* @return array
*/
if (!function_exists("array_replace")) {
function array_replace(/* & (?) */$target/*, $from, $from2, ...*/) {
$merge = func_get_args();
array_shift($merge);
foreach ($merge as $add) {
foreach ($add as $i=>$v) {
$target[$i] = $v;
}
}
return $target;
}
}
/**
* Descends into sub-arrays when replacing values by key in $target array.
*
*/
if (!function_exists("array_replace_recursive")) {
function array_replace_recursive($target/*, $from1, $from2, ...*/) {
$merge = func_get_args();
array_shift($merge);
// loop through all merge arrays
foreach ($merge as $from) {
foreach ($from as $i=>$v) {
// just add (wether array or scalar) if key does not exist yet
if (!isset($target[$i])) {
$target[$i] = $v;
}
// dive in
elseif (is_array($v) && is_array($target[$i])) {
$target[$i] = array_replace_recursive($target[$i], $v);
}
// replace
else {
$target[$i] = $v;
}
}
}
return $target;
}
}
/**
* Breaks up a SINGLE LINE in CSV format.
* abc,123,"text with spaces",xy,"\""
*
*/
if (!function_exists("str_getcsv")) {
function str_getcsv($line, $del=",", $q='"', $esc="\\", $rm_spaces="\s*") {
$line = rtrim($line, "\r\n");
preg_match_all("/\G $rm_spaces ([^$q$del]*?) $rm_spaces $del | $q(( [$esc$esc][$q]|[^$q]* )+)$q \s* $del /xms", $line.$del, $r);
foreach ($r[1] as $i=>$v) { // merge both captures
if (empty($v) && strlen($r[2][$i])) {
$r[1][$i] = str_replace("$esc$q", "$q", $r[2][$i]); // remove escape character
} # use stripcslashes to support standard CSV \r \n escapes
}
return($r[1]);
}
}
/**
* @stub: Basically aliases for function calls; just throw an error if called from main() and not from within a class.
* The real implementations would behave on late static binding, though.
*
*/
if (!function_exists("forward_static_call")) {
function forward_static_call_array($callback, $args=NULL) {
return call_user_func_array($callback, $args);
}
function forward_static_call($callback /*, ... */) {
$args = func_get_args();
array_shift($args);
return call_user_func_array($callback, $args);
}
}
/**
* Encodes special chars as =0D=0A patterns. Soft-break at 76 characters.
*
*/
if (!function_exists("quoted_printable_encode")) {
function quoted_printable_encode($str) {
$str = preg_replace_callback("/([\\000-\\041=\\176-\\377])/", "quoted_printable_encode___hexquote", $str);
$str = preg_replace("/(.{1,76})(?<=[^=][^=])/ims", "\$1=\r\n", $str); // QP-soft-break
return $str;
}
function quoted_printable_encode___hexquote($match) {
return "=" . strtoupper(dechex(ord($match[0])));
}
}
/**
* ------------------------------ 5.2 ---
* @group 5_2
* @since 5.2
*
* Additions of PHP 5.2.0
* - some listed here might have appeared earlier or in release candidates
*
* @emulated
* json_encode
* json_decode
* error_get_last
* preg_last_error
* lchown
* lchgrp
* E_RECOVERABLE_ERROR
* M_SQRTPI
* M_LNPI
* M_EULER
* M_SQRT3
* array_fill_keys (@doc: 4.2 or 5.2 ?)
* array_diff_key (@doc: 5.1 or 5.2 ?)
* array_diff_ukey
* array_product
* inet_ntop
* inet_pton
* array_intersect_key
* array_intersect_ukey
* mysql_set_charset
*
* @missing
* sys_getloadavg
* ftp_ssl_connect
* XmlReader
* XmlWriter
* PDO*
* pdo_drivers (should be in ext/pdo)
*
* @unimplementable
* stream_*
*
*/
/**
* @since unknown
*/
if (!defined("E_RECOVERABLE_ERROR")) { define("E_RECOVERABLE_ERROR", 4096); }
/**
* 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
}
if (!function_exists("json_encode")) {
function json_encode($var, $options=0, $_indent="") {
global ${'.json_last_error'};
${'.json_last_error'} = JSON_ERROR_NONE;
#-- prepare JSON string
$obj = ($options & JSON_FORCE_OBJECT);
list($_space, $_tab, $_nl) = ($options & JSON_PRETTY_PRINT) ? array(" ", " $_indent", "\n") : array("", "", "");
$json = "$_indent";
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))) {
#-- check if array is associative
if (!$obj) {
$keys = array_keys((array)$var);
$obj = !($keys == array_keys($keys)); // keys must be in 0,1,2,3, ordering, but PHP treats integers==strings otherwise
}
#-- concat individual entries
$empty = 0; $json = "";
foreach ((array)$var as $i=>$v) {
$json .= ($empty++ ? ",$_nl" : "") // comma separators
. $_tab . ($obj ? (json_encode((string)$i, $options & ~JSON_NUMERIC_CHECK, $_tab) . ":$_space") : "") // assoc prefix
. (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 (!preg_match("/^.+$/su", $var)) {
trigger_error("json_encode: invalid UTF-8 encoding in string, 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_QUOT ? "\\u0022" : "\\\"",
"&" => $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_callback("/[^\\x{0020}-\\x{007F}]/u", "json_encode___utf8_escape", $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;
}
else {
$json = "$var";
}
}
#-- something went wrong
else {
trigger_error("json_encode: don't know what a '" .gettype($var). "' is.", E_USER_WARNING);
${'.json_last_error'} = JSON_ERROR_UNSUPPORTED_TYPE;
return;
}
#-- done
return($json);
}
function json_encode___utf8_escape($match) {
return "\\u" . current(unpack("H*", iconv("UTF-8", "UCS-2BE", $match[0])));
}
}
/**
* Parses a JSON (JavaScript value expression) string into a PHP variable
* (array or object).
*
* @compat
* Behaves similar to PECL version, but is less quiet on errors.
* Now even decodes unicode \uXXXX string escapes into UTF-8.
* "Only" 27 times slower than native function.
* @bugs
* Might parse some misformed representations, when other implementations
* would scream error or explode.
* @code
* This is state machine spaghetti code. Needs the extranous parameters to
* process subarrays, etc. When it recursively calls itself, $n is the
* current position, and $waitfor a string with possible end-tokens.
*
* @param $json string JSON encoded values
* @param $assoc bool pack data into php array/hashes instead of objects
* @return mixed parsed into PHP variable/array/object
*/
if (!function_exists("json_decode")) {
defined("JSON_OBJECT_AS_ARRAY") or define("JSON_OBJECT_AS_ARRAY", 1); // undocumented
defined("JSON_BIGINT_AS_STRING") or define("JSON_BIGINT_AS_STRING", 2); // 5.4.0
defined("JSON_PARSE_JAVASCRIPT") or define("JSON_PARSE_JAVASCRIPT", 4); // unquoted object keys, and single quotes ' strings identical to double quoted, more relaxed parsing
function json_decode($json, $assoc=FALSE, $limit=512, $options=0, /*emu_args*/$n=0,$state=0,$waitfor=0) {
global ${'.json_last_error'};
${'.json_last_error'} = JSON_ERROR_NONE;
#-- maximum nesting depth for decoding
if ($limit < 0) {
${'.json_last_error'} = JSON_ERROR_DEPTH;
return; // fall through
}
#-- result var
$val = NULL;
// shortcut state for parsing errors
$FAILURE = array(
NULL, // result var
1<<31 // tokenizer position
);
// transliterations from JSON to PHP values
static $lang_eq = array("true" => TRUE, "false" => FALSE, "null" => NULL);
static $str_eq = array("n"=>"\012", "r"=>"\015", "\\"=>"\\", '"'=>'"', "f"=>"\f", "b"=>"\010", "t"=>"\t", "/"=>"/");
#-- strip UTF-8 BOM (the native version doesn't do this, but .. should)
while (strncmp($json, "\xEF\xBB\xBF", 3) == 0) {
trigger_error("UTF-8 BOM prefaces JSON, that's invalid for PHPs native json_decode", E_USER_ERROR);
$json = substr($json, 3);
}
#-- flat char-wise parsing
for (/*$n=0,*/ $len = strlen($json); $n<$len; /*$n++*/) {
$c = $json[$n];
#-= in-string
if ($state==='"' or $state==="'") {
if ($c == '\\') {
$c = $json[++$n];
// simple C escapes
if (isset($str_eq[$c])) {
$val .= $str_eq[$c];
}
// here we transform \uXXXX Unicode (always 4 nibbles) references to UTF-8
elseif ($c == "u") {
// read just 16bit (therefore value can't be negative)
$hex = hexdec( substr($json, $n+1, 4) );
$n += 4;
// Unicode ranges
if ($hex < 0x80) { // plain ASCII character
$val .= chr($hex);
}
elseif ($hex < 0x800) { // 110xxxxx 10xxxxxx
$val .= chr(0xC0 + $hex>>6) . chr(0x80 + $hex&63);
}
elseif ($hex <= 0xFFFF) { // 1110xxxx 10xxxxxx 10xxxxxx
$val .= chr(0xE0 + $hex>>12) . chr(0x80 + ($hex>>6)&63) . chr(0x80 + $hex&63);
}
// other ranges, like 0x1FFFFF=0xF0, 0x3FFFFFF=0xF8 and 0x7FFFFFFF=0xFC do not apply
}
// for JS (not JSON) the extraneous backslash just gets omitted
elseif ($options & JSON_PARSE_JAVASCRIPT) {
if (is_numeric($c) and preg_match("/[0-3][0-7][0-7]|[0-7]{1,2}/", substr($json, $n), $m)) {
$val .= chr(octdec($m[0]));
$n += strlen($m[0]) - 1;
}
else {
$val .= $c;
}
}
// redundant backslashes disallowed in JSON
else {
$val .= "\\$c";
${'.json_last_error'} = JSON_ERROR_CTRL_CHAR; // not quite, but
trigger_error("Invalid backslash escape for JSON \\$c", E_USER_WARNING);
return $FAILURE;
}
}
// end of string
elseif ($c == $state) {
$state = 0;
}
//@COMPAT: specialchars check - but native json doesn't do it?
#elseif (ord($c) < 32) && !in_array($c, $str_eq)) {
# ${'.json_last_error'} = JSON_ERROR_CTRL_CHAR;
#}
// a single character was found
else/*if (ord($c) >= 32)*/ {
$val .= $c;
}
}
#-> end of sub-call (array/object)
elseif ($waitfor && (strpos($waitfor, $c) !== false)) {
return array($val, $n); // return current value and state
}
#-= in-array
elseif ($state===']') {
list($v, $n) = json_decode($json, $assoc, $limit, $options, $n, 0, ",]");
$val[] = $v;
if ($json[$n] == "]") { return array($val, $n); }
}
#-= in-object
elseif ($state==='}') {
// quick regex parsing cheat for unquoted JS object keys
if ($options & JSON_PARSE_JAVASCRIPT and $c != '"' and preg_match("/^\s*(?!\d)(\w\pL*)\s*/u", substr($json, $n), $m)) {
$i = $m[1];
$n = $n + strlen($m[0]);
}
else {
// this allowed non-string indicies
list($i, $n) = json_decode($json, $assoc, $limit, $options, $n, 0, ":");
}
list($v, $n) = json_decode($json, $assoc, $limit, $options, $n+1, 0, ",}");
$val[$i] = $v;
if ($json[$n] == "}") { return array($val, $n); }
}
#-- looking for next item (0)
else {
#-> whitespace
if (preg_match("/\s/", $c)) {
// skip
}
#-> string begin
elseif ($c == '"') {
$state = $c;
}
#-> object
elseif ($c == "{") {
list($val, $n) = json_decode($json, $assoc, $limit-1, $options, $n+1, '}', "}");
if ($val && $n) {
$val = $assoc ? (array)$val : (object)$val;
}
}
#-> array
elseif ($c == "[") {
list($val, $n) = json_decode($json, $assoc, $limit-1, $options, $n+1, ']', "]");
}
#-> numbers
elseif (preg_match("#^(-?\d+(?:\.\d+)?)(?:[eE]([-+]?\d+))?#", substr($json, $n), $uu)) {
$val = $uu[1];
$n += strlen($uu[0]) - 1;
if (strpos($val, ".")) { // float
$val = floatval($val);
}
elseif ($val[0] == "0") { // oct
$val = octdec($val);
}
else {
$toobig = strval(intval($val)) !== strval($val);
if ($toobig and !isset($uu[2]) and ($options & JSON_BIGINT_AS_STRING)) {
$val = $val; // keep lengthy numbers as string
}
elseif ($toobig or isset($uu[2])) { // must become float anyway
$val = floatval($val);
}
else { // int
$val = intval($val);
}
}
// exponent?
if (isset($uu[2])) {
$val *= pow(10, (int)$uu[2]);
}
}
#-> boolean or null
elseif (preg_match("#^(true|false|null)\b#", substr($json, $n), $uu)) {
$val = $lang_eq[$uu[1]];
$n += strlen($uu[1]) - 1;
}
#-> JS-string begin
elseif ($options & JSON_PARSE_JAVASCRIPT and $c == "'") {
$state = $c;
}
#-> comment
elseif ($options & JSON_PARSE_JAVASCRIPT and ($c == "/") and ($json[$n+1]=="*")) {
// just find end, skip over
($n = strpos($json, "*/", $n+1)) or ($n = strlen($json));
}
#-- parsing error
else {
// PHPs native json_decode() breaks here usually and QUIETLY
trigger_error("json_decode: error parsing '$c' at position $n", E_USER_WARNING);
${'.json_last_error'} = JSON_ERROR_SYNTAX;
return $waitfor ? $FAILURE : NULL;
}
}//state
#-- next char
if ($n === NULL) { ${'.json_last_error'} = JSON_ERROR_STATE_MISMATCH; return NULL; } // ooops, seems we have two failure modes
$n++;
}//for
#-- final result
return ($val);
}
}
/**
* @stub
*
* Should return last JSON decoding error.
*
*/
if (!defined("JSON_ERROR_NONE")) {
define("JSON_ERROR_NONE", 0);
define("JSON_ERROR_DEPTH", 1);
define("JSON_ERROR_STATE_MISMATCH", 2);
define("JSON_ERROR_CTRL_CHAR", 3);
define("JSON_ERROR_SYNTAX", 4);
define("JSON_ERROR_UTF8", 5);
define("JSON_ERROR_RECURSION", 6);
define("JSON_ERROR_INF_OR_NAN", 7);
define("JSON_ERROR_UNSUPPORTED_TYPE", 7);
}
if (!function_exists("json_last_error")) {
function json_last_error() {
global ${'.json_last_error'};
return ${'.json_last_error'}; // gives a notice if json_decode was never invoked before (no status constant for that)
}
}
/**
* @stub
*
* Should return last PCRE error.
*
*/
if (!function_exists("preg_last_error")) {
if (!defined("PREG_NO_ERROR")) { define("PREG_NO_ERROR", 0); }
if (!defined("PREG_INTERNAL_ERROR")) { define("PREG_INTERNAL_ERROR", 1); }
if (!defined("PREG_BACKTRACK_LIMIT_ERROR")) { define("PREG_BACKTRACK_LIMIT_ERROR", 2); }
if (!defined("PREG_RECURSION_LIMIT_ERROR")) { define("PREG_RECURSION_LIMIT_ERROR", 3); }
if (!defined("PREG_BAD_UTF8_ERROR")) { define("PREG_BAD_UTF8_ERROR", 4); }
function preg_last_error() {
return PREG_NO_ERROR;
}
}
/**
* returns path of the system directory for temporary files
*
* @since 5.2.1
*/
if (!function_exists("sys_get_temp_dir")) {
function sys_get_temp_dir() {
# check possible alternatives
($temp = ini_get("temp_dir"))
or
($temp = @$_ENV["TMPDIR"])
or
($temp = @$_ENV["TEMP"])
or
($temp = @$_ENV["TMP"])
or
($temp = "/tmp");
# fin
return($temp);
}
}
/**
* @stub
*
* Should return associative array with last error message.
*
*/
if (!function_exists("error_get_last")) {
function error_get_last() {
return array(
"type" => 0,
"message" => $GLOBALS["php_errormsg"],
"file" => "unknonw",
"line" => 0,
);
}
}
/**
* @flag quirky, exec, realmode
*
* Change owner of a symlink filename.
*
*/
if (!function_exists("lchown")) {
function lchown($fn, $user) {
if (PHP_OS != "Linux") {
return false;
}
$user = escapeshellcmd($user);
$fn = escapeshellcmd($fn);
exec("chown -h '$user' '$fn'", $uu, $state);
return($state);
}
}
/**
* @flag quirky, exec, realmode
*
* Change group of a symlink filename.
*
*/
if (!function_exists("lchgrp")) {
function lchgrp($fn, $group) {
return lchown($fn, ":$group");
}
}
/**
* @doc: Got this function new in PHP 5.2, but documentation says 4.2 ???
*
* array_fill() with given $keys
*
*/
if (!function_exists("array_fill_keys")) {
function array_fill_keys($keys, $value) {
return array_combine($keys, array_fill(0, count($keys), $value));
}
}
/**
* @doc: php manual says 5.1, but function appeared with 5.2
*
* Returns array entries, whose keys are not in any of the comparison arrays.
*
*/
if (!function_exists("array_diff_key")) {
function array_diff_key($base /*...*/) {
$other = func_get_args();
array_shift($other);
$cmp = call_user_func_array("array_merge", array_map("array_keys", $other));
foreach ($cmp as $key) {
$key = (string) $key;
if (array_key_exists($key, $base)) {
// cannot compare if $key is actually a string in $base
unset($base[$key]);
}
}
return ($base);
}
}
/**
* @doc: php manual says 5.1, but function appeared with 5.2
*
* Uses callback function to compare array keys.
* Callback returns -1, 0, +1, and then some keys are filtered???
* Let's assume ==0 is meant for no difference --> and no difference => filter out
*
*/
if (!function_exists("array_diff_ukey")) {
function array_diff_ukey($base, $other_arrays/*...*/, $callback) {
$other = func_get_args();
array_shift($other);
$callback = array_pop($other);
$cmp = call_user_func_array("array_merge", array_map("array_keys", $other));
foreach ($base as $key=>$value) {
// compare against each key from $other arrays
foreach ($cmp as $k) {
if ($callback($key, $k) === 0) {
unset($base[$key]);
}
}
}
return $base;
}
}
/**
* @doc: 5.1 vs 5.2
*
* Keeps only array-entries, if key exists also in comparison arrays
*
*/
if (!function_exists("array_intersect_key")) {
function array_intersect_key($base /*...*/) {
$all_arrays = array_map("array_keys", func_get_args());
$keep = call_user_func_array("array_intersect", $all_arrays);
$r = array();
foreach ($keep as $k) {
$r[$k] = $base[$k];
}
return ($r);
}
}
/**
* @doc: 5.1 vs 5.2
*
* array_uintersect on keys
*
*/
if (!function_exists("array_intersect_ukey")) {
function array_intersect_ukey(/*...*/) {
$args = func_get_args();
$base = $args[0];
$callback = array_pop($other);
$keys = array_map("array_values", $args);
$intersect = call_user_func_array("array_uintersect", array_merge($keys, array($callback)));
$r = array();
foreach ($intersect as $key) {
$r[$key] = $base[$key];
}
return $r;
}
}
/**
* Hmmm.
*
*/
if (!function_exists("array_product")) {
function array_product($multiply_us) {
$r = count($multiply_us) ? 1 : NULL;
foreach ($multiply_us as $m) {
$r = $r * $m;
}
return $r;
}
}
/**
* Converts chr/bin/string-representation to human-readable IP text.
*
*/
if (!function_exists("inet_ntop")) {
function inet_ntop($bin) {
if (strlen($bin) == 4) { // IPv4
return implode(".", array_map("ord", str_split($bin, 1)));
}
elseif (strlen($bin) == 16) { // IPv6
return preg_replace("/:?(0000:)+/", "::", implode(":", str_split(bin2hex($bin), 4)));
}
elseif (strlen($bin) == 6) { // MAC
return implode(":", str_split(bin2hex($bin), 2));
}
}
}
/**
* Compact IPv4 1.2.3.4 or IPv6 ::FFFF:0001 addresses into binary string.
*
*/
if (!function_exists("inet_pton")) {
function inet_pton($str) {
if (strpos($str, ".")) { // IPv4
return array_map("chr", explode(".", $str));
}
elseif (strstr($str, ":")) { // IPv6
$str = str_replace("::", str_repeat(":", 2 + 7 - substr_count($str, ":")), $str); // padding "::" can appear anywhere inside, replaces 7-x other :0000 colons and zeros
$str = implode(array_map("inet_pton___ipv6_pad", explode(":", $str)));
return pack("H32", $str);
}
}
function inet_pton___ipv6_pad($s) {
return str_pad($s, 4, "0", STR_PAD_LEFT);
}
}
/**
* @since 5.2.3
* SET NAMES $charset
*
*/
if (!function_exists("mysql_set_charset")) {
function mysql_set_charset($charset, $link=NULL) {
return mysqli_query("SET NAMES '$charset'", $link);
}
}
/**
* ------------------------------ 5.1 ---
* @group 5_1
* @since 5.1
*
* Additions in PHP 5.1
* - most functions here appeared in -rc1 already
* - and were backported to 4.4 series?
*
* @emulated
* hash_hmac
* property_exists
* time_sleep_until
* fputcsv
* strptime
* ENT_COMPAT
* ENT_QUOTES
* ENT_NOQUOTES
* htmlspecialchars_decode
* PHP_INT_SIZE
* PHP_INT_MAX
* M_SQRTPI
* M_LNPI
* M_EULER
* M_SQRT3
*
* @missing
* strptime
*
* @unimplementable
* ...
*
*/
/**
* HMAC as per rfc2104,
* only works with PHP-available "md5" and "sha1" algorithm backends
*
*/
if (!function_exists("hash_hmac")) {
function hash_hmac($H, $data, $key, $raw=0) {
# algorithm parameters
static $bitsize = array("sha1"=>160, "md5"=>128, "sha256"=>256, "sha512"=>512, "sha384"=>384, "sha224"=>224, "ripemd"=>160);
$B = 64;
# bring key to block size 64, hash it first if longer
if (strlen($key) > $B) {
$key = $H($key, 1);
}
$key .= str_repeat("\0", $B - strlen($key));
# padding, XOR with key
$inner_pad = "";
$outer_pad = "";
for ($i=0; $i<$B; $i++) {
$inner_pad .= chr(0x36 ^ ord($key[$i]));
$outer_pad .= chr(0x5C ^ ord($key[$i]));
}
# apply hash
$data = $H($outer_pad . $H($inner_pad . $data, 1), 1);
# bin2hex
return $raw ? $data : bin2hex($data);
}
}
/**
* Constants for future 64-bit integer support.
*
*/
if (!defined("PHP_INT_SIZE")) { define("PHP_INT_SIZE", 4); }
if (!defined("PHP_INT_MAX")) { define("PHP_INT_MAX", 2147483647); }
/**
* @flag bugfix
* @see #33895
*
* Missing constants in 5.1, originally appeared in 4.0.
*/
if (!defined("M_SQRTPI")) { define("M_SQRTPI", 1.7724538509055); }
if (!defined("M_LNPI")) { define("M_LNPI", 1.1447298858494); }
if (!defined("M_EULER")) { define("M_EULER", 0.57721566490153); }
if (!defined("M_SQRT3")) { define("M_SQRT3", 1.7320508075689); }
/**
* removes entities < > & and eventually " from HTML string
*
*/
if (!function_exists("htmlspecialchars_decode")) {
if (!defined("ENT_COMPAT")) { define("ENT_COMPAT", 2); }
if (!defined("ENT_QUOTES")) { define("ENT_QUOTES", 3); }
if (!defined("ENT_NOQUOTES")) { define("ENT_NOQUOTES", 0); }
function htmlspecialchars_decode($string, $quotes=2) {
$d = $quotes & ENT_COMPAT;
$s = $quotes & ENT_QUOTES;
return str_replace(
array("<", ">", ($s ? """ : "&.-;"), ($d ? "'" : "&.-;"), "&"),
array("<", ">", "'", "\"", "&"),
$string
);
}
}
/**
* @flag needs5
*
* Checks for existence of object property, should return TRUE even for NULL values.
*
* @compat
* no test for edge cases
*/
if (!function_exists("property_exists")) {
function property_exists($obj, $propname) {
if (is_object($obj)) {
$props = array_keys(get_object_vars($obj));
}
elseif (class_exists($obj)) {
$props = array_keys(get_class_vars($obj));
}
return !empty($props) and in_array($propname, $props);
}
}
/**
* halt execution, until given timestamp
*
*/
if (!function_exists("time_sleep_until")) {
function time_sleep_until($t) {
$delay = $t - time();
if ($delay < 0) {
trigger_error("time_sleep_until: timestamp in the past", E_USER_WARNING);
return false;
}
else {
sleep((int)$delay);
#usleep(($delay - floor($delay)) * 1000000);
return true;
}
}
}
/**
* @untested
*
* Writes an array as CSV text line into opened filehandle.
*
*/
if (!function_exists("fputcsv")) {
function fputcsv($fp, $fields, $delim=",", $encl='"') {
$line = "";
foreach ((array)$fields as $str) {
$line .= ($line ? $delim : "")
. $encl
. str_replace(array('\\', $encl), array('\\\\'. '\\'.$encl), $str)
. $encl;
}
fwrite($fp, $line."\n");
}
}
/**
* @flag basic
* @untested
*
* @compat
* only implements a few basic regular expression lookups
* no idea how to handle all of it
*/
if (!function_exists("strptime")) {
function strptime($str, $format) {
static $expand = array(
"%D" => "%m/%d/%y",
"%T" => "%H:%M:%S",
);
static $map_r = array(
"%S"=>"tm_sec",
"%M"=>"tm_min",
"%H"=>"tm_hour",
"%d"=>"tm_mday",
"%m"=>"tm_mon",
"%Y"=>"tm_year",
"%y"=>"tm_year",
"%W"=>"tm_wday",
"%D"=>"tm_yday",
"%u"=>"unparsed",
);
static $names = array(
"Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6,
"Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10, "Nov" => 11, "Dec" => 12,
"Sun" => 0, "Mon" => 1, "Tue" => 2, "Wed" => 3, "Thu" => 4, "Fri" => 5, "Sat" => 6,
);
#-- transform $format into extraction regex
$format = str_replace(array_keys($expand), array_values($expand), $format);
$preg = preg_replace("/(%\w)/", "(\w+)", preg_quote($format));
#-- record the positions of all STRFCMD-placeholders
preg_match_all("/(%\w)/", $format, $positions);
$positions = $positions[1];
#-- get individual values
if (preg_match("#$preg#", "$str", $extracted)) {
#-- get values
foreach ($positions as $pos=>$strfc) {
$v = $extracted[$pos + 1];
#-- add
if ($n = $map_r[$strfc]) {
$vals[$n] = ($v > 0) ? (int)$v : $v;
}
else {
$vals["unparsed"] .= $v . " ";
}
}
#-- fixup some entries
$vals["tm_wday"] = $names[ substr($vals["tm_wday"], 0, 3) ];
if ($vals["tm_year"] >= 1900) {
$tm_year -= 1900;
}
elseif ($vals["tm_year"] > 0) {
$vals["tm_year"] += 100;
}
if ($vals["tm_mon"]) {
$vals["tm_mon"] -= 1;
}
else {
$vals["tm_mon"] = $names[ substr($vals["tm_mon"], 0, 3) ] - 1;
}
#-- calculate wday
// ... (mktime)
}
return isset($vals) ? $vals : false;
}
}
/**
* ----------------------------- 5.0 ---
* @group 5_0
* @since 5.0
* @moved ext/php50.php
*
*
* Now even the 5.1 compatibility code is seriously redundant in 2015,
* so the 4.1 till 5.0 have been extracted out into ext/php50.php
*
*/
?>