source: minimanager/js/ajax/Php.php@ 5

Last change on this file since 5 was 5, checked in by george, 18 years ago

import

File size: 12.3 KB
Line 
1<?php
2/**
3 * JsHttpRequest: PHP backend for JavaScript DHTML loader.
4 * (C) 2005 Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 * See http://www.gnu.org/copyleft/lesser.html
11 *
12 * Do not remove this comment if you want to use the script!
13 *
14 * This backend library also supports POST requests additionally to GET.
15 *
16 * @author Dmitry Koterov
17 * @version 3.35
18 */
19
20class JsHttpRequest
21{
22 var $SCRIPT_ENCODING = "windows-1251";
23 var $SCRIPT_DECODE_MODE = '';
24 var $UNIQ_HASH;
25 var $SCRIPT_ID;
26 var $LOADER = null;
27 var $QUOTING = null;
28
29 /**
30 * Constructor.
31 *
32 * Create new Subsys_JsHttpRequest_Php backend object and attach it
33 * to script output buffer. As a result - script will always return
34 * correct JavaScript code, even in case of fatal errors.
35 */
36 function JsHttpRequest($enc)
37 {
38 // QUERY_STRING is in form: PHPSESSID=<sid>&a=aaa&b=bbb&<id>
39 // where <id> is request ID, <sid> - session ID (if present),
40 // PHPSESSID - session parameter name (by default = "PHPSESSID").
41
42 // Parse QUERY_STRING wrapper format.
43 $this->LOADER = "SCRIPT";
44 if (preg_match('/(\d+)((?:-\w+)?)$/s', $_SERVER['QUERY_STRING'], $m)) {
45 $this->SCRIPT_ID = $m[1];
46 // XMLHttpRequest is used if URI ends with "&".
47 if ($m[2] == '-xml') $this->LOADER = "XMLHttpRequest";
48 } else {
49 $this->SCRIPT_ID = 0;
50 }
51
52 // Start OB handling early.
53 $this->UNIQ_HASH = md5(microtime().getmypid());
54 ini_set('error_prepend_string', ini_get('error_prepend_string').$this->UNIQ_HASH);
55 ini_set('error_append_string', ini_get('error_append_string') .$this->UNIQ_HASH);
56 ob_start(array(&$this, "_obHandler"));
57
58 // Set up encoding.
59 $this->setEncoding($enc);
60
61 // Check if headers are already sent (see Content-Type library usage).
62 // If true - generate debug message and exit.
63 $file = $line = null;
64 if (headers_sent($file, $line)) {
65 trigger_error(
66 "HTTP headers are already sent" . ($line !== null? " in $file on line $line" : "") . ". "
67 . "Possibly you have extra spaces (or newlines) before first line of the script or any library. "
68 . "Please note that Subsys_JsHttpRequest uses its own Content-Type header and fails if "
69 . "this header cannot be set. See header() function documentation for details",
70 E_USER_ERROR
71 );
72 exit();
73 }
74 }
75
76
77 /**
78 * string getJsCode()
79 *
80 * Return JavaScript part of library.
81 */
82 function getJsCode()
83 {
84 return file_get_contents(dirname(__FILE__).'/Js.js');
85 }
86
87
88 /**
89 * void setEncoding(string $encoding)
90 *
91 * Set active script encoding & correct QUERY_STRING according to it.
92 * Examples:
93 * "windows-1251" - set plain encoding (non-windows characters,
94 * e.g. hieroglyphs, are totally ignored)
95 * "windows-1251 entities" - set windows encoding, BUT additionally replace:
96 * "&" -> "&amp;"
97 * hieroglyph -> &#XXXX; entity
98 */
99 function setEncoding($enc)
100 {
101 // Parse encoding.
102 preg_match('/^(\S*)(?:\s+(\S*))$/', $enc, $p);
103 $this->SCRIPT_ENCODING = strtolower(@$p[1]? $p[1] : $enc);
104 $this->SCRIPT_DECODE_MODE = @$p[2]? $p[2] : '';
105 // Manually parse QUERY_STRING because of damned Unicode's %uXXXX.
106 $this->_correctQueryString();
107 }
108
109
110 /**
111 * string quoteInput(string $input)
112 *
113 * Quote string according to input decoding mode.
114 * If entities is used (see setEncoding()), no '&' character is quoted,
115 * only '"', '>' and '<' (we presume than '&' is already quoted by
116 * input reader function).
117 *
118 * Use this function INSTEAD of htmlspecialchars() for $_GET data
119 * in your scripts.
120 */
121 function quoteInput($s)
122 {
123 if ($this->SCRIPT_DECODE_MODE == 'entities')
124 return str_replace(array('"', '<', '>'), array('&quot;', '&lt;', '&gt;'), $s);
125 else
126 return htmlspecialchars($s);
127 }
128
129
130 /**
131 * Convert PHP scalar, array or hash to JS scalar/array/hash.
132 */
133 function php2js($a)
134 {
135 if (is_null($a)) return 'null';
136 if ($a === false) return 'false';
137 if ($a === true) return 'true';
138 if (is_scalar($a)) {
139 $a = addslashes($a);
140 $a = str_replace("\n", '\n', $a);
141 $a = str_replace("\r", '\r', $a);
142 $a = preg_replace('{(</)(script)}i', "$1'+'$2", $a); // for FORM loader
143 return "'$a'";
144 }
145 $isList = true;
146 for ($i=0, reset($a); $i<count($a); $i++, next($a))
147 if (key($a) !== $i) { $isList = false; break; }
148 $result = array();
149 if ($isList) {
150 foreach ($a as $v) $result[] = JsHttpRequest::php2js($v);
151 return '[ ' . join(',', $result) . ' ]';
152 } else {
153 foreach ($a as $k=>$v) $result[] = JsHttpRequest::php2js($k) . ': ' . JsHttpRequest::php2js($v);
154 return '{ ' . join(',', $result) . ' }';
155 }
156 }
157
158
159 /**
160 * Internal methods.
161 */
162
163 /**
164 * Parse & decode QUERY_STRING.
165 */
166 function _correctQueryString()
167 {
168 // ATTENTION!!!
169 // HTTP_RAW_POST_DATA is only accessible when Content-Type of POST request
170 // is NOT default "application/x-www-form-urlencoded"!!!
171 // Library frontend sets "application/octet-stream" for that purpose,
172 // see JavaScript code.
173 foreach (array('_GET'=>$_SERVER['QUERY_STRING'], '_POST'=>@$GLOBALS['HTTP_RAW_POST_DATA']) as $dst=>$src) {
174 if (isset($GLOBALS[$dst])) {
175 // First correct all 2-byte entities.
176 $s = preg_replace('/%(?!5B)(?!5D)([0-9a-f]{2})/si', '%u00\\1', $src);
177 // Now we can use standard parse_str() with no worry!
178 $data = null;
179 parse_str($s, $data);
180 $GLOBALS[$dst] = $this->_ucs2EntitiesDecode($data);
181 }
182 }
183 $_REQUEST =
184 (isset($_COOKIE)? $_COOKIE : array()) +
185 (isset($_POST)? $_POST : array()) +
186 (isset($_GET)? $_GET : array());
187 if (ini_get('register_globals')) {
188 // TODO?
189 }
190 }
191
192
193 /**
194 * Called in case of error too!
195 */
196 function _obHandler($text)
197 {
198 // Check for error.
199 if (preg_match('{'.$this->UNIQ_HASH.'(.*?)'.$this->UNIQ_HASH.'}sx', $text)) {
200 $text = str_replace($this->UNIQ_HASH, '', $text);
201 $this->WAS_ERROR = 1;
202 }
203 // Content-type header.
204 // In XMLHttpRRequest mode we must return text/plain - damned stupid Opera 8.0. :(
205 header("Content-type: " . ($this->LOADER=="SCRIPT"? "text/javascript" : "text/plain") . "; charset=" . $this->SCRIPT_ENCODING);
206 // Make resulting hash.
207 if (!isset($this->RESULT)) $this->RESULT = @$GLOBALS['_RESULT'];
208 $result = $this->php2js($this->RESULT);
209 $text =
210 "// BEGIN JsHttpRequest\n" .
211 "JsHttpRequest.dataReady(\n" .
212 " " . $this->php2js($this->SCRIPT_ID) . ", // this ID is passed from JavaScript frontend\n" .
213 " " . $this->php2js(trim($text)) . ",\n" .
214 " " . $result . "\n" .
215 ")\n" .
216 "// END JsHttpRequest\n" .
217 "";
218// $f = fopen("debug", "w"); fwrite($f, $text); fclose($f);
219 return $text;
220 }
221
222
223 /**
224 * Decode all %uXXXX entities in string or array (recurrent).
225 * String must not contain %XX entities - they are ignored!
226 */
227 function _ucs2EntitiesDecode($data)
228 {
229 if (is_array($data)) {
230 $d = array();
231 foreach ($data as $k=>$v) {
232 $d[$this->_ucs2EntitiesDecode($k)] = $this->_ucs2EntitiesDecode($v);
233 }
234 return $d;
235 } else {
236 if (strpos($data, '%u') !== false) { // improve speed
237 $data = preg_replace_callback('/%u([0-9A-F]{1,4})/si', array(&$this, '_ucs2EntitiesDecodeCallback'), $data);
238 }
239 return $data;
240 }
241 }
242
243
244 /**
245 * Decode one %uXXXX entity (RE callback).
246 */
247 function _ucs2EntitiesDecodeCallback($p)
248 {
249 $hex = $p[1];
250 $dec = hexdec($hex);
251 if ($dec === "38" && $this->SCRIPT_DECODE_MODE == 'entities') {
252 // Process "&" separately in "entities" decode mode.
253 $c = "&amp;";
254 } else {
255 if (is_callable('iconv')) {
256 $c = @iconv('UCS-2BE', $this->SCRIPT_ENCODING, pack('n', $dec));
257 } else {
258 $c = $this->_decUcs2Decode($dec, $this->SCRIPT_ENCODING);
259 }
260 if (!strlen($c)) {
261 if ($this->SCRIPT_DECODE_MODE == 'entities') {
262 $c = '&#'.$dec.';';
263 } else {
264 $c = '?';
265 }
266 }
267 }
268 return $c;
269 }
270
271
272 /**
273 * If there is no ICONV, try to decode 1-byte characters manually
274 * (for most popular charsets only).
275 */
276
277 /**
278 * Convert from UCS-2BE decimal to $toEnc.
279 */
280 function _decUcs2Decode($code, $toEnc)
281 {
282 if ($code < 128) return chr($code);
283 if (isset($this->_encTables[$toEnc])) {
284 $p = array_search($code, $this->_encTables[$toEnc]);
285 if ($p !== false) return chr(128 + $p);
286 }
287 return "";
288 }
289
290
291 /**
292 * UCS-2BE -> 1-byte encodings (from #128).
293 */
294 var $_encTables = array(
295 'windows-1251' => array(
296 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
297 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
298 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
299 0x0098, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
300 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
301 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
302 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
303 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
304 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
305 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
306 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
307 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
308 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
309 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
310 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
311 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
312 ),
313 'koi8-r' => array(
314 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
315 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
316 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
317 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7,
318 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556,
319 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255d, 0x255E,
320 0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565,
321 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x00A9,
322 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
323 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043d, 0x043E,
324 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
325 0x044C, 0x044B, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044A,
326 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
327 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041d, 0x041E,
328 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
329 0x042C, 0x042B, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042A
330 ),
331 );
332}
333?>
Note: See TracBrowser for help on using the repository browser.