PHP userland backwards compatibility layer that emulates PHP 5.5+ core functions.

⌈⌋ ⎇ branch:  upgrade.php


Check-in [41bd525d03]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:implemented json_last_error, PHP 5.4 functions: hex2bin, gzdecode retagged 5.4, stub functions for traits
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 41bd525d0339fa453af966a5e573f2827554f2c4
User & Date: mario 2012-01-24 19:59:00
Context
2012-01-26
14:49
http_response_code, http_redirect, http_send_content_type, zlib_decode, zlib_encode, session_status check-in: 2e9586cd8e user: mario tags: trunk
2012-01-24
19:59
implemented json_last_error, PHP 5.4 functions: hex2bin, gzdecode retagged 5.4, stub functions for traits check-in: 41bd525d03 user: mario tags: trunk
2012-01-16
16:01
single quoted strings are parsed like double quoted now, octal escapes implemented, \b bell escape fixed (srsly, who needs that?), added failure messages in JSON standard parsing mode for redundant backslashes check-in: 2a658b49be user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to doc/devtools/cmpversion.

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
 * assemble list of defined functions/etc
 *
 */
function get_defined_stuff() {
   $rl = array();
   $f = get_defined_functions();
   while ($f["internal"]) {
      $f = $f["internal"];
   }
   $rl["functions"] = $f;
   $c = get_defined_constants();
   $rl["constants"] = $c;
   $c = get_declared_classes();
   $rl["classes"] = $c;
   $rl["version"] = PHP_VERSION;
   return($rl);
}


?>







|













61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
 * assemble list of defined functions/etc
 *
 */
function get_defined_stuff() {
   $rl = array();
   $f = get_defined_functions();
   while (@$f["internal"]) {
      $f = $f["internal"];
   }
   $rl["functions"] = $f;
   $c = get_defined_constants();
   $rl["constants"] = $c;
   $c = get_declared_classes();
   $rl["classes"] = $c;
   $rl["version"] = PHP_VERSION;
   return($rl);
}


?>

Changes to doc/devtools/cmpversion.data.

cannot compute difference between binary files

Changes to upgrade.php.

55
56
57
58
59
60
61
62


63


64
65





































































66
67
68
69
70
71
72
73
74
 *    contrib/xmlentities
 *
 */







/**


 * @since 6.0
 *





































































 * Inflates a string enriched with gzip headers. Counterpart to gzencode().
 * Not yet in any Zend-PHP.
 *
 */
