a66201fc5c2cb3fdffb3f36e0a57f8d00cdb08a0
[atutor.git] / mods / wiki / plugins / lib / upgrade.php
1 <?php
2 /*
3    This include() script adds missing PHP functions to earlier interpreter
4    versions, so you can make downwards compatible scripts without having
5    to stick to the least common denominator. It only defines the ones that
6    are really missing - the faster native functions will be used whenever
7    available.
8
9    - many of the emulation functions are one-liners
10    - a few features have been added that never made it into one of the
11      official versions (CVS code and the ever-absent "gzdecode" and
12      "file_put_contents" for example)
13    - a few very extravagant functions (array_u?diff*_u*assoc?) and other
14      extensions have been separated out into ext/
15    - the advanced OO-capabilities and language syntax extensions of PHP5
16      and ZE2 cannot seriously be emulated here, this script only takes care
17      of procedural interfaces
18    - with only this part loaded, you get "PHP 4.1 COMPATIBILITY"
19    - this is PuplicDomain (no copyright, no license, no warranty) so you
20      can melt it into anything, regardless of your preferred license (you
21      may strip this paragraph and turn it all into GPL, BSD, GNU LGPL,
22      Artistic, MPL, PHP license, M$ EULA, or whatever you like best)
23    
24    Get update notes via "http://freshmeat.net/projects/upgradephp" or
25    google for it. Any contribution is appreciated. <milky*users·sf·net>
26 */
27
28
29
30 #------------------------------------------------------------------ CVS ---
31 // most of this appeared in 5.0
32 // ...
33
34
35
36
37 #------------------------------------------------------------------ 6.0 ---
38 // following functions were never implemented in PHP
39
40
41 #-- inflates a string enriched with gzip headers
42 #   (this is the logical counterpart to gzencode(), but don't tell anyone!)
43 if (!function_exists("gzdecode")) {
44    function gzdecode($data, $maxlen=NULL) {
45
46       #-- decode header
47       $len = strlen($data);
48       if ($len < 20) {
49          return;
50       }
51       $head = substr($data, 0, 10);
52       $head = unpack("n1id/C1cm/C1flg/V1mtime/C1xfl/C1os", $head);
53       list($ID, $CM, $FLG, $MTIME, $XFL, $OS) = array_values($head);
54       $FTEXT = 1<<0;
55       $FHCRC = 1<<1;
56       $FEXTRA = 1<<2;
57       $FNAME = 1<<3;
58       $FCOMMENT = 1<<4;
59       $head = unpack("V1crc/V1isize", substr($data, $len-8, 8));
60       list($CRC32, $ISIZE) = array_values($head);
61
62       #-- check gzip stream identifier
63       if ($ID != 0x1f8b) {
64          trigger_error("gzdecode: not in gzip format", E_USER_WARNING);
65          return;
66       }
67       #-- check for deflate algorithm
68       if ($CM != 8) {
69          trigger_error("gzdecode: cannot decode anything but deflated streams", E_USER_WARNING);
70          return;
71       }
72
73       #-- start of data, skip bonus fields
74       $s = 10;
75       if ($FLG & $FEXTRA) {
76          $s += $XFL;
77       }
78       if ($FLG & $FNAME) {
79          $s = strpos($data, "\000", $s) + 1;
80       }
81       if ($FLG & $FCOMMENT) {
82          $s = strpos($data, "\000", $s) + 1;
83       }
84       if ($FLG & $FHCRC) {
85          $s += 2;  // cannot check
86       }
87       
88       #-- get data, uncompress
89       $data = substr($data, $s, $len-$s);
90       if ($maxlen) {
91          $data = gzinflate($data, $maxlen);
92          return($data);  // no checks(?!)
93       }
94       else {
95          $data = gzinflate($data);
96       }
97       
98       #-- check+fin
99       $chk = crc32($data);
100       if ($CRC32 != $chk) {
101          trigger_error("gzdecode: checksum failed (real$chk != comp$CRC32)", E_USER_WARNING);
102       }
103       elseif ($ISIZE != strlen($data)) {
104          trigger_error("gzdecode: stream size mismatch", E_USER_WARNING);
105       }
106       else {
107          return($data);
108       }
109    }
110 }
111
112
113 #-- get all already made headers(),
114 #   CANNOT be emulated, because output buffering functions
115 #   already swallow up any sent http header
116 if (!function_exists("ob_get_headers")) {
117    function ob_get_headers() {
118       return (array)NULL;
119    }
120 }
121
122
123 #-- encodes required named XML entities, like htmlentities(),
124 #   but does not re-encode numeric &#xxxx; character references
125 #   - could screw up scripts which then implement this themselves
126 #   - doesn't fix bogus or invalid numeric entities
127 if (!function_exists("xmlentities")) {
128    function xmlentities($str) {
129       return strtr($str, array(
130         "&#"=>"&#", "&"=>"&amp;", "'"=>"&apos;",
131         "<"=>"&lt;", ">"=>"&gt;", "\""=>"&quot;", 
132       ));
133    }
134 }
135
136
137
138 #------------------------------------------------------------------ 5.0 ---
139 # set_exception_handler - unimpl.
140 # restore_exception_handler - unimpl.
141 # debug_print_backtrace - unimpl.
142 # class_implements - unimplementable
143 # proc_terminate - unimpl?
144 # proc_get_status - unimpl.
145 # --
146 # proc_nice
147 # dns_get_record
148 # date_sunrise - undoc.
149 # date_sunset - undoc.
150
151
152
153 #-- constant: end of line
154 if (!defined("PHP_EOL")) {
155    define("PHP_EOL", ( (DIRECTORY_SEPARATOR == "\\") ?"\015\012" :(strncmp(PHP_OS,"D",1)?"\012":"\015") )  ); #"
156 }
157
158
159 #-- case-insensitive string search function,
160 #   - finds position of first occourence of a string c-i
161 #   - parameters identical to strpos()
162 if (!function_exists("stripos")) {
163    function stripos($haystack, $needle, $offset=NULL) {
164    
165       #-- simply lowercase args
166       $haystack = strtolower($haystack);
167       $needle = strtolower($needle);
168       
169       #-- search
170       $pos = strpos($haystack, $needle, $offset);
171       return($pos);
172    }
173 }
174
175
176 #-- case-insensitive string search function
177 #   - but this one starts from the end of string (right to left)
178 #   - offset can be negative or positive
179 if (!function_exists("strripos")) {
180    function strripos($haystack, $needle, $offset=NULL) {
181
182       #-- lowercase incoming strings
183       $haystack = strtolower($haystack);
184       $needle = strtolower($needle);
185
186       #-- [-]$offset tells to ignore a few string bytes,
187       #   we simply cut a bit from the right
188       if (isset($offset) && ($offset < 0)) {
189          $haystack = substr($haystack, 0, strlen($haystack) - 1);
190       }
191
192       #-- let PHP do it
193       $pos = strrpos($haystack, $needle);
194
195       #-- [+]$offset => ignore left haystack bytes
196       if (isset($offset) && ($offset > 0) && ($pos > $offset)) {
197          $pos = false;
198       }
199
200       #-- result      
201       return($pos);
202    }
203 }
204
205
206 #-- case-insensitive version of str_replace
207 if (!function_exists("str_ireplace")) {
208    function str_ireplace($search, $replace, $subject, $count=NULL) {
209
210       #-- call ourselves recursively, if parameters are arrays/lists 
211       if (is_array($search)) {
212          $replace = array_values($replace);
213          foreach (array_values($search) as $i=>$srch) {
214             $subject = str_ireplace($srch, $replace[$i], $subject);
215          }
216       }
217       
218       #-- sluice replacement strings through the Perl-regex module
219       #   (faster than doing it by hand)
220       else {
221          $replace = addcslashes($replace, "$\\");
222          $search = "{" . preg_quote($search) . "}i";
223          $subject = preg_replace($search, $replace, $subject);
224       }
225
226       #-- result
227       return($subject);
228    }
229 }
230
231
232 #-- performs a http HEAD request
233 if (!function_exists("get_headers")) {
234    function get_headers($url, $parse=0) {
235    
236       #-- extract URL parts ($host, $port, $path, ...)
237       $c = parse_url($url);
238       extract($c);
239       if (!isset($port)) { 
240          $port = 80;
241       }
242       
243       #-- try to open TCP connection      
244       $f = fsockopen($host, $port, $errno, $errstr, $timeout=15);
245       if (!$f) {
246          return;
247       }
248
249       #-- send request header
250       socket_set_blocking($f, true);
251       fwrite($f, "HEAD $path HTTP/1.0\015\012"
252                . "Host: $host\015\012"
253                . "Connection: close\015\012"
254                . "Accept: */*, xml/*\015\012"
255                . "User-Agent: ".trim(ini_get("user_agent"))."\015\012"
256                . "\015\012");
257
258       #-- read incoming lines
259       $ls = array();
260       while ( !feof($f) && ($line = trim(fgets($f, 1<<16))) ) {
261          
262          #-- read header names to make result an hash (names in array index)
263          if ($parse) {
264             if ($l = strpos($line, ":")) {
265                $name = substr($line, 0, $l);
266                $value = trim(substr($line, $l + 1));
267                #-- merge headers
268                if (isset($ls[$name])) {
269                   $ls[$name] .= ", $value";
270                }
271                else {
272                   $ls[$name] = $value;
273                }
274             }
275             #-- HTTP response status header as result[0]
276             else {
277                $ls[] = $line;
278             }
279          }
280          
281          #-- unparsed header list (numeric indices)
282          else {
283             $ls[] = $line;
284          }
285       }
286
287       #-- close TCP connection and give result
288       fclose($f);
289       return($ls);
290    }
291 }
292
293
294 #-- list of already/potentially sent HTTP responsee headers(),
295 #   CANNOT be implemented (except for Apache module maybe)
296 if (!function_exists("headers_list")) {
297    function headers_list() {
298       trigger_error("headers_list(): not supported by this PHP version", E_USER_WARNING);
299       return (array)NULL;
300    }
301 }
302
303
304 #-- write formatted string to stream/file,
305 #   arbitrary numer of arguments
306 if (!function_exists("fprintf")) {
307    function fprintf(/*...*/) {
308       $args = func_get_args();
309       $stream = array_shift($args);
310       return fwrite($stream, call_user_func_array("sprintf", $args));
311    }
312 }
313
314
315 #-- write formatted string to stream, args array
316 if (!function_exists("vfprintf")) {
317    function vfprintf($stream, $format, $args=NULL) {
318       return fwrite($stream, vsprintf($format, $args));
319    }
320 }
321
322
323 #-- splits a string in evenly sized chunks
324 #   and returns this as array
325 if (!function_exists("str_split")) {
326    function str_split($str, $chunk=1) {
327       $r = array();
328       
329       #-- return back as one chunk completely, if size chosen too low
330       if ($chunk < 1) {
331          $r[] = $str;
332       }
333       
334       #-- add substrings to result array until subject strings end reached
335       else {
336          $len = strlen($str);
337          for ($n=0; $n<$len; $n+=$chunk) {
338             $r[] = substr($str, $n, $chunk);
339          }
340       }
341       return($r);
342    }
343 }
344
345
346 #-- constructs a QUERY_STRING (application/x-www-form-urlencoded format, non-raw)
347 #   from a nested array/hash with name=>value pairs
348 #   - only first two args are part of the original API - rest used for recursion
349 if (!function_exists("http_build_query")) {
350    function http_build_query($data, $int_prefix="", $subarray_pfix="", $level=0) {
351    
352       #-- empty starting string
353       $s = "";
354       ($SEP = ini_get("arg_separator.output")) or ($SEP = "&");
355       
356       #-- traverse hash/array/list entries 
357       foreach ($data as $index=>$value) {
358          
359          #-- add sub_prefix for subarrays (happens for recursed innovocation)
360          if ($subarray_pfix) {
361             if ($level) {
362                $index = "[" . $index . "]";
363             }
364             $index =  $subarray_pfix . $index;
365          }
366          #-- add user-specified prefix for integer-indices
367          elseif (is_int($index) && strlen($int_prefix)) {
368             $index = $int_prefix . $index;
369          }
370          
371          #-- recurse for sub-arrays
372          if (is_array($value)) {
373             $s .= http_build_query($value, "", $index, $level + 1);
374          }
375          else {   // or just literal URL parameter
376             $s .= $SEP . $index . "=" . urlencode($value);
377          }
378       }
379       
380       #-- remove redundant "&" from first round (-not checked above to simplifiy loop)
381       if (!$subarray_pfix) {
382          $s = substr($s, strlen($SEP));
383       }
384
385       #-- return result / to previous array level and iteration
386       return($s);
387    }
388 }
389
390
391 #-- transform into 3to4 uuencode
392 #   - this is the bare encoding, not the uu file format
393 if (!function_exists("convert_uuencode")) {
394    function convert_uuencode($data) {
395
396       #-- init vars
397       $out = "";
398       $line = "";
399       $len = strlen($data);
400 #      $data .= "\252\252\252";   // PHP and uuencode(1) use some special garbage??, looks like "\000"* and "`\n`" simply appended
401
402       #-- canvass source string
403       for ($n=0; $n<$len; ) {
404       
405          #-- make 24-bit integer from first three bytes
406          $x = (ord($data[$n++]) << 16)
407             + (ord($data[$n++]) <<  8)
408             + (ord($data[$n++]) <<  0);
409             
410          #-- disperse that into 4 ascii characters
411          $line .= chr( 32 + (($x >> 18) & 0x3f) )
412                 . chr( 32 + (($x >> 12) & 0x3f) )
413                 . chr( 32 + (($x >>  6) & 0x3f) )
414                 . chr( 32 + (($x >>  0) & 0x3f) );
415                 
416          #-- cut lines, inject count prefix before each
417          if (($n % 45) == 0) {
418             $out .= chr(32 + 45) . "$line\n";
419             $line = "";
420          }
421       }
422
423       #-- throw last line, +length prefix
424       if ($trail = ($len % 45)) {
425          $out .= chr(32 + $trail) . "$line\n";
426       }
427
428       // uuencode(5) doesn't tell so, but spaces are replaced with the ` char in most implementations
429       $out = strtr("$out \n", " ", "`");
430       return($out);
431    }
432 }
433
434
435 #-- decodes uuencoded() data again
436 if (!function_exists("convert_uudecode")) {
437    function convert_uudecode($data) {
438
439       #-- prepare
440       $out = "";
441       $data = strtr($data, "`", " ");
442       
443       #-- go through lines
444       foreach(explode("\n", ltrim($data)) as $line) {
445          if (!strlen($line)) {
446             break;  // end reached
447          }
448          
449          #-- current line length prefix
450          unset($num);
451          $num = ord($line{0}) - 32;
452          if (($num <= 0) || ($num > 62)) {  // 62 is the maximum line length
453             break;          // according to uuencode(5), so we stop here too
454          }
455          $line = substr($line, 1);
456          
457          #-- prepare to decode 4-char chunks
458          $add = "";
459          for ($n=0; strlen($add)<$num; ) {
460          
461             #-- merge 24 bit integer from the 4 ascii characters (6 bit each)
462             $x = ((ord($line[$n++]) - 32) << 18)
463                + ((ord($line[$n++]) - 32) << 12)  // were saner with "& 0x3f"
464                + ((ord($line[$n++]) - 32) <<  6)
465                + ((ord($line[$n++]) - 32) <<  0);
466                
467             #-- reconstruct the 3 original data chars
468             $add .= chr( ($x >> 16) & 0xff )
469                   . chr( ($x >>  8) & 0xff )
470                   . chr( ($x >>  0) & 0xff );
471          }
472
473          #-- cut any trailing garbage (last two decoded chars may be wrong)
474          $out .= substr($add, 0, $num);
475          $line = "";
476       }
477
478       return($out);
479    }
480 }
481
482
483 #-- return array of filenames in a given directory
484 #   (only works for local files)
485 if (!function_exists("scandir")) {
486    function scandir($dirname, $desc=0) {
487    
488       #-- check for file:// protocol, others aren't handled
489       if (strpos($dirname, "file://") === 0) {
490          $dirname = substr($dirname, 7);
491          if (strpos($dirname, "localh") === 0) {
492             $dirname = substr($dirname, strpos($dirname, "/"));
493          }
494       }
495       
496       #-- directory reading handle
497       if ($dh = opendir($dirname)) {
498          $ls = array();
499          while ($fn = readdir($dh)) {
500             $ls[] = $fn;  // add to array
501          }
502          closedir($dh);
503          
504          #-- sort filenames
505          if ($desc) {
506             rsort($ls);
507          }
508          else {
509             sort($ls);
510          }
511          return $ls;
512       }
513
514       #-- failure
515       return false;
516    }
517 }
518
519
520 #-- like date(), but returns an integer for given one-letter format parameter
521 if (!function_exists("idate")) {
522    function idate($formatchar, $timestamp=NULL) {
523    
524       #-- reject non-simple type parameters
525       if (strlen($formatchar) != 1) {
526          return false;
527       }
528       
529       #-- get current time, if not given
530       if (!isset($timestamp)) {
531          $timestamp = time();
532       }
533       
534       #-- get and turn into integer
535       $str = date($formatchar, $timestamp);
536       return (int)$str;
537    }
538 }
539
540
541
542 #-- combined sleep() and usleep() 
543 if (!function_exists("time_nanosleep")) {
544    function time_nanosleep($sec, $nano) {
545       sleep($sec);
546       usleep($nano);
547    }
548 }
549
550
551
552 #-- search first occourence of any of the given chars, returns rest of haystack
553 #   (char_list must be a string for compatibility with the real PHP func)
554 if (!function_exists("strpbrk")) {
555    function strpbrk($haystack, $char_list) {
556    
557       #-- prepare
558       $len = strlen($char_list);
559       $min = strlen($haystack);
560       
561       #-- check with every symbol from $char_list
562       for ($n = 0; $n < $len; $n++) {
563          $l = strpos($haystack, $char_list{$n});
564          
565          #-- get left-most occourence
566          if (($l !== false) && ($l < $min)) {
567             $min = $l;
568          }
569       }
570       
571       #-- result
572       if ($min) {
573          return(substr($haystack, $min));
574       }
575       else {
576          return(false);
577       }
578    }
579 }
580
581
582
583 #-- logo image activation URL query strings (gaga feature)
584 if (!function_exists("php_real_logo_guid")) {
585    function php_real_logo_guid() { return php_logo_guid(); }
586    function php_egg_logo_guid() { return zend_logo_guid(); }
587 }
588
589
590 #-- no need to implement this
591 #   (there aren't interfaces in PHP4 anyhow)
592 if (!function_exists("get_declared_interfaces")) {
593    function get_declared_interfaces() {
594       trigger_error("get_declared_interfaces(): Current script won't run reliably with PHP4.", E_USER_WARNING);
595       return( (array)NULL );
596    }
597 }
598
599
600 #-- creates an array from lists of $keys and $values
601 #   (both should have same number of entries)
602 if (!function_exists("array_combine")) {
603    function array_combine($keys, $values) {
604    
605       #-- convert input arrays into lists
606       $keys = array_values($keys);
607       $values = array_values($values);
608       $r = array();
609       
610       #-- one from each
611       foreach ($values as $i=>$val) {
612          if ($key = $keys[$i]) {
613             $r[$key] = $val;
614          }
615          else {
616             $r[] = $val;   // useless, PHP would have long aborted here
617          }
618       }
619       return($r);
620    }
621 }
622
623
624 #-- apply userfunction to each array element (descending recursively)
625 #   use it like:  array_walk_recursive($_POST, "stripslashes");
626 #   - $callback can be static function name or object/method, class/method
627 if (!function_exists("array_walk_recursive")) {
628    function array_walk_recursive(&$input, $callback, $userdata=NULL) {
629       #-- each entry
630       foreach ($input as $key=>$value) {
631
632          #-- recurse for sub-arrays
633          if (is_array($value)) {
634             array_walk_recursive($input[$key], $callback, $userdata);
635          }
636
637          #-- $callback handles scalars
638          else {
639             call_user_func_array($callback, array(&$input[$key], $key, $userdata) );
640          }
641       }
642
643       // no return value
644    }
645 }
646
647
648 #-- complicated wrapper around substr() and and strncmp()
649 if (!function_exists("substr_compare")) {
650    function substr_compare($haystack, $needle, $offset=0, $len=0, $ci=0) {
651
652       #-- check params   
653       if ($len <= 0) {   // not well documented
654          $len = strlen($needle);
655          if (!$len) { return(0); }
656       }
657       #-- length exception
658       if ($len + $offset >= strlen($haystack)) {
659          trigger_error("substr_compare: given length exceeds main_str", E_USER_WARNING);
660          return(false);
661       }
662
663       #-- cut
664       if ($offset) {
665          $haystack = substr($haystack, $offset, $len);
666       }
667       #-- case-insensitivity
668       if ($ci) {
669          $haystack = strtolower($haystack);
670          $needle = strtolower($needle);
671       }
672
673       #-- do
674       return(strncmp($haystack, $needle, $len));
675    }
676 }
677
678
679 #-- stub, returns empty list as usual;
680 #   you must load "ext/spl.php" beforehand to get this
681 if (!function_exists("spl_classes")) {
682    function spl_classes() {
683       trigger_error("spl_classes(): not built into this PHP version");
684       return (array)NULL;
685    }
686 }
687
688
689
690 #-- gets you list of class names the given objects class was derived from, slow
691 if (!function_exists("class_parents")) {
692    function class_parents($obj) {
693    
694       #-- first get full list
695       $all = get_declared_classes();
696       $r = array();
697       
698       #-- filter out
699       foreach ($all as $potential_parent) {
700          if (is_subclass_of($obj, $potential_parent)) {
701             $r[$potential_parent] = $potential_parent;
702          }
703       }
704       return($r);
705    }
706 }
707
708
709 #-- an alias
710 if (!function_exists("session_commit") && function_exists("session_write_close")) {
711    function session_commit() {
712       // simple
713       session_write_close();
714    }
715 }
716
717
718 #-- aliases
719 if (!function_exists("dns_check_record")) {
720    function dns_check_record($host, $type=NULL) {
721       // synonym to
722       return checkdnsrr($host, $type);
723    }
724 }
725 if (!function_exists("dns_get_mx")) {
726    function dns_get_mx($host, $mx) {
727       $args = func_get_args();
728       // simple alias - except the optional, but referenced third parameter
729       if ($args[2]) {
730          $w = & $args[2];
731       }
732       else {
733          $w = false;
734       }
735       return getmxrr($host, $mx, $w);
736    }
737 }
738
739
740 #-- setrawcookie(),
741 #   can this be emulated 100% exactly?
742 if (!function_exists("setrawcookie")) {
743    // we output everything directly as HTTP header(), PHP doesn't seem
744    // to manage an internal cookie list anyhow
745    function setrawcookie($name, $value=NULL, $expire=NULL, $path=NULL, $domain=NULL, $secure=0) {
746       if (isset($value) && strpbrk($value, ",; \r\t\n\f\014\013")) {
747          trigger_error("setrawcookie: value may not contain any of ',; \r\n' and some other control chars; thrown away", E_USER_WARNING);
748       }
749       else {
750          $h = "Set-Cookie: $name=$value"
751             . ($expire ? "; expires=" . gmstrftime("%a, %d-%b-%y %H:%M:%S %Z", $expire) : "")
752             . ($path ? "; path=$path": "")
753             . ($domain ? "; domain=$domain" : "")
754             . ($secure ? "; secure" : "");
755          header($h);
756       }
757    }
758 }
759
760
761 #-- write-at-once file access (counterpart to file_get_contents)
762 if (!function_exists("file_put_contents")) {
763    function file_put_contents($filename, $data, $flags=0, $resource=NULL) {
764
765       #-- prepare
766       $mode = ($flags & FILE_APPEND ? "a" : "w" ) ."b";
767       $incl = $flags & FILE_USE_INCLUDE_PATH;
768       $length = strlen($data);
769
770       #-- open for writing
771       $f = fopen($filename, $mode, $incl);
772       if ($f) {
773          $written = fwrite($f, $data);
774          fclose($f);
775          
776          #-- only report success, if completely saved
777          return($length == $written);
778       }
779    }
780 }
781
782
783 #-- file-related constants
784 if (!defined("FILE_APPEND")) {
785    define("FILE_USE_INCLUDE_PATH", 1);
786    define("FILE_IGNORE_NEW_LINES", 2);
787    define("FILE_SKIP_EMPTY_LINES", 4);
788    define("FILE_APPEND", 8);
789    define("FILE_NO_DEFAULT_CONTEXT", 16);
790 }
791
792
793 #-- more new constants for 5.0
794 if (!defined("E_STRICT")) {
795    define("E_STRICT", 2048);  // _STRICT is a special case of _NOTICE (_DEBUG)
796    # PHP_CONFIG_FILE_SCAN_DIR
797 }
798
799
800 #-- array count_recursive()
801 if (!defined("COUNT_RECURSIVE")) {
802    define("COUNT_NORMAL", 0);       // count($array, 0);
803    define("COUNT_RECURSIVE", 1);    // not supported
804 }
805
806
807 #-- we introduce a new function, because we cannot emulate the
808 #   newly introduced second parameter to count()
809 if (!function_exists("count_recursive")) {
810    function count_recursive($array, $mode=1) {
811       if (!$mode) {
812          return(count($array));
813       }
814       else {
815          $c = count($array);
816          foreach ($array as $sub) {
817             if (is_array($sub)) {
818                $c += count_recursive($sub);
819             }
820          }
821          return($c);
822       }
823    }
824 }
825
826
827
828
829
830
831
832 #------------------------------------------------------------------ 4.3 ---
833 # money_format - unimpl?
834 # sha1, sha1_file - too much code to pack it into here; and this
835 #                   has already been implemented elsewhere, btw
836
837
838 #-- simplified file read-at-once function
839 if (!function_exists("file_get_contents")) {
840    function file_get_contents($filename, $use_include_path=1) {
841
842       #-- open file, let fopen() report error
843       $f = fopen($filename, "rb", $use_include_path);
844       if (!$f) { return; }
845
846       #-- read max 2MB
847       $content = fread($f, 1<<21);
848       fclose($f);
849       return($content);
850    }
851 }
852
853
854
855 #-- shell-like filename matching (* and ? globbing characters)
856 if (!function_exists("fnmatch")) {
857
858    #-- associated constants
859    define("FNM_PATHNAME", 1<<0);  // no wildcard ever matches a "/"
860    define("FNM_NOESCAPE", 1<<1);  // backslash can't escape meta chars
861    define("FNM_PERIOD",   1<<2);  // leading dot must be given explicit
862    define("FNM_LEADING_DIR", 1<<3);  // not in PHP
863    define("FNM_CASEFOLD", 0x50);  // match case-insensitive
864    define("FNM_EXTMATCH", 1<<5);  // not in PHP
865    
866    #-- implementation
867    function fnmatch($pattern, $str, $flags=0x0000) {
868       
869       #-- 'hidden' files
870       if ($flags & FNM_PERIOD) {
871          if (($str[0] == ".") && ($pattern[0] != ".")) {
872             return(false);    // abort early
873          }
874       }
875
876       #-- case-insensitivity
877       $rxci = "";
878       if ($flags & FNM_CASEFOLD) {
879          $rxci = "i";
880       }
881       #-- handline of pathname separators (/)
882       $wild = ".";
883       if ($flags & FNM_PATHNAME) {
884          $wild = "[^/".DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR."]";
885       }
886
887       #-- check for cached regular expressions
888       static $cmp = array();
889       if (isset($cmp["$pattern+$flags"])) {
890          $rx = $cmp["$pattern+$flags"];
891       }
892
893       #-- convert filename globs into regex
894       else {
895          $rx = preg_quote($pattern);
896          $rx = strtr($rx, array(
897             "\\*"=>"$wild*?", "\\?"=>"$wild", "\\["=>"[", "\\]"=>"]",
898          ));
899          $rx = "{^" . $rx . "$}" . $rxci;
900          
901          #-- cache
902          if (count($cmp) >= 50) {
903             $cmp = array();   // free
904          }
905          $cmp["$pattern+$flags"] = $rx;
906       }
907       
908       #-- compare
909       return(preg_match($rx, $str));
910    }
911 }
912
913
914 #-- file search and name matching (with shell patterns)
915 if (!function_exists("glob")) {
916
917    #-- introduced constants
918    define("GLOB_MARK", 1<<0);
919    define("GLOB_NOSORT", 1<<1);
920    define("GLOB_NOCHECK", 1<<2);
921    define("GLOB_NOESCAPE", 1<<3);
922    define("GLOB_BRACE", 1<<4);
923    define("GLOB_ONLYDIR", 1<<5);
924    define("GLOB_NOCASE", 1<<6);
925    define("GLOB_DOTS", 1<<7);
926    // unlikely to work under Win(?), without replacing the explode() with
927    // a preg_split() incorporating the native DIRECTORY_SEPARATOR as well
928
929    #-- implementation
930    function glob($pattern, $flags=0x0000) {
931       $ls = array();
932       $rxci = ($flags & GLOB_NOCASE) ? "i" : "";
933 #echo "\n=> glob($pattern)...\n";
934       
935       #-- transform glob pattern into regular expression
936       #   (similar to fnmatch() but still different enough to require a second func)
937       if ($pattern) {
938
939          #-- look at each directory/fn spec part separately
940          $parts2 = explode("/", $pattern);
941          $pat = preg_quote($pattern);
942          $pat = strtr($pat, array("\\*"=>".*?", "\\?"=>".?"));
943          if ($flags ^ GLOB_NOESCAPE) {
944             // uh, oh, ouuch - the above is unclean enough...
945          }
946          if ($flags ^ GLOB_BRACE) {
947             $pat = preg_replace("/\{(.+?)\}/e", 'strtr("[$1]", ",", "")', $pat);
948          }
949          $parts = explode("/", $pat);
950 #echo "parts == ".implode(" // ", $parts) . "\n";
951          $lasti = count($parts) - 1;
952          $dn = "";
953          foreach ($parts as $i=>$p) {
954
955             #-- basedir included (yet no pattern matching necessary)
956             if (!strpos($p, "*?") && (strpos($p, ".?")===false)) {
957                $dn .= $parts2[$i] . ($i!=$lasti ? "/" : "");
958 #echo "skip:$i, cause no pattern matching char found -> only a basedir spec\n";
959                continue;
960             }
961             
962             #-- start reading dir + match filenames against current pattern
963             if ($dh = opendir($dn ?$dn:'.')) {
964                $with_dot = ($p[1]==".") || ($flags & GLOB_DOTS);
965 #echo "part:$i:$p\n";
966 #echo "reading dir \"$dn\"\n";
967                while ($fn = readdir($dh)) {
968                   if (preg_match("\007^$p$\007$rxci", $fn)) {
969
970                      #-- skip over 'hidden' files
971                      if (($fn[0] == ".") && !$with_dot) {
972                         continue;
973                      }
974
975                      #-- add filename only if last glob/pattern part
976                      if ($i==$lasti) {
977                         if (is_dir("$dn$fn")) {
978                            if ($flags & GLOB_ONLYDIR) {
979                               continue;
980                            }
981                            if ($flags & GLOB_MARK) {
982                               $fn .= "/";
983                            }
984                         }
985 #echo "adding '$fn' for dn=$dn to list\n";
986                         $ls[] = "$dn$fn";
987                      }
988
989                      #-- initiate a subsearch, merge result list in
990                      elseif (is_dir("$dn$fn")) {
991                         // add reamaining search patterns to current basedir
992                         $remaind = implode("/", array_slice($parts2, $i+1));
993                         $ls = array_merge($ls, glob("$dn$fn/$remaind", $flags));
994                      }
995                   }
996                }
997                closedir($dh);
998
999                #-- prevent scanning a 2nd part/dir in same glob() instance:
1000                break;  
1001             }
1002
1003             #-- given dirname doesn't exist
1004             else {
1005                return($ls);
1006             }
1007
1008          }// foreach $parts
1009       }
1010
1011       #-- return result list
1012       if (!$ls && ($flags & GLOB_NOCHECK)) {
1013          $ls[] = $pattern;
1014       }
1015       if ($flags ^ GLOB_NOSORT) {
1016          sort($ls);
1017       }
1018 #print_r($ls);
1019 #echo "<=\n";
1020       return($ls);
1021    }
1022 } //@FIX: fully comment, remove debugging code (- as soon as it works ;)
1023
1024
1025 #-- redundant alias for isset()
1026 if (!function_exists("array_key_exists")) {
1027    function array_key_exists($key, $search) {
1028       return isset($search[$key]);
1029    }
1030 }
1031
1032
1033 #-- who could need that?
1034 if (!function_exists("array_intersect_assoc")) {
1035    function array_intersect_assoc( /*array, array, array...*/ ) {
1036
1037       #-- parameters, prepare
1038       $in = func_get_args();
1039       $cmax = count($in);
1040       $whatsleftover = array();
1041       
1042       #-- walk through each array pair
1043       #   (take first as checklist)
1044       foreach ($in[0] as $i => $v) {
1045          for ($c = 1; $c < $cmax; $c++) {
1046             #-- remove entry, as soon as it isn't present
1047             #   in one of the other arrays
1048             if (!isset($in[$c][$i]) || (@$in[$c][$i] !== $v)) {
1049                continue 2;
1050             }
1051          }
1052          #-- it was found in all other arrays
1053          $whatsleftover[$i] = $v;
1054       }
1055       return $whatsleftover;
1056    }
1057 }
1058
1059
1060 #-- the opposite of the above
1061 if (!function_exists("array_diff_assoc")) {
1062    function array_diff_assoc( /*array, array, array...*/ ) {
1063
1064       #-- params
1065       $in = func_get_args();
1066       $diff = array();
1067       
1068       #-- compare each array with primary/first
1069       foreach ($in[0] as $i=>$v) {
1070          for ($c=1; $c<count($in); $c++) {
1071             #-- skip as soon as it matches with entry in another array
1072             if (isset($in[$c][$i]) && ($in[$c][$i] == $v)) {
1073                continue 2;
1074             }
1075          }
1076          #-- else
1077          $diff[$i] = $v;
1078       }
1079       return $diff;
1080    }
1081 }
1082
1083
1084 #-- opposite of htmlentities
1085 if (!function_exists("html_entity_decode")) {
1086    function html_entity_decode($string, $quote_style=ENT_COMPAT, $charset="ISO-8859-1") {
1087       //@FIX: we fall short on anything other than Latin-1
1088       $y = array_flip(get_html_translation_table(HTML_ENTITIES, $quote_style));
1089       return strtr($string, $y);
1090    }
1091 }
1092
1093
1094 #-- extracts single words from a string
1095 if (!function_exists("str_word_count")) {
1096    function str_word_count($string, $result=0) {
1097    
1098       #-- let someone else do the work
1099       preg_match_all('/([\w](?:[-\'\w]?[\w]+)*)/', $string, $uu);
1100
1101       #-- return full word list
1102       if ($result == 1) {
1103          return($uu[1]);
1104       }
1105       
1106       #-- array() of $pos=>$word entries
1107       elseif ($result >= 2) {
1108          $r = array();
1109          $l = 0;
1110          foreach ($uu[1] as $word) {
1111             $l = strpos($string, $word, $l);
1112             $r[$l] = $word;
1113             $l += strlen($word);  // speed up next search
1114          }
1115          return($r);
1116       }
1117
1118       #-- only count
1119       else {
1120          return(count($uu[1]));
1121       }
1122    }
1123 }
1124
1125
1126 #-- creates a permutation of the given strings characters
1127 #   (let's hope the random number generator was alread initialized)
1128 if (!function_exists("str_shuffle")) {
1129    function str_shuffle($str) {
1130       $r = "";
1131
1132       #-- cut string down with every iteration
1133       while (strlen($str)) {
1134          $n = strlen($str) - 1;
1135          if ($n) {
1136             $n = rand(0, $n);   // glibcs` rand is ok since 2.1 at least
1137          }
1138          
1139          #-- cut out elected char, add to result string
1140          $r .= $str{$n};
1141          $str = substr($str, 0, $n) . substr($str, $n + 1);
1142       }
1143       return($r);
1144    }
1145 }
1146
1147
1148 #-- simple shorthands
1149 if (!function_exists("get_include_path")) {
1150    function get_include_path() {
1151       return(get_cfg_var("include_path"));
1152    }
1153    function set_include_path($new) {
1154       return ini_set("include_path", $new);
1155    }
1156    function restore_include_path() {
1157       ini_restore("include_path");
1158    }
1159 }
1160
1161
1162 #-- constants for 4.3
1163 if (!defined("PATH_SEPARATOR")) {
1164    define("PATH_SEPARATOR", ((DIRECTORY_SEPARATOR=='\\') ? ';' :':'));
1165    define("PHP_SHLIB_SUFFIX", ((DIRECTORY_SEPARATOR=='\\') ? 'dll' :'so'));
1166 }
1167 if (!defined("PHP_SAPI")) {
1168    define("PHP_SAPI", php_sapi_name());
1169 }
1170
1171
1172 #-- not identical to what PHP reports (it seems to `which` for itself)
1173 if (!defined("PHP_PREFIX") && isset($_ENV["_"])) { 
1174    define("PHP_PREFIX", substr($_ENV["_"], 0, strpos($_ENV["_"], "bin/")));
1175 }
1176
1177
1178
1179
1180
1181
1182 #------------------------------------------------------------------ 4.2 ---
1183 # almost complete!?
1184
1185
1186 #-- shy away from this one - it was broken in all real PHP4.2 versions, and
1187 #   this function emulation script won't change that
1188 if (!function_exists("str_rot13")) {
1189    function str_rot13($str) {
1190       static $from = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1191       static $to = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm";
1192       return strtr($str, $from, $to);
1193    }
1194 }
1195
1196
1197 #-- well, if you need it
1198 if (!function_exists("array_change_key_case")) {
1199    
1200    #-- introduced constants
1201    define("CASE_LOWER", 0);
1202    define("CASE_UPPER", 1);
1203    
1204    #-- implementation
1205    function array_change_key_case($array, $case=CASE_LOWER) {
1206    
1207       #-- loop through
1208       foreach ($array as $i=>$v) {
1209          #-- do anything for strings only
1210          if (is_string($i)) {
1211             unset($array[$i]);
1212             $i = ($case==CASE_LOWER) ? strtolower($i) : strtoupper($i);
1213             $array[$i] = $v;
1214          }
1215          // non-recursive      
1216       }
1217       return($array);
1218    }
1219 }
1220
1221
1222 #-- create fixed-length array made up of $value data
1223 if (!function_exists("array_fill")) {
1224    function array_fill($start_index, $num, $value) {
1225
1226       #-- params
1227       $r = array();
1228       $i = $start_index;
1229       $end = $num + $start_index;
1230       
1231       #-- append
1232       for (; $i < $end; $i++)
1233       {
1234          $r[$i] = $value;
1235       }
1236       return($r);
1237    }
1238 }
1239
1240
1241 #-- split an array into evenly sized parts
1242 if (!function_exists("array_chunk")) {
1243    function array_chunk($input, $size, $preserve_keys=false) {
1244    
1245       #-- array for chunked output
1246       $r = array();
1247       $n = -1;  // chunk index
1248       
1249       #-- enum input array blocks
1250       foreach ($input as $i=>$v) {
1251       
1252          #-- new chunk
1253          if (($n < 0) || (count($r[$n]) == $size)) {
1254             $n++;
1255             $r[$n] = array();
1256          }
1257          
1258          #-- add input value into current [$n] chunk
1259          if ($preserve_keys) {
1260             $r[$n][$i] = $v;
1261          }
1262          else {
1263             $r[$n][] = $v;
1264          }
1265       }
1266       return($r);
1267    }
1268 }
1269
1270
1271 #-- convenience wrapper
1272 if (!function_exists("md5_file")) {
1273    function md5_file($filename, $raw_output=false) {
1274
1275       #-- read file, apply hash function
1276       $data = file_get_contents($filename, "rb");
1277       $r = md5($data);
1278       $data = NULL;
1279          
1280       #-- transform? and return
1281       if ($raw_output) {
1282          $r = pack("H*", $r);
1283       }
1284       return $r;
1285    }
1286 }
1287
1288
1289 #-- object type checking
1290 if (!function_exists("is_a")) {
1291    function is_a($obj, $classname) {
1292    
1293       #-- lowercase everything for comparison
1294       $classnaqme = strtolower($classname);
1295       $obj_class =  strtolower(get_class($obj));
1296       
1297       #-- two possible checks
1298       return ($obj_class == $classname) or is_subclass_of($obj, $classname);
1299    }
1300 }
1301
1302
1303 #-- floating point modulo
1304 if (!function_exists("fmod")) {
1305    function fmod($x, $y) {
1306       $r = $x / $y;
1307       $r -= (int)$r;
1308       $r *= $y;
1309       return($r);
1310    }
1311 }
1312
1313
1314 #-- makes float variable from string
1315 if (!function_exists("floatval")) {
1316    function floatval($str) {
1317       $str = ltrim($str);
1318       return (float)$str;
1319    }
1320 }
1321
1322
1323 #-- floats
1324 if (!function_exists("is_infinite")) {
1325
1326    #-- constants as-is
1327    define("NAN", "NAN");
1328    define("INF", "INF");   // there is also "-INF"
1329    
1330    #-- simple checks
1331    function is_infinite($f) {
1332       $s = (string)$f;
1333       return(  ($s=="INF") || ($s=="-INF")  );
1334    }
1335    function is_nan($f) {
1336       $s = (string)$f;
1337       return(  $s=="NAN"  );
1338    }
1339    function is_finite($f) {
1340       $s = (string)$f;
1341       return(  !strpos($s, "N")  );
1342    }
1343 }
1344
1345
1346 #-- throws value-instantiation PHP-code for given variable
1347 #   (a bit different from the standard, was intentional for its orig use)
1348 if (!function_exists("var_export")) {
1349    function var_export($var, $return=false, $indent="", $output="") {
1350
1351       #-- output as in-class variable definitions
1352       if (is_object($var)) {
1353          $output = "class " . get_class($var) . " {\n";
1354          foreach (((array)$var) as $id=>$var) {
1355             $output .= "  var \$$id = " . var_export($var, true) . ";\n";
1356          }
1357          $output .= "}";
1358       }
1359       
1360       #-- array constructor
1361       elseif (is_array($var)) {
1362          foreach ($var as $id=>$next) {
1363             if ($output) $output .= ",\n";
1364                     else $output = "array(\n";
1365             $output .= $indent . '  '
1366                     . (is_numeric($id) ? $id : '"'.addslashes($id).'"')
1367                     . ' => ' . var_export($next, true, "$indent  ");
1368          }
1369          if (empty($output)) $output = "array(";
1370          $output .= "\n{$indent})";
1371        #if ($indent == "") $output .= ";";
1372       }
1373       
1374       #-- literals
1375       elseif (is_numeric($var)) {
1376          $output = "$var";
1377       }
1378       elseif (is_bool($var)) {
1379          $output = $var ? "true" : "false";
1380       }
1381       else {
1382          $output = "'" . preg_replace("/([\\\\\'])/", '\\\\$1', $var) . "'";
1383       }
1384
1385       #-- done
1386       if ($return) {
1387          return($output);
1388       }
1389       else {
1390          print($output);
1391       }
1392    }
1393 }
1394
1395
1396 #-- strcmp() variant that respects locale setting,
1397 #   existed since PHP 4.0.5, but under Win32 first since 4.3.2
1398 if (!function_exists("strcoll")) {
1399    function strcoll($str1, $str2) {
1400       return strcmp($str1, $str2);
1401    }
1402 }
1403
1404
1405
1406
1407
1408 #------------------------------------------------------------------ 4.1 ---
1409 # nl_langinfo - unimpl?
1410 # getmygid
1411 # version_compare
1412 #
1413 # See also "ext/math41.php" for some more (rarely used mathematical funcs).
1414
1415
1416
1417
1418 #-- aliases (an earlier fallen attempt to unify PHP function names)
1419 if (!function_exists("diskfreespace")) {
1420    function diskfreespace() {
1421       return disk_free_sapce();
1422    }
1423    function disktotalspace() {
1424       return disk_total_sapce();
1425    }
1426 }
1427
1428
1429 #-- variable count of arguments (in array list) printf variant
1430 if (!function_exists("vprintf")) {
1431    function vprintf($format, $args=NULL) {
1432       call_user_func_array("fprintf", get_func_args());
1433    }
1434 }
1435
1436
1437 #-- same as above, but doesn't output directly and returns formatted string
1438 if (!function_exists("vsprintf")) {
1439    function vsprintf($format, $args=NULL) {
1440       $args = array_merge(array($format), array_values((array)$args));
1441       return call_user_func_array("sprintf", $args);
1442    }
1443 }
1444
1445
1446 #-- can be used to simulate a register_globals=on environment
1447 if (!function_exists("import_request_variables")) {
1448    function import_request_variables($types="GPC", $pfix="") {
1449       
1450       #-- associate abbreviations to global var names
1451       $alias = array(
1452          "G" => "_GET",
1453          "P" => "_POST",
1454          "C" => "_COOKIE",
1455          "S" => "_SERVER",   // non-standard
1456          "E" => "_ENV",      // non-standard
1457       );
1458       #-- alias long names (PHP < 4.0.6)
1459       if (!isset($_REQUEST)) {
1460          $_GET = & $HTTP_GET_VARS;
1461          $_POST = & $HTTP_POST_VARS;
1462          $_COOKIE = & $HTTP_COOKIE_VARS;
1463       }
1464       
1465       #-- copy
1466       for ($i=0; $i<strlen($types); $i++) {
1467          if ($FROM = $alias[strtoupper($c)]) {
1468             foreach ($$FROM as $key=>$val) {
1469                if (!isset($GLOBALS[$pfix.$key])) {
1470                   $GLOBALS[$pfix . $key] = $val;
1471                }
1472             }
1473          }
1474       }
1475       // done
1476    }
1477 }
1478
1479
1480 // a few mathematical functions follow
1481 // (wether we should really emulate them is a different question)
1482
1483 #-- me has no idea what this function means
1484 if (!function_exists("hypot")) {
1485    function hypot($num1, $num2) {
1486       return sqrt($num1*$num1 + $num2*$num2);  // as per PHP manual ;)
1487    }
1488 }
1489
1490 #-- more accurate logarithm func, but we cannot simulate it
1491 #   (too much work, too slow in PHP)
1492 if (!function_exists("log1p")) {
1493    function log1p($x) {
1494       return(  log(1+$x)  );
1495    }
1496    #-- same story for:
1497    function expm1($x) {
1498       return(  exp($x)-1  );
1499    }
1500 }
1501
1502 #-- as per PHP manual
1503 if (!function_exists("sinh")) {
1504    function sinh($f) {
1505       return(  (exp($f) - exp(-$f)) / 2  );
1506    }
1507    function cosh($f) {
1508       return(  (exp($f) + exp(-$f)) / 2  );
1509    }
1510    function tanh($f) {
1511       return(  sinh($f) / cosh($f)  );   // ok, that one makes sense again :)
1512    }
1513 }
1514
1515 #-- these look a bit more complicated
1516 if (!function_exists("asinh")) {
1517    function asinh($x) {
1518       return(  log($x + sqrt($x*$x+1))  );
1519    }
1520    function acosh($x) {
1521       return(  log($x + sqrt($x*$x-1))  );
1522    }
1523    function atanh($x) {
1524       return(  log1p( 2*$x / (1-$x) ) / 2  );
1525    }
1526 }
1527
1528
1529 #-- HMAC from RFC2104, but see also PHP_Compat and Crypt_HMAC
1530 if (!function_exists("mhash")) {
1531
1532    #-- constants
1533    define("MHASH_CRC32", 0);
1534    define("MHASH_MD5", 1);       // RFC1321
1535    define("MHASH_SHA1", 2);      // RFC3174
1536    define("MHASH_TIGER", 7);
1537    define("MHASH_MD4", 16);      // RFC1320
1538    define("MHASH_SHA256", 17);
1539    define("MHASH_ADLER32", 18);
1540    
1541    #-- implementation
1542    function mhash($hashtype, $text, $key) {
1543    
1544       #-- hash function
1545       static $hash_funcs = array(
1546           MHASH_CRC32 => "crc32",   // needs dechex()ing here
1547           MHASH_MD5 => "md5",
1548           MHASH_SHA1 => "sha1",
1549       );
1550       if (!($func = $hash_funcs[$hashtype]) || !function_exists($func)) {
1551          return trigger_error("mhash: cannot use hash algorithm #$hashtype/$func", E_USER_ERROR);
1552       }
1553       if (!$key) {
1554          trigger_error("mhash: called without key", E_USER_WARNING);
1555       }
1556       
1557       #-- params
1558       $bsize = 64;   // fixed size
1559
1560       #-- pad key
1561       if (strlen($key) > $bsize) {  // hash key, when it's too long
1562          $key = $func($key); 
1563          $key = pack("H*", $key);   // binarify
1564       }
1565       $key = str_pad($key, $bsize, "\0");  // fill up with NULs (1)
1566       
1567       #-- prepare inner and outer padding stream
1568       $ipad = str_pad("", $bsize, "6");   // %36
1569       $opad = str_pad("", $bsize, "\\");  // %5C
1570       
1571       #-- call hash func    // php can XOR strings for us
1572       $dgst = pack("H*",  $func(  ($key ^ $ipad)  .  $text  ));  // (2,3,4)
1573       $dgst = pack("H*",  $func(  ($key ^ $opad)  .  $dgst  ));  // (5,6,7)
1574       return($dgst);
1575    }
1576 }
1577
1578
1579
1580 #-- other stuff
1581 /*
1582   removed funcs??
1583       [18] => leak
1584 */
1585
1586
1587
1588 #-- pre-4.1 -- end
1589 // no need to implement anything below that, because such old versions
1590 // will be incompatbile anyhow (- none of the newer superglobals known),
1591 // but see also "ext/old"
1592
1593
1594 ?>