if (!function_exists("gzdecode")) {
   function gzdecode($gzdata, $maxlen=NULL) {

      #-- decode header
      $len = strlen($gzdata);








>
>

>
>
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<







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
 *    contrib/xmlentities
 *
 */







/**
 *                                   ----------------------------- 5.4 ---
 * @group 5_4
 * @since 5.4
 *
 * Extensions in PHP 5.4
 *
 * @emulated
 *    gzdecode
 *    hex2bin
 *
 * @stub
 *    class_uses
 *    trait_exists
 *    get_declared_traits
 *
 * @missing
 *    libxml_set_external_entity_loader
 *    zlib_encode
 *    zlib_decode
 *    imageantialias
 *    imagecreatefromxbm
 *    imagelayereffect
 *    imagexbm
 *    session_status
 *    session_register_shutdown
 *    socket_import_stream
 *    getimagesizefromstring
 *    header_register_callback
 *    http_response_code
 *    stream_set_chunk_size
 */



/**
 * 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);
   }
}


/**
 * @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);
131
132
133
134
135
136
137



138
139
140
141
142
143
144
         trigger_error("gzdecode: stream size mismatch", E_USER_WARNING);
      }
      else {
         return($gzdata);
      }
   }
}








/**
 *                                   ----------------------------- 5.3 ---







>
>
>







203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
         trigger_error("gzdecode: stream size mismatch", E_USER_WARNING);
      }
      else {
         return($gzdata);
      }
   }
}








/**
 *                                   ----------------------------- 5.3 ---
514
515
516
517
518
519
520
521
522
523
524
525
526
527


528


529
530
531
532
533
534
535
536
537
538

539
540
541
542
543
544
545
 *    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 (!function_exists("json_encode")) {

   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);


   define("JSON_NUMERIC_CHECK", 32);      // 5.3.3


   define("JSON_UNESCAPED_SLASHES", 64);  // 5.4.0
   define("JSON_PRETTY_PRINT", 128);      // 5.4.0
   define("JSON_UNESCAPED_UNICODE", 256); // 5.4.0
   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);


   function json_encode($var, $options=0, $_indent="") {

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







<
|





>
>

>
>



<
<
<
<
<
<
|
>







589
590
591
592
593
594
595

596
597
598
599
600
601
602
603
604
605
606
607
608
609






610
611
612
613
614
615
616
617
618
 *    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="") {

      #-- prepare JSON string
      $obj = ($options & JSON_FORCE_OBJECT);
      list($_space, $_tab, $_nl) = ($options & JSON_PRETTY_PRINT) ? array(" ", "    $_indent", "\n") : array("", "", "");
      $json = "$_indent";
      
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
         if (!utf8_decode($var)) {
            trigger_error("json_encode: invalid UTF-8 encoding in string, cannot proceed.", E_USER_WARNING);
            $var = NULL;
         }
         $rewrite = array(
             "\\" => "\\\\",
             "\"" => "\\\"",
             "\b" => "\\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);
         if (1 or function_exists("iconv") && ($options & JSON_UNESCAPED_UNICODE) == 0) {
            $var = preg_replace("/[^\\x{0000}-\\x{007F}]/ue", "'\\u'.current(unpack('H*', iconv('UTF-8', 'UCS-2BE', '$0')))", $var);
         }
         if ($options & 0x8000) {//@COMPAT: for fully-fully-compliance
            $var = preg_replace("/[\000-\037]/", "", $var);
         }
         $json = '"' . $var . '"';
      }







|












|







647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
         if (!utf8_decode($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);
         if (function_exists("iconv") && ($options & JSON_UNESCAPED_UNICODE) == 0) {
            $var = preg_replace("/[^\\x{0000}-\\x{007F}]/ue", "'\\u'.current(unpack('H*', iconv('UTF-8', 'UCS-2BE', '$0')))", $var);
         }
         if ($options & 0x8000) {//@COMPAT: for fully-fully-compliance
            $var = preg_replace("/[\000-\037]/", "", $var);
         }
         $json = '"' . $var . '"';
      }
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
      
      #-- done
      return($json);
   }
}



/**
 * 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.







<







691
692
693
694
695
696
697

698
699
700
701
702
703
704
      
      #-- done
      return($json);
   }
}



/**
 * 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.
646
647
648
649
650
651
652

653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
if (!function_exists("json_decode")) {

   define("JSON_OBJECT_AS_ARRAY", 1);     // undocumented
   define("JSON_BIGINT_AS_STRING", 2);    // 5.4.0
   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) {


      #-- result var
      $val = NULL;
      $FAILURE = array(NULL, 1<<31);
      static $lang_eq = array("true" => TRUE, "false" => FALSE, "null" => NULL);
      static $str_eq = array("n"=>"\012", "r"=>"\015", "\\"=>"\\", '"'=>'"', "f"=>"\f", "b"=>"\010", "t"=>"\t", "/"=>"/");
      if ($limit<0) return /* __cannot_compensate */;
      
      #-- 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*/; $n<strlen($json); /*n*/) {
         $c = $json[$n];

         #-= in-string
         if ($state==='"' or $state==="'") {

            if ($c == '\\') {
               $c = $json[++$n];







>



|


|








|







718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
if (!function_exists("json_decode")) {

   define("JSON_OBJECT_AS_ARRAY", 1);     // undocumented
   define("JSON_BIGINT_AS_STRING", 2);    // 5.4.0
   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;

      #-- result var
      $val = NULL;
      $FAILURE = array(/*$val:=*/ NULL, /*$n:=*/ 1<<31);
      static $lang_eq = array("true" => TRUE, "false" => FALSE, "null" => NULL);
      static $str_eq = array("n"=>"\012", "r"=>"\015", "\\"=>"\\", '"'=>'"', "f"=>"\f", "b"=>"\010", "t"=>"\t", "/"=>"/");
      if ($limit<0) { ${'.json_last_error'} = JSON_ERROR_DEPTH; return /* __cannot_compensate */; }
      
      #-- 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];
707
708
709
710
711
712
713

714
715
716
717
718
719
720
721
722
723





724
725
726
727
728
729
730
731
732
                     $val .= $c;
                  }
               }
               
               // redundant backslashes disallowed in JSON
               else {
                  $val .= "\\$c";

                  trigger_error("Invalid backslash escape for JSON \\$c", E_USER_WARNING);
                  return $FAILURE;
               }
            }

            // end of string
            elseif ($c == $state) {
               $state = 0;
            }






            // a single character was found
            else/*if (ord($c) >= 32)*/ { //@COMPAT: specialchars check - but native json doesn't do it?
               $val .= $c;
            }
         }

         #-> end of sub-call (array/object)
         elseif ($waitfor && (strpos($waitfor, $c) !== false)) {
            return array($val, $n);  // return current value and state







>










>
>
>
>
>

|







780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
                     $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
827
828
829
830
831
832
833

834
835
836
837
838
839
840
841
842
843
844
845
846
847
848





















849
850
851
852
853
854
855
               ($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);

               return $waitfor ? $FAILURE : NULL;
            }

         }//state
         
         #-- next char
         if ($n === NULL) { return NULL; }   // ooops, seems we have two failure modes
         $n++;
      }//for

      #-- final result
      return ($val);
   }
}

























/**
 * @stub
 *
 * Should return last PCRE error.







>






|








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
               ($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);
}
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.