Version Notes
1.3.1
Download this release
Release Info
Developer | Magento Core Team |
Extension | Lib_PEAR |
Version | 1.3.1 |
Comparing to | |
See all releases |
Version 1.3.1
- lib/PEAR/HTTP/HTTP.php +548 -0
- lib/PEAR/HTTP/Request.php +1521 -0
- lib/PEAR/HTTP/Request/Listener.php +106 -0
- lib/PEAR/Mail/mime.php +1095 -0
- lib/PEAR/Mail/mimeDecode.php +849 -0
- lib/PEAR/Mail/mimePart.php +439 -0
- lib/PEAR/Mail/xmail.dtd +19 -0
- lib/PEAR/Mail/xmail.xsl +70 -0
- lib/PEAR/Net/Socket.php +592 -0
- lib/PEAR/Net/URL.php +485 -0
- lib/PEAR/PEAR.php +1118 -0
- lib/PEAR/PEAR/PEAR.php +1118 -0
- lib/PEAR/SOAP/Base.php +1142 -0
- lib/PEAR/SOAP/Client.php +837 -0
- lib/PEAR/SOAP/Fault.php +129 -0
- lib/PEAR/SOAP/Parser.php +499 -0
- lib/PEAR/SOAP/Transport.php +147 -0
- lib/PEAR/SOAP/Transport/HTTP.php +624 -0
- lib/PEAR/SOAP/Value.php +288 -0
- lib/PEAR/SOAP/WSDL.php +2294 -0
- lib/PEAR/XML/Parser.php +768 -0
- lib/PEAR/XML/Parser/Simple.php +326 -0
- lib/PEAR/XML/Serializer.php +1222 -0
- lib/PEAR/XML/Unserializer.php +983 -0
- package.xml +18 -0
lib/PEAR/HTTP/HTTP.php
ADDED
@@ -0,0 +1,548 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
4 |
+
|
5 |
+
/**
|
6 |
+
* HTTP
|
7 |
+
*
|
8 |
+
* PHP versions 4 and 5
|
9 |
+
*
|
10 |
+
* @category HTTP
|
11 |
+
* @package HTTP
|
12 |
+
* @author Stig Bakken <ssb@fast.no>
|
13 |
+
* @author Sterling Hughes <sterling@php.net>
|
14 |
+
* @author Tomas V.V.Cox <cox@idecnet.com>
|
15 |
+
* @author Richard Heyes <richard@php.net>
|
16 |
+
* @author Philippe Jausions <jausions@php.net>
|
17 |
+
* @author Michael Wallner <mike@php.net>
|
18 |
+
* @copyright 2002-2008 The Authors
|
19 |
+
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
20 |
+
* @version CVS: $Id: HTTP.php,v 1.56 2008/08/31 20:15:43 jausions Exp $
|
21 |
+
* @link http://pear.php.net/package/HTTP
|
22 |
+
*/
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Miscellaneous HTTP Utilities
|
26 |
+
*
|
27 |
+
* PEAR::HTTP provides static shorthand methods for generating HTTP dates,
|
28 |
+
* issueing HTTP HEAD requests, building absolute URIs, firing redirects and
|
29 |
+
* negotiating user preferred language.
|
30 |
+
*
|
31 |
+
* @category HTTP
|
32 |
+
* @package HTTP
|
33 |
+
* @author Stig Bakken <ssb@fast.no>
|
34 |
+
* @author Sterling Hughes <sterling@php.net>
|
35 |
+
* @author Tomas V.V.Cox <cox@idecnet.com>
|
36 |
+
* @author Richard Heyes <richard@php.net>
|
37 |
+
* @author Philippe Jausions <jausions@php.net>
|
38 |
+
* @author Michael Wallner <mike@php.net>
|
39 |
+
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
40 |
+
* @abstract
|
41 |
+
* @version Release: $Revision: 1.56 $
|
42 |
+
* @link http://pear.php.net/package/HTTP
|
43 |
+
*/
|
44 |
+
class HTTP
|
45 |
+
{
|
46 |
+
/**
|
47 |
+
* Formats a RFC compliant GMT date HTTP header. This function honors the
|
48 |
+
* "y2k_compliance" php.ini directive and formats the GMT date corresponding
|
49 |
+
* to either RFC850 or RFC822.
|
50 |
+
*
|
51 |
+
* @param mixed $time unix timestamp or date (default = current time)
|
52 |
+
*
|
53 |
+
* @return mixed GMT date string, or false for an invalid $time parameter
|
54 |
+
* @access public
|
55 |
+
* @static
|
56 |
+
*/
|
57 |
+
function Date($time = null)
|
58 |
+
{
|
59 |
+
if (!isset($time)) {
|
60 |
+
$time = time();
|
61 |
+
} elseif (!is_numeric($time) && (-1 === $time = strtotime($time))) {
|
62 |
+
return false;
|
63 |
+
}
|
64 |
+
|
65 |
+
// RFC822 or RFC850
|
66 |
+
$format = ini_get('y2k_compliance') ? 'D, d M Y' : 'l, d-M-y';
|
67 |
+
|
68 |
+
return gmdate($format .' H:i:s \G\M\T', $time);
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Negotiates language with the user's browser through the Accept-Language
|
73 |
+
* HTTP header or the user's host address. Language codes are generally in
|
74 |
+
* the form "ll" for a language spoken in only one country, or "ll-CC" for a
|
75 |
+
* language spoken in a particular country. For example, U.S. English is
|
76 |
+
* "en-US", while British English is "en-UK". Portugese as spoken in
|
77 |
+
* Portugal is "pt-PT", while Brazilian Portugese is "pt-BR".
|
78 |
+
*
|
79 |
+
* Quality factors in the Accept-Language: header are supported, e.g.:
|
80 |
+
* Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
|
81 |
+
*
|
82 |
+
* <code>
|
83 |
+
* require_once 'HTTP.php';
|
84 |
+
* $langs = array(
|
85 |
+
* 'en' => 'locales/en',
|
86 |
+
* 'en-US' => 'locales/en',
|
87 |
+
* 'en-UK' => 'locales/en',
|
88 |
+
* 'de' => 'locales/de',
|
89 |
+
* 'de-DE' => 'locales/de',
|
90 |
+
* 'de-AT' => 'locales/de',
|
91 |
+
* );
|
92 |
+
* $neg = HTTP::negotiateLanguage($langs);
|
93 |
+
* $dir = $langs[$neg];
|
94 |
+
* </code>
|
95 |
+
*
|
96 |
+
* @param array $supported An associative array of supported languages,
|
97 |
+
* whose values must evaluate to true.
|
98 |
+
* @param string $default The default language to use if none is found.
|
99 |
+
*
|
100 |
+
* @return string The negotiated language result or the supplied default.
|
101 |
+
* @static
|
102 |
+
* @access public
|
103 |
+
*/
|
104 |
+
function negotiateLanguage($supported, $default = 'en-US')
|
105 |
+
{
|
106 |
+
$supp = array();
|
107 |
+
foreach ($supported as $lang => $isSupported) {
|
108 |
+
if ($isSupported) {
|
109 |
+
$supp[strtolower($lang)] = $lang;
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
if (!count($supp)) {
|
114 |
+
return $default;
|
115 |
+
}
|
116 |
+
|
117 |
+
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
118 |
+
$match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_LANGUAGE'],
|
119 |
+
$supp);
|
120 |
+
if (!is_null($match)) {
|
121 |
+
return $match;
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
if (isset($_SERVER['REMOTE_HOST'])) {
|
126 |
+
$lang = strtolower(end($h = explode('.', $_SERVER['REMOTE_HOST'])));
|
127 |
+
if (isset($supp[$lang])) {
|
128 |
+
return $supp[$lang];
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
return $default;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Negotiates charset with the user's browser through the Accept-Charset
|
137 |
+
* HTTP header.
|
138 |
+
*
|
139 |
+
* Quality factors in the Accept-Charset: header are supported, e.g.:
|
140 |
+
* Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
|
141 |
+
*
|
142 |
+
* <code>
|
143 |
+
* require_once 'HTTP.php';
|
144 |
+
* $charsets = array(
|
145 |
+
* 'UTF-8',
|
146 |
+
* 'ISO-8859-1',
|
147 |
+
* );
|
148 |
+
* $charset = HTTP::negotiateCharset($charsets);
|
149 |
+
* </code>
|
150 |
+
*
|
151 |
+
* @param array $supported An array of supported charsets
|
152 |
+
* @param string $default The default charset to use if none is found.
|
153 |
+
*
|
154 |
+
* @return string The negotiated language result or the supplied default.
|
155 |
+
* @static
|
156 |
+
* @author Philippe Jausions <jausions@php.net>
|
157 |
+
* @access public
|
158 |
+
* @since 1.4.1
|
159 |
+
*/
|
160 |
+
function negotiateCharset($supported, $default = 'ISO-8859-1')
|
161 |
+
{
|
162 |
+
$supp = array();
|
163 |
+
foreach ($supported as $charset) {
|
164 |
+
$supp[strtolower($charset)] = $charset;
|
165 |
+
}
|
166 |
+
|
167 |
+
if (!count($supp)) {
|
168 |
+
return $default;
|
169 |
+
}
|
170 |
+
|
171 |
+
if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
|
172 |
+
$match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_CHARSET'],
|
173 |
+
$supp);
|
174 |
+
if (!is_null($match)) {
|
175 |
+
return $match;
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
return $default;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Negotiates content type with the user's browser through the Accept
|
184 |
+
* HTTP header.
|
185 |
+
*
|
186 |
+
* Quality factors in the Accept: header are supported, e.g.:
|
187 |
+
* Accept: application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8
|
188 |
+
*
|
189 |
+
* <code>
|
190 |
+
* require_once 'HTTP.php';
|
191 |
+
* $contentType = array(
|
192 |
+
* 'application/xhtml+xml',
|
193 |
+
* 'application/xml',
|
194 |
+
* 'text/html',
|
195 |
+
* 'text/plain',
|
196 |
+
* );
|
197 |
+
* $mime = HTTP::negotiateContentType($contentType);
|
198 |
+
* </code>
|
199 |
+
*
|
200 |
+
* @param array $supported An associative array of supported MIME types.
|
201 |
+
* @param string $default The default type to use if none match.
|
202 |
+
*
|
203 |
+
* @return string The negotiated MIME type result or the supplied default.
|
204 |
+
* @static
|
205 |
+
* @author Philippe Jausions <jausions@php.net>
|
206 |
+
* @access public
|
207 |
+
* @since 1.4.1
|
208 |
+
*/
|
209 |
+
function negotiateMimeType($supported, $default)
|
210 |
+
{
|
211 |
+
$supp = array();
|
212 |
+
foreach ($supported as $type) {
|
213 |
+
$supp[strtolower($type)] = $type;
|
214 |
+
}
|
215 |
+
|
216 |
+
if (!count($supp)) {
|
217 |
+
return $default;
|
218 |
+
}
|
219 |
+
|
220 |
+
if (isset($_SERVER['HTTP_ACCEPT'])) {
|
221 |
+
$accepts = HTTP::_sortAccept($_SERVER['HTTP_ACCEPT']);
|
222 |
+
|
223 |
+
foreach ($accepts as $type => $q) {
|
224 |
+
if (substr($type, -2) != '/*') {
|
225 |
+
if (isset($supp[$type])) {
|
226 |
+
return $supp[$type];
|
227 |
+
}
|
228 |
+
continue;
|
229 |
+
}
|
230 |
+
if ($type == '*/*') {
|
231 |
+
return array_shift($supp);
|
232 |
+
}
|
233 |
+
list($general, $specific) = explode('/', $type);
|
234 |
+
$general .= '/';
|
235 |
+
$len = strlen($general);
|
236 |
+
foreach ($supp as $mime => $t) {
|
237 |
+
if (strncasecmp($general, $mime, $len) == 0) {
|
238 |
+
return $t;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
}
|
242 |
+
}
|
243 |
+
|
244 |
+
return $default;
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Parses a weighed "Accept" HTTP header and matches it against a list
|
249 |
+
* of supported options
|
250 |
+
*
|
251 |
+
* @param string $header The HTTP "Accept" header to parse
|
252 |
+
* @param array $supported A list of supported values
|
253 |
+
*
|
254 |
+
* @return string|NULL a matched option, or NULL if no match
|
255 |
+
* @access private
|
256 |
+
* @static
|
257 |
+
*/
|
258 |
+
function _matchAccept($header, $supported)
|
259 |
+
{
|
260 |
+
$matches = HTTP::_sortAccept($header);
|
261 |
+
foreach ($matches as $key => $q) {
|
262 |
+
if (isset($supported[$key])) {
|
263 |
+
return $supported[$key];
|
264 |
+
}
|
265 |
+
}
|
266 |
+
// If any (i.e. "*") is acceptable, return the first supported format
|
267 |
+
if (isset($matches['*'])) {
|
268 |
+
return array_shift($supported);
|
269 |
+
}
|
270 |
+
return null;
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* Parses and sorts a weighed "Accept" HTTP header
|
275 |
+
*
|
276 |
+
* @param string $header The HTTP "Accept" header to parse
|
277 |
+
*
|
278 |
+
* @return array a sorted list of "accept" options
|
279 |
+
* @access private
|
280 |
+
* @static
|
281 |
+
*/
|
282 |
+
function _sortAccept($header)
|
283 |
+
{
|
284 |
+
$matches = array();
|
285 |
+
foreach (explode(',', $header) as $option) {
|
286 |
+
$option = array_map('trim', explode(';', $option));
|
287 |
+
|
288 |
+
$l = strtolower($option[0]);
|
289 |
+
if (isset($option[1])) {
|
290 |
+
$q = (float) str_replace('q=', '', $option[1]);
|
291 |
+
} else {
|
292 |
+
$q = null;
|
293 |
+
// Assign default low weight for generic values
|
294 |
+
if ($l == '*/*') {
|
295 |
+
$q = 0.01;
|
296 |
+
} elseif (substr($l, -1) == '*') {
|
297 |
+
$q = 0.02;
|
298 |
+
}
|
299 |
+
}
|
300 |
+
// Unweighted values, get high weight by their position in the
|
301 |
+
// list
|
302 |
+
$matches[$l] = isset($q) ? $q : 1000 - count($matches);
|
303 |
+
}
|
304 |
+
arsort($matches, SORT_NUMERIC);
|
305 |
+
return $matches;
|
306 |
+
}
|
307 |
+
|
308 |
+
/**
|
309 |
+
* Sends a "HEAD" HTTP command to a server and returns the headers
|
310 |
+
* as an associative array.
|
311 |
+
*
|
312 |
+
* Example output could be:
|
313 |
+
* <code>
|
314 |
+
* Array
|
315 |
+
* (
|
316 |
+
* [response_code] => 200 // The HTTP response code
|
317 |
+
* [response] => HTTP/1.1 200 OK // The full HTTP response string
|
318 |
+
* [Date] => Fri, 11 Jan 2002 01:41:44 GMT
|
319 |
+
* [Server] => Apache/1.3.20 (Unix) PHP/4.1.1
|
320 |
+
* [X-Powered-By] => PHP/4.1.1
|
321 |
+
* [Connection] => close
|
322 |
+
* [Content-Type] => text/html
|
323 |
+
* )
|
324 |
+
* </code>
|
325 |
+
*
|
326 |
+
* @param string $url A valid URL, e.g.: http://pear.php.net/credits.php
|
327 |
+
* @param integer $timeout Timeout in seconds (default = 10)
|
328 |
+
*
|
329 |
+
* @return array Returns associative array of response headers on success
|
330 |
+
* or PEAR error on failure.
|
331 |
+
* @static
|
332 |
+
* @access public
|
333 |
+
* @see HTTP_Client::head()
|
334 |
+
* @see HTTP_Request
|
335 |
+
*/
|
336 |
+
function head($url, $timeout = 10)
|
337 |
+
{
|
338 |
+
$p = parse_url($url);
|
339 |
+
if (!isset($p['scheme'])) {
|
340 |
+
$p = parse_url(HTTP::absoluteURI($url));
|
341 |
+
} elseif ($p['scheme'] != 'http') {
|
342 |
+
return HTTP::raiseError('Unsupported protocol: '. $p['scheme']);
|
343 |
+
}
|
344 |
+
|
345 |
+
$port = isset($p['port']) ? $p['port'] : 80;
|
346 |
+
|
347 |
+
if (!$fp = @fsockopen($p['host'], $port, $eno, $estr, $timeout)) {
|
348 |
+
return HTTP::raiseError("Connection error: $estr ($eno)");
|
349 |
+
}
|
350 |
+
|
351 |
+
$path = !empty($p['path']) ? $p['path'] : '/';
|
352 |
+
$path .= !empty($p['query']) ? '?' . $p['query'] : '';
|
353 |
+
|
354 |
+
fputs($fp, "HEAD $path HTTP/1.0\r\n");
|
355 |
+
fputs($fp, 'Host: ' . $p['host'] . ':' . $port . "\r\n");
|
356 |
+
fputs($fp, "Connection: close\r\n\r\n");
|
357 |
+
|
358 |
+
$response = rtrim(fgets($fp, 4096));
|
359 |
+
if (preg_match("|^HTTP/[^\s]*\s(.*?)\s|", $response, $status)) {
|
360 |
+
$headers['response_code'] = $status[1];
|
361 |
+
}
|
362 |
+
$headers['response'] = $response;
|
363 |
+
|
364 |
+
while ($line = fgets($fp, 4096)) {
|
365 |
+
if (!trim($line)) {
|
366 |
+
break;
|
367 |
+
}
|
368 |
+
if (($pos = strpos($line, ':')) !== false) {
|
369 |
+
$header = substr($line, 0, $pos);
|
370 |
+
$value = trim(substr($line, $pos + 1));
|
371 |
+
|
372 |
+
$headers[$header] = $value;
|
373 |
+
}
|
374 |
+
}
|
375 |
+
fclose($fp);
|
376 |
+
return $headers;
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* This function redirects the client. This is done by issuing
|
381 |
+
* a "Location" header and exiting if wanted. If you set $rfc2616 to true
|
382 |
+
* HTTP will output a hypertext note with the location of the redirect.
|
383 |
+
*
|
384 |
+
* @param string $url URL where the redirect should go to.
|
385 |
+
* @param bool $exit Whether to exit immediately after redirection.
|
386 |
+
* @param bool $rfc2616 Wheter to output a hypertext note where we're
|
387 |
+
* redirecting to (Redirecting to
|
388 |
+
* <a href="...">...</a>.)
|
389 |
+
*
|
390 |
+
* @return boolean Returns TRUE on succes (or exits) or FALSE if headers
|
391 |
+
* have already been sent.
|
392 |
+
* @static
|
393 |
+
* @access public
|
394 |
+
*/
|
395 |
+
function redirect($url, $exit = true, $rfc2616 = false)
|
396 |
+
{
|
397 |
+
if (headers_sent()) {
|
398 |
+
return false;
|
399 |
+
}
|
400 |
+
|
401 |
+
$url = HTTP::absoluteURI($url);
|
402 |
+
header('Location: '. $url);
|
403 |
+
|
404 |
+
if ($rfc2616 && isset($_SERVER['REQUEST_METHOD'])
|
405 |
+
&& $_SERVER['REQUEST_METHOD'] != 'HEAD') {
|
406 |
+
echo '
|
407 |
+
<p>Redirecting to: <a href="'.str_replace('"', '%22', $url).'">'
|
408 |
+
.htmlspecialchars($url).'</a>.</p>
|
409 |
+
<script type="text/javascript">
|
410 |
+
//<![CDATA[
|
411 |
+
if (location.replace == null) {
|
412 |
+
location.replace = location.assign;
|
413 |
+
}
|
414 |
+
location.replace("'.str_replace('"', '\\"', $url).'");
|
415 |
+
// ]]>
|
416 |
+
</script>';
|
417 |
+
}
|
418 |
+
if ($exit) {
|
419 |
+
exit;
|
420 |
+
}
|
421 |
+
return true;
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* This function returns the absolute URI for the partial URL passed.
|
426 |
+
* The current scheme (HTTP/HTTPS), host server, port, current script
|
427 |
+
* location are used if necessary to resolve any relative URLs.
|
428 |
+
*
|
429 |
+
* Offsets potentially created by PATH_INFO are taken care of to resolve
|
430 |
+
* relative URLs to the current script.
|
431 |
+
*
|
432 |
+
* You can choose a new protocol while resolving the URI. This is
|
433 |
+
* particularly useful when redirecting a web browser using relative URIs
|
434 |
+
* and to switch from HTTP to HTTPS, or vice-versa, at the same time.
|
435 |
+
*
|
436 |
+
* @param string $url Absolute or relative URI the redirect should
|
437 |
+
* go to.
|
438 |
+
* @param string $protocol Protocol to use when redirecting URIs.
|
439 |
+
* @param integer $port A new port number.
|
440 |
+
*
|
441 |
+
* @return string The absolute URI.
|
442 |
+
* @author Philippe Jausions <Philippe.Jausions@11abacus.com>
|
443 |
+
* @static
|
444 |
+
* @access public
|
445 |
+
*/
|
446 |
+
function absoluteURI($url = null, $protocol = null, $port = null)
|
447 |
+
{
|
448 |
+
// filter CR/LF
|
449 |
+
$url = str_replace(array("\r", "\n"), ' ', $url);
|
450 |
+
|
451 |
+
// Mess around protocol and port with already absolute URIs
|
452 |
+
if (preg_match('!^([a-z0-9]+)://!i', $url)) {
|
453 |
+
if (empty($protocol) && empty($port)) {
|
454 |
+
return $url;
|
455 |
+
}
|
456 |
+
if (!empty($protocol)) {
|
457 |
+
$url = $protocol .':'. end($array = explode(':', $url, 2));
|
458 |
+
}
|
459 |
+
if (!empty($port)) {
|
460 |
+
$url = preg_replace('!^(([a-z0-9]+)://[^/:]+)(:[\d]+)?!i',
|
461 |
+
'\1:'. $port, $url);
|
462 |
+
}
|
463 |
+
return $url;
|
464 |
+
}
|
465 |
+
|
466 |
+
$host = 'localhost';
|
467 |
+
if (!empty($_SERVER['HTTP_HOST'])) {
|
468 |
+
list($host) = explode(':', $_SERVER['HTTP_HOST']);
|
469 |
+
} elseif (!empty($_SERVER['SERVER_NAME'])) {
|
470 |
+
list($host) = explode(':', $_SERVER['SERVER_NAME']);
|
471 |
+
}
|
472 |
+
|
473 |
+
if (empty($protocol)) {
|
474 |
+
if (isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'], 'on')) {
|
475 |
+
$protocol = 'https';
|
476 |
+
} else {
|
477 |
+
$protocol = 'http';
|
478 |
+
}
|
479 |
+
if (!isset($port) || $port != intval($port)) {
|
480 |
+
$port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
|
481 |
+
}
|
482 |
+
}
|
483 |
+
|
484 |
+
if ($protocol == 'http' && $port == 80) {
|
485 |
+
unset($port);
|
486 |
+
}
|
487 |
+
if ($protocol == 'https' && $port == 443) {
|
488 |
+
unset($port);
|
489 |
+
}
|
490 |
+
|
491 |
+
$server = $protocol.'://'.$host.(isset($port) ? ':'.$port : '');
|
492 |
+
|
493 |
+
$uriAll = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI']
|
494 |
+
: $_SERVER['PHP_SELF'];
|
495 |
+
if (false !== ($q = strpos($uriAll, '?'))) {
|
496 |
+
$uriBase = substr($uriAll, 0, $q);
|
497 |
+
} else {
|
498 |
+
$uriBase = $uriAll;
|
499 |
+
}
|
500 |
+
if (!strlen($url) || $url{0} == '#') {
|
501 |
+
$url = $uriAll.$url;
|
502 |
+
} elseif ($url{0} == '?') {
|
503 |
+
$url = $uriBase.$url;
|
504 |
+
}
|
505 |
+
if ($url{0} == '/') {
|
506 |
+
return $server . $url;
|
507 |
+
}
|
508 |
+
|
509 |
+
// Adjust for PATH_INFO if needed
|
510 |
+
if (isset($_SERVER['PATH_INFO']) && strlen($_SERVER['PATH_INFO'])) {
|
511 |
+
$path = dirname(substr($uriBase, 0,
|
512 |
+
-strlen($_SERVER['PATH_INFO'])));
|
513 |
+
} else {
|
514 |
+
/**
|
515 |
+
* Fixes bug #12672 PHP_SELF ending on / causes incorrect redirects
|
516 |
+
*
|
517 |
+
* @link http://pear.php.net/bugs/12672
|
518 |
+
*/
|
519 |
+
$path = dirname($uriBase.'-');
|
520 |
+
}
|
521 |
+
|
522 |
+
if (substr($path = strtr($path, '\\', '/'), -1) != '/') {
|
523 |
+
$path .= '/';
|
524 |
+
}
|
525 |
+
|
526 |
+
return $server . $path . $url;
|
527 |
+
}
|
528 |
+
|
529 |
+
/**
|
530 |
+
* Raise Error
|
531 |
+
*
|
532 |
+
* Lazy raising of PEAR_Errors.
|
533 |
+
*
|
534 |
+
* @param mixed $error Error
|
535 |
+
* @param integer $code Error code
|
536 |
+
*
|
537 |
+
* @return object PEAR_Error
|
538 |
+
* @static
|
539 |
+
* @access protected
|
540 |
+
*/
|
541 |
+
function raiseError($error = null, $code = null)
|
542 |
+
{
|
543 |
+
include_once 'PEAR.php';
|
544 |
+
return PEAR::raiseError($error, $code);
|
545 |
+
}
|
546 |
+
}
|
547 |
+
|
548 |
+
?>
|
lib/PEAR/HTTP/Request.php
ADDED
@@ -0,0 +1,1521 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class for performing HTTP requests
|
4 |
+
*
|
5 |
+
* PHP versions 4 and 5
|
6 |
+
*
|
7 |
+
* LICENSE:
|
8 |
+
*
|
9 |
+
* Copyright (c) 2002-2007, Richard Heyes
|
10 |
+
* All rights reserved.
|
11 |
+
*
|
12 |
+
* Redistribution and use in source and binary forms, with or without
|
13 |
+
* modification, are permitted provided that the following conditions
|
14 |
+
* are met:
|
15 |
+
*
|
16 |
+
* o Redistributions of source code must retain the above copyright
|
17 |
+
* notice, this list of conditions and the following disclaimer.
|
18 |
+
* o Redistributions in binary form must reproduce the above copyright
|
19 |
+
* notice, this list of conditions and the following disclaimer in the
|
20 |
+
* documentation and/or other materials provided with the distribution.
|
21 |
+
* o The names of the authors may not be used to endorse or promote
|
22 |
+
* products derived from this software without specific prior written
|
23 |
+
* permission.
|
24 |
+
*
|
25 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
26 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
27 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
28 |
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
29 |
+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
30 |
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
31 |
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
32 |
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
33 |
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
34 |
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
35 |
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
36 |
+
*
|
37 |
+
* @category HTTP
|
38 |
+
* @package HTTP_Request
|
39 |
+
* @author Richard Heyes <richard@phpguru.org>
|
40 |
+
* @author Alexey Borzov <avb@php.net>
|
41 |
+
* @copyright 2002-2007 Richard Heyes
|
42 |
+
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
43 |
+
* @version CVS: $Id: Request.php,v 1.63 2008/10/11 11:07:10 avb Exp $
|
44 |
+
* @link http://pear.php.net/package/HTTP_Request/
|
45 |
+
*/
|
46 |
+
|
47 |
+
/**
|
48 |
+
* PEAR and PEAR_Error classes (for error handling)
|
49 |
+
*/
|
50 |
+
require_once 'PEAR.php';
|
51 |
+
/**
|
52 |
+
* Socket class
|
53 |
+
*/
|
54 |
+
require_once 'Net/Socket.php';
|
55 |
+
/**
|
56 |
+
* URL handling class
|
57 |
+
*/
|
58 |
+
require_once 'Net/URL.php';
|
59 |
+
|
60 |
+
/**#@+
|
61 |
+
* Constants for HTTP request methods
|
62 |
+
*/
|
63 |
+
define('HTTP_REQUEST_METHOD_GET', 'GET', true);
|
64 |
+
define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true);
|
65 |
+
define('HTTP_REQUEST_METHOD_POST', 'POST', true);
|
66 |
+
define('HTTP_REQUEST_METHOD_PUT', 'PUT', true);
|
67 |
+
define('HTTP_REQUEST_METHOD_DELETE', 'DELETE', true);
|
68 |
+
define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
|
69 |
+
define('HTTP_REQUEST_METHOD_TRACE', 'TRACE', true);
|
70 |
+
/**#@-*/
|
71 |
+
|
72 |
+
/**#@+
|
73 |
+
* Constants for HTTP request error codes
|
74 |
+
*/
|
75 |
+
define('HTTP_REQUEST_ERROR_FILE', 1);
|
76 |
+
define('HTTP_REQUEST_ERROR_URL', 2);
|
77 |
+
define('HTTP_REQUEST_ERROR_PROXY', 4);
|
78 |
+
define('HTTP_REQUEST_ERROR_REDIRECTS', 8);
|
79 |
+
define('HTTP_REQUEST_ERROR_RESPONSE', 16);
|
80 |
+
define('HTTP_REQUEST_ERROR_GZIP_METHOD', 32);
|
81 |
+
define('HTTP_REQUEST_ERROR_GZIP_READ', 64);
|
82 |
+
define('HTTP_REQUEST_ERROR_GZIP_DATA', 128);
|
83 |
+
define('HTTP_REQUEST_ERROR_GZIP_CRC', 256);
|
84 |
+
/**#@-*/
|
85 |
+
|
86 |
+
/**#@+
|
87 |
+
* Constants for HTTP protocol versions
|
88 |
+
*/
|
89 |
+
define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
|
90 |
+
define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
|
91 |
+
/**#@-*/
|
92 |
+
|
93 |
+
if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
|
94 |
+
/**
|
95 |
+
* Whether string functions are overloaded by their mbstring equivalents
|
96 |
+
*/
|
97 |
+
define('HTTP_REQUEST_MBSTRING', true);
|
98 |
+
} else {
|
99 |
+
/**
|
100 |
+
* @ignore
|
101 |
+
*/
|
102 |
+
define('HTTP_REQUEST_MBSTRING', false);
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Class for performing HTTP requests
|
107 |
+
*
|
108 |
+
* Simple example (fetches yahoo.com and displays it):
|
109 |
+
* <code>
|
110 |
+
* $a = &new HTTP_Request('http://www.yahoo.com/');
|
111 |
+
* $a->sendRequest();
|
112 |
+
* echo $a->getResponseBody();
|
113 |
+
* </code>
|
114 |
+
*
|
115 |
+
* @category HTTP
|
116 |
+
* @package HTTP_Request
|
117 |
+
* @author Richard Heyes <richard@phpguru.org>
|
118 |
+
* @author Alexey Borzov <avb@php.net>
|
119 |
+
* @version Release: 1.4.4
|
120 |
+
*/
|
121 |
+
class HTTP_Request
|
122 |
+
{
|
123 |
+
/**#@+
|
124 |
+
* @access private
|
125 |
+
*/
|
126 |
+
/**
|
127 |
+
* Instance of Net_URL
|
128 |
+
* @var Net_URL
|
129 |
+
*/
|
130 |
+
var $_url;
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Type of request
|
134 |
+
* @var string
|
135 |
+
*/
|
136 |
+
var $_method;
|
137 |
+
|
138 |
+
/**
|
139 |
+
* HTTP Version
|
140 |
+
* @var string
|
141 |
+
*/
|
142 |
+
var $_http;
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Request headers
|
146 |
+
* @var array
|
147 |
+
*/
|
148 |
+
var $_requestHeaders;
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Basic Auth Username
|
152 |
+
* @var string
|
153 |
+
*/
|
154 |
+
var $_user;
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Basic Auth Password
|
158 |
+
* @var string
|
159 |
+
*/
|
160 |
+
var $_pass;
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Socket object
|
164 |
+
* @var Net_Socket
|
165 |
+
*/
|
166 |
+
var $_sock;
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Proxy server
|
170 |
+
* @var string
|
171 |
+
*/
|
172 |
+
var $_proxy_host;
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Proxy port
|
176 |
+
* @var integer
|
177 |
+
*/
|
178 |
+
var $_proxy_port;
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Proxy username
|
182 |
+
* @var string
|
183 |
+
*/
|
184 |
+
var $_proxy_user;
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Proxy password
|
188 |
+
* @var string
|
189 |
+
*/
|
190 |
+
var $_proxy_pass;
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Post data
|
194 |
+
* @var array
|
195 |
+
*/
|
196 |
+
var $_postData;
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Request body
|
200 |
+
* @var string
|
201 |
+
*/
|
202 |
+
var $_body;
|
203 |
+
|
204 |
+
/**
|
205 |
+
* A list of methods that MUST NOT have a request body, per RFC 2616
|
206 |
+
* @var array
|
207 |
+
*/
|
208 |
+
var $_bodyDisallowed = array('TRACE');
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Methods having defined semantics for request body
|
212 |
+
*
|
213 |
+
* Content-Length header (indicating that the body follows, section 4.3 of
|
214 |
+
* RFC 2616) will be sent for these methods even if no body was added
|
215 |
+
*
|
216 |
+
* @var array
|
217 |
+
*/
|
218 |
+
var $_bodyRequired = array('POST', 'PUT');
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Files to post
|
222 |
+
* @var array
|
223 |
+
*/
|
224 |
+
var $_postFiles = array();
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Connection timeout.
|
228 |
+
* @var float
|
229 |
+
*/
|
230 |
+
var $_timeout;
|
231 |
+
|
232 |
+
/**
|
233 |
+
* HTTP_Response object
|
234 |
+
* @var HTTP_Response
|
235 |
+
*/
|
236 |
+
var $_response;
|
237 |
+
|
238 |
+
/**
|
239 |
+
* Whether to allow redirects
|
240 |
+
* @var boolean
|
241 |
+
*/
|
242 |
+
var $_allowRedirects;
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Maximum redirects allowed
|
246 |
+
* @var integer
|
247 |
+
*/
|
248 |
+
var $_maxRedirects;
|
249 |
+
|
250 |
+
/**
|
251 |
+
* Current number of redirects
|
252 |
+
* @var integer
|
253 |
+
*/
|
254 |
+
var $_redirects;
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Whether to append brackets [] to array variables
|
258 |
+
* @var bool
|
259 |
+
*/
|
260 |
+
var $_useBrackets = true;
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Attached listeners
|
264 |
+
* @var array
|
265 |
+
*/
|
266 |
+
var $_listeners = array();
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Whether to save response body in response object property
|
270 |
+
* @var bool
|
271 |
+
*/
|
272 |
+
var $_saveBody = true;
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Timeout for reading from socket (array(seconds, microseconds))
|
276 |
+
* @var array
|
277 |
+
*/
|
278 |
+
var $_readTimeout = null;
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Options to pass to Net_Socket::connect. See stream_context_create
|
282 |
+
* @var array
|
283 |
+
*/
|
284 |
+
var $_socketOptions = null;
|
285 |
+
/**#@-*/
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Constructor
|
289 |
+
*
|
290 |
+
* Sets up the object
|
291 |
+
* @param string The url to fetch/access
|
292 |
+
* @param array Associative array of parameters which can have the following keys:
|
293 |
+
* <ul>
|
294 |
+
* <li>method - Method to use, GET, POST etc (string)</li>
|
295 |
+
* <li>http - HTTP Version to use, 1.0 or 1.1 (string)</li>
|
296 |
+
* <li>user - Basic Auth username (string)</li>
|
297 |
+
* <li>pass - Basic Auth password (string)</li>
|
298 |
+
* <li>proxy_host - Proxy server host (string)</li>
|
299 |
+
* <li>proxy_port - Proxy server port (integer)</li>
|
300 |
+
* <li>proxy_user - Proxy auth username (string)</li>
|
301 |
+
* <li>proxy_pass - Proxy auth password (string)</li>
|
302 |
+
* <li>timeout - Connection timeout in seconds (float)</li>
|
303 |
+
* <li>allowRedirects - Whether to follow redirects or not (bool)</li>
|
304 |
+
* <li>maxRedirects - Max number of redirects to follow (integer)</li>
|
305 |
+
* <li>useBrackets - Whether to append [] to array variable names (bool)</li>
|
306 |
+
* <li>saveBody - Whether to save response body in response object property (bool)</li>
|
307 |
+
* <li>readTimeout - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
|
308 |
+
* <li>socketOptions - Options to pass to Net_Socket object (array)</li>
|
309 |
+
* </ul>
|
310 |
+
* @access public
|
311 |
+
*/
|
312 |
+
function HTTP_Request($url = '', $params = array())
|
313 |
+
{
|
314 |
+
$this->_method = HTTP_REQUEST_METHOD_GET;
|
315 |
+
$this->_http = HTTP_REQUEST_HTTP_VER_1_1;
|
316 |
+
$this->_requestHeaders = array();
|
317 |
+
$this->_postData = array();
|
318 |
+
$this->_body = null;
|
319 |
+
|
320 |
+
$this->_user = null;
|
321 |
+
$this->_pass = null;
|
322 |
+
|
323 |
+
$this->_proxy_host = null;
|
324 |
+
$this->_proxy_port = null;
|
325 |
+
$this->_proxy_user = null;
|
326 |
+
$this->_proxy_pass = null;
|
327 |
+
|
328 |
+
$this->_allowRedirects = false;
|
329 |
+
$this->_maxRedirects = 3;
|
330 |
+
$this->_redirects = 0;
|
331 |
+
|
332 |
+
$this->_timeout = null;
|
333 |
+
$this->_response = null;
|
334 |
+
|
335 |
+
foreach ($params as $key => $value) {
|
336 |
+
$this->{'_' . $key} = $value;
|
337 |
+
}
|
338 |
+
|
339 |
+
if (!empty($url)) {
|
340 |
+
$this->setURL($url);
|
341 |
+
}
|
342 |
+
|
343 |
+
// Default useragent
|
344 |
+
$this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
|
345 |
+
|
346 |
+
// We don't do keep-alives by default
|
347 |
+
$this->addHeader('Connection', 'close');
|
348 |
+
|
349 |
+
// Basic authentication
|
350 |
+
if (!empty($this->_user)) {
|
351 |
+
$this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass));
|
352 |
+
}
|
353 |
+
|
354 |
+
// Proxy authentication (see bug #5913)
|
355 |
+
if (!empty($this->_proxy_user)) {
|
356 |
+
$this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($this->_proxy_user . ':' . $this->_proxy_pass));
|
357 |
+
}
|
358 |
+
|
359 |
+
// Use gzip encoding if possible
|
360 |
+
if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) {
|
361 |
+
$this->addHeader('Accept-Encoding', 'gzip');
|
362 |
+
}
|
363 |
+
}
|
364 |
+
|
365 |
+
/**
|
366 |
+
* Generates a Host header for HTTP/1.1 requests
|
367 |
+
*
|
368 |
+
* @access private
|
369 |
+
* @return string
|
370 |
+
*/
|
371 |
+
function _generateHostHeader()
|
372 |
+
{
|
373 |
+
if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
|
374 |
+
$host = $this->_url->host . ':' . $this->_url->port;
|
375 |
+
|
376 |
+
} elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
|
377 |
+
$host = $this->_url->host . ':' . $this->_url->port;
|
378 |
+
|
379 |
+
} elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
|
380 |
+
$host = $this->_url->host . ':' . $this->_url->port;
|
381 |
+
|
382 |
+
} else {
|
383 |
+
$host = $this->_url->host;
|
384 |
+
}
|
385 |
+
|
386 |
+
return $host;
|
387 |
+
}
|
388 |
+
|
389 |
+
/**
|
390 |
+
* Resets the object to its initial state (DEPRECATED).
|
391 |
+
* Takes the same parameters as the constructor.
|
392 |
+
*
|
393 |
+
* @param string $url The url to be requested
|
394 |
+
* @param array $params Associative array of parameters
|
395 |
+
* (see constructor for details)
|
396 |
+
* @access public
|
397 |
+
* @deprecated deprecated since 1.2, call the constructor if this is necessary
|
398 |
+
*/
|
399 |
+
function reset($url, $params = array())
|
400 |
+
{
|
401 |
+
$this->HTTP_Request($url, $params);
|
402 |
+
}
|
403 |
+
|
404 |
+
/**
|
405 |
+
* Sets the URL to be requested
|
406 |
+
*
|
407 |
+
* @param string The url to be requested
|
408 |
+
* @access public
|
409 |
+
*/
|
410 |
+
function setURL($url)
|
411 |
+
{
|
412 |
+
$this->_url = new Net_URL($url, $this->_useBrackets);
|
413 |
+
|
414 |
+
if (!empty($this->_url->user) || !empty($this->_url->pass)) {
|
415 |
+
$this->setBasicAuth($this->_url->user, $this->_url->pass);
|
416 |
+
}
|
417 |
+
|
418 |
+
if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
|
419 |
+
$this->addHeader('Host', $this->_generateHostHeader());
|
420 |
+
}
|
421 |
+
|
422 |
+
// set '/' instead of empty path rather than check later (see bug #8662)
|
423 |
+
if (empty($this->_url->path)) {
|
424 |
+
$this->_url->path = '/';
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Returns the current request URL
|
430 |
+
*
|
431 |
+
* @return string Current request URL
|
432 |
+
* @access public
|
433 |
+
*/
|
434 |
+
function getUrl()
|
435 |
+
{
|
436 |
+
return empty($this->_url)? '': $this->_url->getUrl();
|
437 |
+
}
|
438 |
+
|
439 |
+
/**
|
440 |
+
* Sets a proxy to be used
|
441 |
+
*
|
442 |
+
* @param string Proxy host
|
443 |
+
* @param int Proxy port
|
444 |
+
* @param string Proxy username
|
445 |
+
* @param string Proxy password
|
446 |
+
* @access public
|
447 |
+
*/
|
448 |
+
function setProxy($host, $port = 8080, $user = null, $pass = null)
|
449 |
+
{
|
450 |
+
$this->_proxy_host = $host;
|
451 |
+
$this->_proxy_port = $port;
|
452 |
+
$this->_proxy_user = $user;
|
453 |
+
$this->_proxy_pass = $pass;
|
454 |
+
|
455 |
+
if (!empty($user)) {
|
456 |
+
$this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
|
457 |
+
}
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Sets basic authentication parameters
|
462 |
+
*
|
463 |
+
* @param string Username
|
464 |
+
* @param string Password
|
465 |
+
*/
|
466 |
+
function setBasicAuth($user, $pass)
|
467 |
+
{
|
468 |
+
$this->_user = $user;
|
469 |
+
$this->_pass = $pass;
|
470 |
+
|
471 |
+
$this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
|
472 |
+
}
|
473 |
+
|
474 |
+
/**
|
475 |
+
* Sets the method to be used, GET, POST etc.
|
476 |
+
*
|
477 |
+
* @param string Method to use. Use the defined constants for this
|
478 |
+
* @access public
|
479 |
+
*/
|
480 |
+
function setMethod($method)
|
481 |
+
{
|
482 |
+
$this->_method = $method;
|
483 |
+
}
|
484 |
+
|
485 |
+
/**
|
486 |
+
* Sets the HTTP version to use, 1.0 or 1.1
|
487 |
+
*
|
488 |
+
* @param string Version to use. Use the defined constants for this
|
489 |
+
* @access public
|
490 |
+
*/
|
491 |
+
function setHttpVer($http)
|
492 |
+
{
|
493 |
+
$this->_http = $http;
|
494 |
+
}
|
495 |
+
|
496 |
+
/**
|
497 |
+
* Adds a request header
|
498 |
+
*
|
499 |
+
* @param string Header name
|
500 |
+
* @param string Header value
|
501 |
+
* @access public
|
502 |
+
*/
|
503 |
+
function addHeader($name, $value)
|
504 |
+
{
|
505 |
+
$this->_requestHeaders[strtolower($name)] = $value;
|
506 |
+
}
|
507 |
+
|
508 |
+
/**
|
509 |
+
* Removes a request header
|
510 |
+
*
|
511 |
+
* @param string Header name to remove
|
512 |
+
* @access public
|
513 |
+
*/
|
514 |
+
function removeHeader($name)
|
515 |
+
{
|
516 |
+
if (isset($this->_requestHeaders[strtolower($name)])) {
|
517 |
+
unset($this->_requestHeaders[strtolower($name)]);
|
518 |
+
}
|
519 |
+
}
|
520 |
+
|
521 |
+
/**
|
522 |
+
* Adds a querystring parameter
|
523 |
+
*
|
524 |
+
* @param string Querystring parameter name
|
525 |
+
* @param string Querystring parameter value
|
526 |
+
* @param bool Whether the value is already urlencoded or not, default = not
|
527 |
+
* @access public
|
528 |
+
*/
|
529 |
+
function addQueryString($name, $value, $preencoded = false)
|
530 |
+
{
|
531 |
+
$this->_url->addQueryString($name, $value, $preencoded);
|
532 |
+
}
|
533 |
+
|
534 |
+
/**
|
535 |
+
* Sets the querystring to literally what you supply
|
536 |
+
*
|
537 |
+
* @param string The querystring data. Should be of the format foo=bar&x=y etc
|
538 |
+
* @param bool Whether data is already urlencoded or not, default = already encoded
|
539 |
+
* @access public
|
540 |
+
*/
|
541 |
+
function addRawQueryString($querystring, $preencoded = true)
|
542 |
+
{
|
543 |
+
$this->_url->addRawQueryString($querystring, $preencoded);
|
544 |
+
}
|
545 |
+
|
546 |
+
/**
|
547 |
+
* Adds postdata items
|
548 |
+
*
|
549 |
+
* @param string Post data name
|
550 |
+
* @param string Post data value
|
551 |
+
* @param bool Whether data is already urlencoded or not, default = not
|
552 |
+
* @access public
|
553 |
+
*/
|
554 |
+
function addPostData($name, $value, $preencoded = false)
|
555 |
+
{
|
556 |
+
if ($preencoded) {
|
557 |
+
$this->_postData[$name] = $value;
|
558 |
+
} else {
|
559 |
+
$this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
|
560 |
+
}
|
561 |
+
}
|
562 |
+
|
563 |
+
/**
|
564 |
+
* Recursively applies the callback function to the value
|
565 |
+
*
|
566 |
+
* @param mixed Callback function
|
567 |
+
* @param mixed Value to process
|
568 |
+
* @access private
|
569 |
+
* @return mixed Processed value
|
570 |
+
*/
|
571 |
+
function _arrayMapRecursive($callback, $value)
|
572 |
+
{
|
573 |
+
if (!is_array($value)) {
|
574 |
+
return call_user_func($callback, $value);
|
575 |
+
} else {
|
576 |
+
$map = array();
|
577 |
+
foreach ($value as $k => $v) {
|
578 |
+
$map[$k] = $this->_arrayMapRecursive($callback, $v);
|
579 |
+
}
|
580 |
+
return $map;
|
581 |
+
}
|
582 |
+
}
|
583 |
+
|
584 |
+
/**
|
585 |
+
* Adds a file to form-based file upload
|
586 |
+
*
|
587 |
+
* Used to emulate file upload via a HTML form. The method also sets
|
588 |
+
* Content-Type of HTTP request to 'multipart/form-data'.
|
589 |
+
*
|
590 |
+
* If you just want to send the contents of a file as the body of HTTP
|
591 |
+
* request you should use setBody() method.
|
592 |
+
*
|
593 |
+
* @access public
|
594 |
+
* @param string name of file-upload field
|
595 |
+
* @param mixed file name(s)
|
596 |
+
* @param mixed content-type(s) of file(s) being uploaded
|
597 |
+
* @return bool true on success
|
598 |
+
* @throws PEAR_Error
|
599 |
+
*/
|
600 |
+
function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
|
601 |
+
{
|
602 |
+
if (!is_array($fileName) && !is_readable($fileName)) {
|
603 |
+
return PEAR::raiseError("File '{$fileName}' is not readable", HTTP_REQUEST_ERROR_FILE);
|
604 |
+
} elseif (is_array($fileName)) {
|
605 |
+
foreach ($fileName as $name) {
|
606 |
+
if (!is_readable($name)) {
|
607 |
+
return PEAR::raiseError("File '{$name}' is not readable", HTTP_REQUEST_ERROR_FILE);
|
608 |
+
}
|
609 |
+
}
|
610 |
+
}
|
611 |
+
$this->addHeader('Content-Type', 'multipart/form-data');
|
612 |
+
$this->_postFiles[$inputName] = array(
|
613 |
+
'name' => $fileName,
|
614 |
+
'type' => $contentType
|
615 |
+
);
|
616 |
+
return true;
|
617 |
+
}
|
618 |
+
|
619 |
+
/**
|
620 |
+
* Adds raw postdata (DEPRECATED)
|
621 |
+
*
|
622 |
+
* @param string The data
|
623 |
+
* @param bool Whether data is preencoded or not, default = already encoded
|
624 |
+
* @access public
|
625 |
+
* @deprecated deprecated since 1.3.0, method setBody() should be used instead
|
626 |
+
*/
|
627 |
+
function addRawPostData($postdata, $preencoded = true)
|
628 |
+
{
|
629 |
+
$this->_body = $preencoded ? $postdata : urlencode($postdata);
|
630 |
+
}
|
631 |
+
|
632 |
+
/**
|
633 |
+
* Sets the request body (for POST, PUT and similar requests)
|
634 |
+
*
|
635 |
+
* @param string Request body
|
636 |
+
* @access public
|
637 |
+
*/
|
638 |
+
function setBody($body)
|
639 |
+
{
|
640 |
+
$this->_body = $body;
|
641 |
+
}
|
642 |
+
|
643 |
+
/**
|
644 |
+
* Clears any postdata that has been added (DEPRECATED).
|
645 |
+
*
|
646 |
+
* Useful for multiple request scenarios.
|
647 |
+
*
|
648 |
+
* @access public
|
649 |
+
* @deprecated deprecated since 1.2
|
650 |
+
*/
|
651 |
+
function clearPostData()
|
652 |
+
{
|
653 |
+
$this->_postData = null;
|
654 |
+
}
|
655 |
+
|
656 |
+
/**
|
657 |
+
* Appends a cookie to "Cookie:" header
|
658 |
+
*
|
659 |
+
* @param string $name cookie name
|
660 |
+
* @param string $value cookie value
|
661 |
+
* @access public
|
662 |
+
*/
|
663 |
+
function addCookie($name, $value)
|
664 |
+
{
|
665 |
+
$cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
|
666 |
+
$this->addHeader('Cookie', $cookies . $name . '=' . $value);
|
667 |
+
}
|
668 |
+
|
669 |
+
/**
|
670 |
+
* Clears any cookies that have been added (DEPRECATED).
|
671 |
+
*
|
672 |
+
* Useful for multiple request scenarios
|
673 |
+
*
|
674 |
+
* @access public
|
675 |
+
* @deprecated deprecated since 1.2
|
676 |
+
*/
|
677 |
+
function clearCookies()
|
678 |
+
{
|
679 |
+
$this->removeHeader('Cookie');
|
680 |
+
}
|
681 |
+
|
682 |
+
/**
|
683 |
+
* Sends the request
|
684 |
+
*
|
685 |
+
* @access public
|
686 |
+
* @param bool Whether to store response body in Response object property,
|
687 |
+
* set this to false if downloading a LARGE file and using a Listener
|
688 |
+
* @return mixed PEAR error on error, true otherwise
|
689 |
+
*/
|
690 |
+
function sendRequest($saveBody = true)
|
691 |
+
{
|
692 |
+
if (!$this->_url instanceof Net_Url) {
|
693 |
+
return PEAR::raiseError('No URL given', HTTP_REQUEST_ERROR_URL);
|
694 |
+
}
|
695 |
+
|
696 |
+
$host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
|
697 |
+
$port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
|
698 |
+
|
699 |
+
if (strcasecmp($this->_url->protocol, 'https') == 0) {
|
700 |
+
// Bug #14127, don't try connecting to HTTPS sites without OpenSSL
|
701 |
+
if (version_compare(PHP_VERSION, '4.3.0', '<') || !extension_loaded('openssl')) {
|
702 |
+
return PEAR::raiseError('Need PHP 4.3.0 or later with OpenSSL support for https:// requests',
|
703 |
+
HTTP_REQUEST_ERROR_URL);
|
704 |
+
} elseif (isset($this->_proxy_host)) {
|
705 |
+
return PEAR::raiseError('HTTPS proxies are not supported', HTTP_REQUEST_ERROR_PROXY);
|
706 |
+
}
|
707 |
+
$host = 'ssl://' . $host;
|
708 |
+
}
|
709 |
+
|
710 |
+
// magic quotes may fuck up file uploads and chunked response processing
|
711 |
+
$magicQuotes = ini_get('magic_quotes_runtime');
|
712 |
+
ini_set('magic_quotes_runtime', false);
|
713 |
+
|
714 |
+
// RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
|
715 |
+
// connection token to a proxy server...
|
716 |
+
if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) &&
|
717 |
+
'Keep-Alive' == $this->_requestHeaders['connection'])
|
718 |
+
{
|
719 |
+
$this->removeHeader('connection');
|
720 |
+
}
|
721 |
+
|
722 |
+
$keepAlive = (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && empty($this->_requestHeaders['connection'])) ||
|
723 |
+
(!empty($this->_requestHeaders['connection']) && 'Keep-Alive' == $this->_requestHeaders['connection']);
|
724 |
+
$sockets = &PEAR::getStaticProperty('HTTP_Request', 'sockets');
|
725 |
+
$sockKey = $host . ':' . $port;
|
726 |
+
unset($this->_sock);
|
727 |
+
|
728 |
+
// There is a connected socket in the "static" property?
|
729 |
+
if ($keepAlive && !empty($sockets[$sockKey]) &&
|
730 |
+
!empty($sockets[$sockKey]->fp))
|
731 |
+
{
|
732 |
+
$this->_sock =& $sockets[$sockKey];
|
733 |
+
$err = null;
|
734 |
+
} else {
|
735 |
+
$this->_notify('connect');
|
736 |
+
$this->_sock = new Net_Socket();
|
737 |
+
$err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
|
738 |
+
}
|
739 |
+
PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
|
740 |
+
|
741 |
+
if (!PEAR::isError($err)) {
|
742 |
+
if (!empty($this->_readTimeout)) {
|
743 |
+
$this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
|
744 |
+
}
|
745 |
+
|
746 |
+
$this->_notify('sentRequest');
|
747 |
+
|
748 |
+
// Read the response
|
749 |
+
$this->_response = new HTTP_Response($this->_sock, $this->_listeners);
|
750 |
+
$err = $this->_response->process(
|
751 |
+
$this->_saveBody && $saveBody,
|
752 |
+
HTTP_REQUEST_METHOD_HEAD != $this->_method
|
753 |
+
);
|
754 |
+
|
755 |
+
if ($keepAlive) {
|
756 |
+
$keepAlive = (isset($this->_response->_headers['content-length'])
|
757 |
+
|| (isset($this->_response->_headers['transfer-encoding'])
|
758 |
+
&& strtolower($this->_response->_headers['transfer-encoding']) == 'chunked'));
|
759 |
+
if ($keepAlive) {
|
760 |
+
if (isset($this->_response->_headers['connection'])) {
|
761 |
+
$keepAlive = strtolower($this->_response->_headers['connection']) == 'keep-alive';
|
762 |
+
} else {
|
763 |
+
$keepAlive = 'HTTP/'.HTTP_REQUEST_HTTP_VER_1_1 == $this->_response->_protocol;
|
764 |
+
}
|
765 |
+
}
|
766 |
+
}
|
767 |
+
}
|
768 |
+
|
769 |
+
ini_set('magic_quotes_runtime', $magicQuotes);
|
770 |
+
|
771 |
+
if (PEAR::isError($err)) {
|
772 |
+
return $err;
|
773 |
+
}
|
774 |
+
|
775 |
+
if (!$keepAlive) {
|
776 |
+
$this->disconnect();
|
777 |
+
// Store the connected socket in "static" property
|
778 |
+
} elseif (empty($sockets[$sockKey]) || empty($sockets[$sockKey]->fp)) {
|
779 |
+
$sockets[$sockKey] =& $this->_sock;
|
780 |
+
}
|
781 |
+
|
782 |
+
// Check for redirection
|
783 |
+
if ( $this->_allowRedirects
|
784 |
+
AND $this->_redirects <= $this->_maxRedirects
|
785 |
+
AND $this->getResponseCode() > 300
|
786 |
+
AND $this->getResponseCode() < 399
|
787 |
+
AND !empty($this->_response->_headers['location'])) {
|
788 |
+
|
789 |
+
|
790 |
+
$redirect = $this->_response->_headers['location'];
|
791 |
+
|
792 |
+
// Absolute URL
|
793 |
+
if (preg_match('/^https?:\/\//i', $redirect)) {
|
794 |
+
$this->_url = new Net_URL($redirect);
|
795 |
+
$this->addHeader('Host', $this->_generateHostHeader());
|
796 |
+
// Absolute path
|
797 |
+
} elseif ($redirect{0} == '/') {
|
798 |
+
$this->_url->path = $redirect;
|
799 |
+
|
800 |
+
// Relative path
|
801 |
+
} elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
|
802 |
+
if (substr($this->_url->path, -1) == '/') {
|
803 |
+
$redirect = $this->_url->path . $redirect;
|
804 |
+
} else {
|
805 |
+
$redirect = dirname($this->_url->path) . '/' . $redirect;
|
806 |
+
}
|
807 |
+
$redirect = Net_URL::resolvePath($redirect);
|
808 |
+
$this->_url->path = $redirect;
|
809 |
+
|
810 |
+
// Filename, no path
|
811 |
+
} else {
|
812 |
+
if (substr($this->_url->path, -1) == '/') {
|
813 |
+
$redirect = $this->_url->path . $redirect;
|
814 |
+
} else {
|
815 |
+
$redirect = dirname($this->_url->path) . '/' . $redirect;
|
816 |
+
}
|
817 |
+
$this->_url->path = $redirect;
|
818 |
+
}
|
819 |
+
|
820 |
+
$this->_redirects++;
|
821 |
+
return $this->sendRequest($saveBody);
|
822 |
+
|
823 |
+
// Too many redirects
|
824 |
+
} elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
|
825 |
+
return PEAR::raiseError('Too many redirects', HTTP_REQUEST_ERROR_REDIRECTS);
|
826 |
+
}
|
827 |
+
|
828 |
+
return true;
|
829 |
+
}
|
830 |
+
|
831 |
+
/**
|
832 |
+
* Disconnect the socket, if connected. Only useful if using Keep-Alive.
|
833 |
+
*
|
834 |
+
* @access public
|
835 |
+
*/
|
836 |
+
function disconnect()
|
837 |
+
{
|
838 |
+
if (!empty($this->_sock) && !empty($this->_sock->fp)) {
|
839 |
+
$this->_notify('disconnect');
|
840 |
+
$this->_sock->disconnect();
|
841 |
+
}
|
842 |
+
}
|
843 |
+
|
844 |
+
/**
|
845 |
+
* Returns the response code
|
846 |
+
*
|
847 |
+
* @access public
|
848 |
+
* @return mixed Response code, false if not set
|
849 |
+
*/
|
850 |
+
function getResponseCode()
|
851 |
+
{
|
852 |
+
return isset($this->_response->_code) ? $this->_response->_code : false;
|
853 |
+
}
|
854 |
+
|
855 |
+
/**
|
856 |
+
* Returns the response reason phrase
|
857 |
+
*
|
858 |
+
* @access public
|
859 |
+
* @return mixed Response reason phrase, false if not set
|
860 |
+
*/
|
861 |
+
function getResponseReason()
|
862 |
+
{
|
863 |
+
return isset($this->_response->_reason) ? $this->_response->_reason : false;
|
864 |
+
}
|
865 |
+
|
866 |
+
/**
|
867 |
+
* Returns either the named header or all if no name given
|
868 |
+
*
|
869 |
+
* @access public
|
870 |
+
* @param string The header name to return, do not set to get all headers
|
871 |
+
* @return mixed either the value of $headername (false if header is not present)
|
872 |
+
* or an array of all headers
|
873 |
+
*/
|
874 |
+
function getResponseHeader($headername = null)
|
875 |
+
{
|
876 |
+
if (!isset($headername)) {
|
877 |
+
return isset($this->_response->_headers)? $this->_response->_headers: array();
|
878 |
+
} else {
|
879 |
+
$headername = strtolower($headername);
|
880 |
+
return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
|
881 |
+
}
|
882 |
+
}
|
883 |
+
|
884 |
+
/**
|
885 |
+
* Returns the body of the response
|
886 |
+
*
|
887 |
+
* @access public
|
888 |
+
* @return mixed response body, false if not set
|
889 |
+
*/
|
890 |
+
function getResponseBody()
|
891 |
+
{
|
892 |
+
return isset($this->_response->_body) ? $this->_response->_body : false;
|
893 |
+
}
|
894 |
+
|
895 |
+
/**
|
896 |
+
* Returns cookies set in response
|
897 |
+
*
|
898 |
+
* @access public
|
899 |
+
* @return mixed array of response cookies, false if none are present
|
900 |
+
*/
|
901 |
+
function getResponseCookies()
|
902 |
+
{
|
903 |
+
return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
|
904 |
+
}
|
905 |
+
|
906 |
+
/**
|
907 |
+
* Builds the request string
|
908 |
+
*
|
909 |
+
* @access private
|
910 |
+
* @return string The request string
|
911 |
+
*/
|
912 |
+
function _buildRequest()
|
913 |
+
{
|
914 |
+
$separator = ini_get('arg_separator.output');
|
915 |
+
ini_set('arg_separator.output', '&');
|
916 |
+
$querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
|
917 |
+
ini_set('arg_separator.output', $separator);
|
918 |
+
|
919 |
+
$host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
|
920 |
+
$port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
|
921 |
+
$path = $this->_url->path . $querystring;
|
922 |
+
$url = $host . $port . $path;
|
923 |
+
|
924 |
+
if (!strlen($url)) {
|
925 |
+
$url = '/';
|
926 |
+
}
|
927 |
+
|
928 |
+
$request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
|
929 |
+
|
930 |
+
if (in_array($this->_method, $this->_bodyDisallowed) ||
|
931 |
+
(0 == strlen($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method ||
|
932 |
+
(empty($this->_postData) && empty($this->_postFiles)))))
|
933 |
+
{
|
934 |
+
$this->removeHeader('Content-Type');
|
935 |
+
} else {
|
936 |
+
if (empty($this->_requestHeaders['content-type'])) {
|
937 |
+
// Add default content-type
|
938 |
+
$this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
|
939 |
+
} elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
|
940 |
+
$boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
|
941 |
+
$this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
|
942 |
+
}
|
943 |
+
}
|
944 |
+
|
945 |
+
// Request Headers
|
946 |
+
if (!empty($this->_requestHeaders)) {
|
947 |
+
foreach ($this->_requestHeaders as $name => $value) {
|
948 |
+
$canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
|
949 |
+
$request .= $canonicalName . ': ' . $value . "\r\n";
|
950 |
+
}
|
951 |
+
}
|
952 |
+
|
953 |
+
// Method does not allow a body, simply add a final CRLF
|
954 |
+
if (in_array($this->_method, $this->_bodyDisallowed)) {
|
955 |
+
|
956 |
+
$request .= "\r\n";
|
957 |
+
|
958 |
+
// Post data if it's an array
|
959 |
+
} elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
|
960 |
+
(!empty($this->_postData) || !empty($this->_postFiles))) {
|
961 |
+
|
962 |
+
// "normal" POST request
|
963 |
+
if (!isset($boundary)) {
|
964 |
+
$postdata = implode('&', array_map(
|
965 |
+
create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
|
966 |
+
$this->_flattenArray('', $this->_postData)
|
967 |
+
));
|
968 |
+
|
969 |
+
// multipart request, probably with file uploads
|
970 |
+
} else {
|
971 |
+
$postdata = '';
|
972 |
+
if (!empty($this->_postData)) {
|
973 |
+
$flatData = $this->_flattenArray('', $this->_postData);
|
974 |
+
foreach ($flatData as $item) {
|
975 |
+
$postdata .= '--' . $boundary . "\r\n";
|
976 |
+
$postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
|
977 |
+
$postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
|
978 |
+
}
|
979 |
+
}
|
980 |
+
foreach ($this->_postFiles as $name => $value) {
|
981 |
+
if (is_array($value['name'])) {
|
982 |
+
$varname = $name . ($this->_useBrackets? '[]': '');
|
983 |
+
} else {
|
984 |
+
$varname = $name;
|
985 |
+
$value['name'] = array($value['name']);
|
986 |
+
}
|
987 |
+
foreach ($value['name'] as $key => $filename) {
|
988 |
+
$fp = fopen($filename, 'r');
|
989 |
+
$basename = basename($filename);
|
990 |
+
$type = is_array($value['type'])? @$value['type'][$key]: $value['type'];
|
991 |
+
|
992 |
+
$postdata .= '--' . $boundary . "\r\n";
|
993 |
+
$postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
|
994 |
+
$postdata .= "\r\nContent-Type: " . $type;
|
995 |
+
$postdata .= "\r\n\r\n" . fread($fp, filesize($filename)) . "\r\n";
|
996 |
+
fclose($fp);
|
997 |
+
}
|
998 |
+
}
|
999 |
+
$postdata .= '--' . $boundary . "--\r\n";
|
1000 |
+
}
|
1001 |
+
$request .= 'Content-Length: ' .
|
1002 |
+
(HTTP_REQUEST_MBSTRING? mb_strlen($postdata, 'iso-8859-1'): strlen($postdata)) .
|
1003 |
+
"\r\n\r\n";
|
1004 |
+
$request .= $postdata;
|
1005 |
+
|
1006 |
+
// Explicitly set request body
|
1007 |
+
} elseif (0 < strlen($this->_body)) {
|
1008 |
+
|
1009 |
+
$request .= 'Content-Length: ' .
|
1010 |
+
(HTTP_REQUEST_MBSTRING? mb_strlen($this->_body, 'iso-8859-1'): strlen($this->_body)) .
|
1011 |
+
"\r\n\r\n";
|
1012 |
+
$request .= $this->_body;
|
1013 |
+
|
1014 |
+
// No body: send a Content-Length header nonetheless (request #12900),
|
1015 |
+
// but do that only for methods that require a body (bug #14740)
|
1016 |
+
} else {
|
1017 |
+
|
1018 |
+
if (in_array($this->_method, $this->_bodyRequired)) {
|
1019 |
+
$request .= "Content-Length: 0\r\n";
|
1020 |
+
}
|
1021 |
+
$request .= "\r\n";
|
1022 |
+
}
|
1023 |
+
|
1024 |
+
return $request;
|
1025 |
+
}
|
1026 |
+
|
1027 |
+
/**
|
1028 |
+
* Helper function to change the (probably multidimensional) associative array
|
1029 |
+
* into the simple one.
|
1030 |
+
*
|
1031 |
+
* @param string name for item
|
1032 |
+
* @param mixed item's values
|
1033 |
+
* @return array array with the following items: array('item name', 'item value');
|
1034 |
+
* @access private
|
1035 |
+
*/
|
1036 |
+
function _flattenArray($name, $values)
|
1037 |
+
{
|
1038 |
+
if (!is_array($values)) {
|
1039 |
+
return array(array($name, $values));
|
1040 |
+
} else {
|
1041 |
+
$ret = array();
|
1042 |
+
foreach ($values as $k => $v) {
|
1043 |
+
if (empty($name)) {
|
1044 |
+
$newName = $k;
|
1045 |
+
} elseif ($this->_useBrackets) {
|
1046 |
+
$newName = $name . '[' . $k . ']';
|
1047 |
+
} else {
|
1048 |
+
$newName = $name;
|
1049 |
+
}
|
1050 |
+
$ret = array_merge($ret, $this->_flattenArray($newName, $v));
|
1051 |
+
}
|
1052 |
+
return $ret;
|
1053 |
+
}
|
1054 |
+
}
|
1055 |
+
|
1056 |
+
|
1057 |
+
/**
|
1058 |
+
* Adds a Listener to the list of listeners that are notified of
|
1059 |
+
* the object's events
|
1060 |
+
*
|
1061 |
+
* Events sent by HTTP_Request object
|
1062 |
+
* - 'connect': on connection to server
|
1063 |
+
* - 'sentRequest': after the request was sent
|
1064 |
+
* - 'disconnect': on disconnection from server
|
1065 |
+
*
|
1066 |
+
* Events sent by HTTP_Response object
|
1067 |
+
* - 'gotHeaders': after receiving response headers (headers are passed in $data)
|
1068 |
+
* - 'tick': on receiving a part of response body (the part is passed in $data)
|
1069 |
+
* - 'gzTick': on receiving a gzip-encoded part of response body (ditto)
|
1070 |
+
* - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
|
1071 |
+
*
|
1072 |
+
* @param HTTP_Request_Listener listener to attach
|
1073 |
+
* @return boolean whether the listener was successfully attached
|
1074 |
+
* @access public
|
1075 |
+
*/
|
1076 |
+
function attach(&$listener)
|
1077 |
+
{
|
1078 |
+
if (!is_a($listener, 'HTTP_Request_Listener')) {
|
1079 |
+
return false;
|
1080 |
+
}
|
1081 |
+
$this->_listeners[$listener->getId()] =& $listener;
|
1082 |
+
return true;
|
1083 |
+
}
|
1084 |
+
|
1085 |
+
|
1086 |
+
/**
|
1087 |
+
* Removes a Listener from the list of listeners
|
1088 |
+
*
|
1089 |
+
* @param HTTP_Request_Listener listener to detach
|
1090 |
+
* @return boolean whether the listener was successfully detached
|
1091 |
+
* @access public
|
1092 |
+
*/
|
1093 |
+
function detach(&$listener)
|
1094 |
+
{
|
1095 |
+
if (!is_a($listener, 'HTTP_Request_Listener') ||
|
1096 |
+
!isset($this->_listeners[$listener->getId()])) {
|
1097 |
+
return false;
|
1098 |
+
}
|
1099 |
+
unset($this->_listeners[$listener->getId()]);
|
1100 |
+
return true;
|
1101 |
+
}
|
1102 |
+
|
1103 |
+
|
1104 |
+
/**
|
1105 |
+
* Notifies all registered listeners of an event.
|
1106 |
+
*
|
1107 |
+
* @param string Event name
|
1108 |
+
* @param mixed Additional data
|
1109 |
+
* @access private
|
1110 |
+
* @see HTTP_Request::attach()
|
1111 |
+
*/
|
1112 |
+
function _notify($event, $data = null)
|
1113 |
+
{
|
1114 |
+
foreach (array_keys($this->_listeners) as $id) {
|
1115 |
+
$this->_listeners[$id]->update($this, $event, $data);
|
1116 |
+
}
|
1117 |
+
}
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
|
1121 |
+
/**
|
1122 |
+
* Response class to complement the Request class
|
1123 |
+
*
|
1124 |
+
* @category HTTP
|
1125 |
+
* @package HTTP_Request
|
1126 |
+
* @author Richard Heyes <richard@phpguru.org>
|
1127 |
+
* @author Alexey Borzov <avb@php.net>
|
1128 |
+
* @version Release: 1.4.4
|
1129 |
+
*/
|
1130 |
+
class HTTP_Response
|
1131 |
+
{
|
1132 |
+
/**
|
1133 |
+
* Socket object
|
1134 |
+
* @var Net_Socket
|
1135 |
+
*/
|
1136 |
+
var $_sock;
|
1137 |
+
|
1138 |
+
/**
|
1139 |
+
* Protocol
|
1140 |
+
* @var string
|
1141 |
+
*/
|
1142 |
+
var $_protocol;
|
1143 |
+
|
1144 |
+
/**
|
1145 |
+
* Return code
|
1146 |
+
* @var string
|
1147 |
+
*/
|
1148 |
+
var $_code;
|
1149 |
+
|
1150 |
+
/**
|
1151 |
+
* Response reason phrase
|
1152 |
+
* @var string
|
1153 |
+
*/
|
1154 |
+
var $_reason;
|
1155 |
+
|
1156 |
+
/**
|
1157 |
+
* Response headers
|
1158 |
+
* @var array
|
1159 |
+
*/
|
1160 |
+
var $_headers;
|
1161 |
+
|
1162 |
+
/**
|
1163 |
+
* Cookies set in response
|
1164 |
+
* @var array
|
1165 |
+
*/
|
1166 |
+
var $_cookies;
|
1167 |
+
|
1168 |
+
/**
|
1169 |
+
* Response body
|
1170 |
+
* @var string
|
1171 |
+
*/
|
1172 |
+
var $_body = '';
|
1173 |
+
|
1174 |
+
/**
|
1175 |
+
* Used by _readChunked(): remaining length of the current chunk
|
1176 |
+
* @var string
|
1177 |
+
*/
|
1178 |
+
var $_chunkLength = 0;
|
1179 |
+
|
1180 |
+
/**
|
1181 |
+
* Attached listeners
|
1182 |
+
* @var array
|
1183 |
+
*/
|
1184 |
+
var $_listeners = array();
|
1185 |
+
|
1186 |
+
/**
|
1187 |
+
* Bytes left to read from message-body
|
1188 |
+
* @var null|int
|
1189 |
+
*/
|
1190 |
+
var $_toRead;
|
1191 |
+
|
1192 |
+
/**
|
1193 |
+
* Constructor
|
1194 |
+
*
|
1195 |
+
* @param Net_Socket socket to read the response from
|
1196 |
+
* @param array listeners attached to request
|
1197 |
+
*/
|
1198 |
+
function HTTP_Response(&$sock, &$listeners)
|
1199 |
+
{
|
1200 |
+
$this->_sock =& $sock;
|
1201 |
+
$this->_listeners =& $listeners;
|
1202 |
+
}
|
1203 |
+
|
1204 |
+
|
1205 |
+
/**
|
1206 |
+
* Processes a HTTP response
|
1207 |
+
*
|
1208 |
+
* This extracts response code, headers, cookies and decodes body if it
|
1209 |
+
* was encoded in some way
|
1210 |
+
*
|
1211 |
+
* @access public
|
1212 |
+
* @param bool Whether to store response body in object property, set
|
1213 |
+
* this to false if downloading a LARGE file and using a Listener.
|
1214 |
+
* This is assumed to be true if body is gzip-encoded.
|
1215 |
+
* @param bool Whether the response can actually have a message-body.
|
1216 |
+
* Will be set to false for HEAD requests.
|
1217 |
+
* @throws PEAR_Error
|
1218 |
+
* @return mixed true on success, PEAR_Error in case of malformed response
|
1219 |
+
*/
|
1220 |
+
function process($saveBody = true, $canHaveBody = true)
|
1221 |
+
{
|
1222 |
+
do {
|
1223 |
+
$line = $this->_sock->readLine();
|
1224 |
+
if (!preg_match('!^(HTTP/\d\.\d) (\d{3})(?: (.+))?!', $line, $s)) {
|
1225 |
+
return PEAR::raiseError('Malformed response', HTTP_REQUEST_ERROR_RESPONSE);
|
1226 |
+
} else {
|
1227 |
+
$this->_protocol = $s[1];
|
1228 |
+
$this->_code = intval($s[2]);
|
1229 |
+
$this->_reason = empty($s[3])? null: $s[3];
|
1230 |
+
}
|
1231 |
+
while ('' !== ($header = $this->_sock->readLine())) {
|
1232 |
+
$this->_processHeader($header);
|
1233 |
+
}
|
1234 |
+
} while (100 == $this->_code);
|
1235 |
+
|
1236 |
+
$this->_notify('gotHeaders', $this->_headers);
|
1237 |
+
|
1238 |
+
// RFC 2616, section 4.4:
|
1239 |
+
// 1. Any response message which "MUST NOT" include a message-body ...
|
1240 |
+
// is always terminated by the first empty line after the header fields
|
1241 |
+
// 3. ... If a message is received with both a
|
1242 |
+
// Transfer-Encoding header field and a Content-Length header field,
|
1243 |
+
// the latter MUST be ignored.
|
1244 |
+
$canHaveBody = $canHaveBody && $this->_code >= 200 &&
|
1245 |
+
$this->_code != 204 && $this->_code != 304;
|
1246 |
+
|
1247 |
+
// If response body is present, read it and decode
|
1248 |
+
$chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
|
1249 |
+
$gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
|
1250 |
+
$hasBody = false;
|
1251 |
+
if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) ||
|
1252 |
+
0 != $this->_headers['content-length']))
|
1253 |
+
{
|
1254 |
+
if ($chunked || !isset($this->_headers['content-length'])) {
|
1255 |
+
$this->_toRead = null;
|
1256 |
+
} else {
|
1257 |
+
$this->_toRead = $this->_headers['content-length'];
|
1258 |
+
}
|
1259 |
+
while (!$this->_sock->eof() && (is_null($this->_toRead) || 0 < $this->_toRead)) {
|
1260 |
+
if ($chunked) {
|
1261 |
+
$data = $this->_readChunked();
|
1262 |
+
} elseif (is_null($this->_toRead)) {
|
1263 |
+
$data = $this->_sock->read(4096);
|
1264 |
+
} else {
|
1265 |
+
$data = $this->_sock->read(min(4096, $this->_toRead));
|
1266 |
+
$this->_toRead -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
|
1267 |
+
}
|
1268 |
+
if ('' == $data && (!$this->_chunkLength || $this->_sock->eof())) {
|
1269 |
+
break;
|
1270 |
+
} else {
|
1271 |
+
$hasBody = true;
|
1272 |
+
if ($saveBody || $gzipped) {
|
1273 |
+
$this->_body .= $data;
|
1274 |
+
}
|
1275 |
+
$this->_notify($gzipped? 'gzTick': 'tick', $data);
|
1276 |
+
}
|
1277 |
+
}
|
1278 |
+
}
|
1279 |
+
|
1280 |
+
if ($hasBody) {
|
1281 |
+
// Uncompress the body if needed
|
1282 |
+
if ($gzipped) {
|
1283 |
+
$body = $this->_decodeGzip($this->_body);
|
1284 |
+
if (PEAR::isError($body)) {
|
1285 |
+
return $body;
|
1286 |
+
}
|
1287 |
+
$this->_body = $body;
|
1288 |
+
$this->_notify('gotBody', $this->_body);
|
1289 |
+
} else {
|
1290 |
+
$this->_notify('gotBody');
|
1291 |
+
}
|
1292 |
+
}
|
1293 |
+
return true;
|
1294 |
+
}
|
1295 |
+
|
1296 |
+
|
1297 |
+
/**
|
1298 |
+
* Processes the response header
|
1299 |
+
*
|
1300 |
+
* @access private
|
1301 |
+
* @param string HTTP header
|
1302 |
+
*/
|
1303 |
+
function _processHeader($header)
|
1304 |
+
{
|
1305 |
+
if (false === strpos($header, ':')) {
|
1306 |
+
return;
|
1307 |
+
}
|
1308 |
+
list($headername, $headervalue) = explode(':', $header, 2);
|
1309 |
+
$headername = strtolower($headername);
|
1310 |
+
$headervalue = ltrim($headervalue);
|
1311 |
+
|
1312 |
+
if ('set-cookie' != $headername) {
|
1313 |
+
if (isset($this->_headers[$headername])) {
|
1314 |
+
$this->_headers[$headername] .= ',' . $headervalue;
|
1315 |
+
} else {
|
1316 |
+
$this->_headers[$headername] = $headervalue;
|
1317 |
+
}
|
1318 |
+
} else {
|
1319 |
+
$this->_parseCookie($headervalue);
|
1320 |
+
}
|
1321 |
+
}
|
1322 |
+
|
1323 |
+
|
1324 |
+
/**
|
1325 |
+
* Parse a Set-Cookie header to fill $_cookies array
|
1326 |
+
*
|
1327 |
+
* @access private
|
1328 |
+
* @param string value of Set-Cookie header
|
1329 |
+
*/
|
1330 |
+
function _parseCookie($headervalue)
|
1331 |
+
{
|
1332 |
+
$cookie = array(
|
1333 |
+
'expires' => null,
|
1334 |
+
'domain' => null,
|
1335 |
+
'path' => null,
|
1336 |
+
'secure' => false
|
1337 |
+
);
|
1338 |
+
|
1339 |
+
// Only a name=value pair
|
1340 |
+
if (!strpos($headervalue, ';')) {
|
1341 |
+
$pos = strpos($headervalue, '=');
|
1342 |
+
$cookie['name'] = trim(substr($headervalue, 0, $pos));
|
1343 |
+
$cookie['value'] = trim(substr($headervalue, $pos + 1));
|
1344 |
+
|
1345 |
+
// Some optional parameters are supplied
|
1346 |
+
} else {
|
1347 |
+
$elements = explode(';', $headervalue);
|
1348 |
+
$pos = strpos($elements[0], '=');
|
1349 |
+
$cookie['name'] = trim(substr($elements[0], 0, $pos));
|
1350 |
+
$cookie['value'] = trim(substr($elements[0], $pos + 1));
|
1351 |
+
|
1352 |
+
for ($i = 1; $i < count($elements); $i++) {
|
1353 |
+
if (false === strpos($elements[$i], '=')) {
|
1354 |
+
$elName = trim($elements[$i]);
|
1355 |
+
$elValue = null;
|
1356 |
+
} else {
|
1357 |
+
list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
|
1358 |
+
}
|
1359 |
+
$elName = strtolower($elName);
|
1360 |
+
if ('secure' == $elName) {
|
1361 |
+
$cookie['secure'] = true;
|
1362 |
+
} elseif ('expires' == $elName) {
|
1363 |
+
$cookie['expires'] = str_replace('"', '', $elValue);
|
1364 |
+
} elseif ('path' == $elName || 'domain' == $elName) {
|
1365 |
+
$cookie[$elName] = urldecode($elValue);
|
1366 |
+
} else {
|
1367 |
+
$cookie[$elName] = $elValue;
|
1368 |
+
}
|
1369 |
+
}
|
1370 |
+
}
|
1371 |
+
$this->_cookies[] = $cookie;
|
1372 |
+
}
|
1373 |
+
|
1374 |
+
|
1375 |
+
/**
|
1376 |
+
* Read a part of response body encoded with chunked Transfer-Encoding
|
1377 |
+
*
|
1378 |
+
* @access private
|
1379 |
+
* @return string
|
1380 |
+
*/
|
1381 |
+
function _readChunked()
|
1382 |
+
{
|
1383 |
+
// at start of the next chunk?
|
1384 |
+
if (0 == $this->_chunkLength) {
|
1385 |
+
$line = $this->_sock->readLine();
|
1386 |
+
if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
|
1387 |
+
$this->_chunkLength = hexdec($matches[1]);
|
1388 |
+
// Chunk with zero length indicates the end
|
1389 |
+
if (0 == $this->_chunkLength) {
|
1390 |
+
$this->_sock->readLine(); // make this an eof()
|
1391 |
+
return '';
|
1392 |
+
}
|
1393 |
+
} else {
|
1394 |
+
return '';
|
1395 |
+
}
|
1396 |
+
}
|
1397 |
+
$data = $this->_sock->read($this->_chunkLength);
|
1398 |
+
$this->_chunkLength -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
|
1399 |
+
if (0 == $this->_chunkLength) {
|
1400 |
+
$this->_sock->readLine(); // Trailing CRLF
|
1401 |
+
}
|
1402 |
+
return $data;
|
1403 |
+
}
|
1404 |
+
|
1405 |
+
|
1406 |
+
/**
|
1407 |
+
* Notifies all registered listeners of an event.
|
1408 |
+
*
|
1409 |
+
* @param string Event name
|
1410 |
+
* @param mixed Additional data
|
1411 |
+
* @access private
|
1412 |
+
* @see HTTP_Request::_notify()
|
1413 |
+
*/
|
1414 |
+
function _notify($event, $data = null)
|
1415 |
+
{
|
1416 |
+
foreach (array_keys($this->_listeners) as $id) {
|
1417 |
+
$this->_listeners[$id]->update($this, $event, $data);
|
1418 |
+
}
|
1419 |
+
}
|
1420 |
+
|
1421 |
+
|
1422 |
+
/**
|
1423 |
+
* Decodes the message-body encoded by gzip
|
1424 |
+
*
|
1425 |
+
* The real decoding work is done by gzinflate() built-in function, this
|
1426 |
+
* method only parses the header and checks data for compliance with
|
1427 |
+
* RFC 1952
|
1428 |
+
*
|
1429 |
+
* @access private
|
1430 |
+
* @param string gzip-encoded data
|
1431 |
+
* @return string decoded data
|
1432 |
+
*/
|
1433 |
+
function _decodeGzip($data)
|
1434 |
+
{
|
1435 |
+
if (HTTP_REQUEST_MBSTRING) {
|
1436 |
+
$oldEncoding = mb_internal_encoding();
|
1437 |
+
mb_internal_encoding('iso-8859-1');
|
1438 |
+
}
|
1439 |
+
$length = strlen($data);
|
1440 |
+
// If it doesn't look like gzip-encoded data, don't bother
|
1441 |
+
if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
|
1442 |
+
return $data;
|
1443 |
+
}
|
1444 |
+
$method = ord(substr($data, 2, 1));
|
1445 |
+
if (8 != $method) {
|
1446 |
+
return PEAR::raiseError('_decodeGzip(): unknown compression method', HTTP_REQUEST_ERROR_GZIP_METHOD);
|
1447 |
+
}
|
1448 |
+
$flags = ord(substr($data, 3, 1));
|
1449 |
+
if ($flags & 224) {
|
1450 |
+
return PEAR::raiseError('_decodeGzip(): reserved bits are set', HTTP_REQUEST_ERROR_GZIP_DATA);
|
1451 |
+
}
|
1452 |
+
|
1453 |
+
// header is 10 bytes minimum. may be longer, though.
|
1454 |
+
$headerLength = 10;
|
1455 |
+
// extra fields, need to skip 'em
|
1456 |
+
if ($flags & 4) {
|
1457 |
+
if ($length - $headerLength - 2 < 8) {
|
1458 |
+
return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
|
1459 |
+
}
|
1460 |
+
$extraLength = unpack('v', substr($data, 10, 2));
|
1461 |
+
if ($length - $headerLength - 2 - $extraLength[1] < 8) {
|
1462 |
+
return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
|
1463 |
+
}
|
1464 |
+
$headerLength += $extraLength[1] + 2;
|
1465 |
+
}
|
1466 |
+
// file name, need to skip that
|
1467 |
+
if ($flags & 8) {
|
1468 |
+
if ($length - $headerLength - 1 < 8) {
|
1469 |
+
return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
|
1470 |
+
}
|
1471 |
+
$filenameLength = strpos(substr($data, $headerLength), chr(0));
|
1472 |
+
if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
|
1473 |
+
return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
|
1474 |
+
}
|
1475 |
+
$headerLength += $filenameLength + 1;
|
1476 |
+
}
|
1477 |
+
// comment, need to skip that also
|
1478 |
+
if ($flags & 16) {
|
1479 |
+
if ($length - $headerLength - 1 < 8) {
|
1480 |
+
return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
|
1481 |
+
}
|
1482 |
+
$commentLength = strpos(substr($data, $headerLength), chr(0));
|
1483 |
+
if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
|
1484 |
+
return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
|
1485 |
+
}
|
1486 |
+
$headerLength += $commentLength + 1;
|
1487 |
+
}
|
1488 |
+
// have a CRC for header. let's check
|
1489 |
+
if ($flags & 1) {
|
1490 |
+
if ($length - $headerLength - 2 < 8) {
|
1491 |
+
return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
|
1492 |
+
}
|
1493 |
+
$crcReal = 0xffff & crc32(substr($data, 0, $headerLength));
|
1494 |
+
$crcStored = unpack('v', substr($data, $headerLength, 2));
|
1495 |
+
if ($crcReal != $crcStored[1]) {
|
1496 |
+
return PEAR::raiseError('_decodeGzip(): header CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
|
1497 |
+
}
|
1498 |
+
$headerLength += 2;
|
1499 |
+
}
|
1500 |
+
// unpacked data CRC and size at the end of encoded data
|
1501 |
+
$tmp = unpack('V2', substr($data, -8));
|
1502 |
+
$dataCrc = $tmp[1];
|
1503 |
+
$dataSize = $tmp[2];
|
1504 |
+
|
1505 |
+
// finally, call the gzinflate() function
|
1506 |
+
// don't pass $dataSize to gzinflate, see bugs #13135, #14370
|
1507 |
+
$unpacked = gzinflate(substr($data, $headerLength, -8));
|
1508 |
+
if (false === $unpacked) {
|
1509 |
+
return PEAR::raiseError('_decodeGzip(): gzinflate() call failed', HTTP_REQUEST_ERROR_GZIP_READ);
|
1510 |
+
} elseif ($dataSize != strlen($unpacked)) {
|
1511 |
+
return PEAR::raiseError('_decodeGzip(): data size check failed', HTTP_REQUEST_ERROR_GZIP_READ);
|
1512 |
+
} elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
|
1513 |
+
return PEAR::raiseError('_decodeGzip(): data CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
|
1514 |
+
}
|
1515 |
+
if (HTTP_REQUEST_MBSTRING) {
|
1516 |
+
mb_internal_encoding($oldEncoding);
|
1517 |
+
}
|
1518 |
+
return $unpacked;
|
1519 |
+
}
|
1520 |
+
} // End class HTTP_Response
|
1521 |
+
?>
|
lib/PEAR/HTTP/Request/Listener.php
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Listener for HTTP_Request and HTTP_Response objects
|
4 |
+
*
|
5 |
+
* PHP versions 4 and 5
|
6 |
+
*
|
7 |
+
* LICENSE:
|
8 |
+
*
|
9 |
+
* Copyright (c) 2002-2007, Richard Heyes
|
10 |
+
* All rights reserved.
|
11 |
+
*
|
12 |
+
* Redistribution and use in source and binary forms, with or without
|
13 |
+
* modification, are permitted provided that the following conditions
|
14 |
+
* are met:
|
15 |
+
*
|
16 |
+
* o Redistributions of source code must retain the above copyright
|
17 |
+
* notice, this list of conditions and the following disclaimer.
|
18 |
+
* o Redistributions in binary form must reproduce the above copyright
|
19 |
+
* notice, this list of conditions and the following disclaimer in the
|
20 |
+
* documentation and/or other materials provided with the distribution.
|
21 |
+
* o The names of the authors may not be used to endorse or promote
|
22 |
+
* products derived from this software without specific prior written
|
23 |
+
* permission.
|
24 |
+
*
|
25 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
26 |
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
27 |
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
28 |
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
29 |
+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
30 |
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
31 |
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
32 |
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
33 |
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
34 |
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
35 |
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
36 |
+
*
|
37 |
+
* @category HTTP
|
38 |
+
* @package HTTP_Request
|
39 |
+
* @author Alexey Borzov <avb@php.net>
|
40 |
+
* @copyright 2002-2007 Richard Heyes
|
41 |
+
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
42 |
+
* @version CVS: $Id: Listener.php,v 1.3 2007/05/18 10:33:31 avb Exp $
|
43 |
+
* @link http://pear.php.net/package/HTTP_Request/
|
44 |
+
*/
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Listener for HTTP_Request and HTTP_Response objects
|
48 |
+
*
|
49 |
+
* This class implements the Observer part of a Subject-Observer
|
50 |
+
* design pattern.
|
51 |
+
*
|
52 |
+
* @category HTTP
|
53 |
+
* @package HTTP_Request
|
54 |
+
* @author Alexey Borzov <avb@php.net>
|
55 |
+
* @version Release: 1.4.4
|
56 |
+
*/
|
57 |
+
class HTTP_Request_Listener
|
58 |
+
{
|
59 |
+
/**
|
60 |
+
* A listener's identifier
|
61 |
+
* @var string
|
62 |
+
*/
|
63 |
+
var $_id;
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Constructor, sets the object's identifier
|
67 |
+
*
|
68 |
+
* @access public
|
69 |
+
*/
|
70 |
+
function HTTP_Request_Listener()
|
71 |
+
{
|
72 |
+
$this->_id = md5(uniqid('http_request_', 1));
|
73 |
+
}
|
74 |
+
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Returns the listener's identifier
|
78 |
+
*
|
79 |
+
* @access public
|
80 |
+
* @return string
|
81 |
+
*/
|
82 |
+
function getId()
|
83 |
+
{
|
84 |
+
return $this->_id;
|
85 |
+
}
|
86 |
+
|
87 |
+
|
88 |
+
/**
|
89 |
+
* This method is called when Listener is notified of an event
|
90 |
+
*
|
91 |
+
* @access public
|
92 |
+
* @param object an object the listener is attached to
|
93 |
+
* @param string Event name
|
94 |
+
* @param mixed Additional data
|
95 |
+
* @abstract
|
96 |
+
*/
|
97 |
+
function update(&$subject, $event, $data = null)
|
98 |
+
{
|
99 |
+
echo "Notified of event: '$event'\n";
|
100 |
+
if (null !== $data) {
|
101 |
+
echo "Additional data: ";
|
102 |
+
var_dump($data);
|
103 |
+
}
|
104 |
+
}
|
105 |
+
}
|
106 |
+
?>
|
lib/PEAR/Mail/mime.php
ADDED
@@ -0,0 +1,1095 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The Mail_Mime class is used to create MIME E-mail messages
|
4 |
+
*
|
5 |
+
* The Mail_Mime class provides an OO interface to create MIME
|
6 |
+
* enabled email messages. This way you can create emails that
|
7 |
+
* contain plain-text bodies, HTML bodies, attachments, inline
|
8 |
+
* images and specific headers.
|
9 |
+
*
|
10 |
+
* Compatible with PHP versions 4 and 5
|
11 |
+
*
|
12 |
+
* LICENSE: This LICENSE is in the BSD license style.
|
13 |
+
* Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
|
14 |
+
* Copyright (c) 2003-2006, PEAR <pear-group@php.net>
|
15 |
+
* All rights reserved.
|
16 |
+
*
|
17 |
+
* Redistribution and use in source and binary forms, with or
|
18 |
+
* without modification, are permitted provided that the following
|
19 |
+
* conditions are met:
|
20 |
+
*
|
21 |
+
* - Redistributions of source code must retain the above copyright
|
22 |
+
* notice, this list of conditions and the following disclaimer.
|
23 |
+
* - Redistributions in binary form must reproduce the above copyright
|
24 |
+
* notice, this list of conditions and the following disclaimer in the
|
25 |
+
* documentation and/or other materials provided with the distribution.
|
26 |
+
* - Neither the name of the authors, nor the names of its contributors
|
27 |
+
* may be used to endorse or promote products derived from this
|
28 |
+
* software without specific prior written permission.
|
29 |
+
*
|
30 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
31 |
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
32 |
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
33 |
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
34 |
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
35 |
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
36 |
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
37 |
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
38 |
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
39 |
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
40 |
+
* THE POSSIBILITY OF SUCH DAMAGE.
|
41 |
+
*
|
42 |
+
* @category Mail
|
43 |
+
* @package Mail_Mime
|
44 |
+
* @author Richard Heyes <richard@phpguru.org>
|
45 |
+
* @author Tomas V.V. Cox <cox@idecnet.com>
|
46 |
+
* @author Cipriano Groenendal <cipri@php.net>
|
47 |
+
* @author Sean Coates <sean@php.net>
|
48 |
+
* @copyright 2003-2006 PEAR <pear-group@php.net>
|
49 |
+
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
50 |
+
* @version CVS: $Id: mime.php,v 1.81 2007/06/21 19:08:28 cipri Exp $
|
51 |
+
* @link http://pear.php.net/package/Mail_mime
|
52 |
+
*
|
53 |
+
* This class is based on HTML Mime Mail class from
|
54 |
+
* Richard Heyes <richard@phpguru.org> which was based also
|
55 |
+
* in the mime_mail.class by Tobias Ratschiller <tobias@dnet.it>
|
56 |
+
* and Sascha Schumann <sascha@schumann.cx>
|
57 |
+
*/
|
58 |
+
|
59 |
+
|
60 |
+
/**
|
61 |
+
* require PEAR
|
62 |
+
*
|
63 |
+
* This package depends on PEAR to raise errors.
|
64 |
+
*/
|
65 |
+
require_once 'PEAR.php';
|
66 |
+
|
67 |
+
/**
|
68 |
+
* require Mail_mimePart
|
69 |
+
*
|
70 |
+
* Mail_mimePart contains the code required to
|
71 |
+
* create all the different parts a mail can
|
72 |
+
* consist of.
|
73 |
+
*/
|
74 |
+
require_once 'Mail/mimePart.php';
|
75 |
+
|
76 |
+
|
77 |
+
/**
|
78 |
+
* The Mail_Mime class provides an OO interface to create MIME
|
79 |
+
* enabled email messages. This way you can create emails that
|
80 |
+
* contain plain-text bodies, HTML bodies, attachments, inline
|
81 |
+
* images and specific headers.
|
82 |
+
*
|
83 |
+
* @category Mail
|
84 |
+
* @package Mail_Mime
|
85 |
+
* @author Richard Heyes <richard@phpguru.org>
|
86 |
+
* @author Tomas V.V. Cox <cox@idecnet.com>
|
87 |
+
* @author Cipriano Groenendal <cipri@php.net>
|
88 |
+
* @author Sean Coates <sean@php.net>
|
89 |
+
* @copyright 2003-2006 PEAR <pear-group@php.net>
|
90 |
+
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
91 |
+
* @version Release: @package_version@
|
92 |
+
* @link http://pear.php.net/package/Mail_mime
|
93 |
+
*/
|
94 |
+
class Mail_mime
|
95 |
+
{
|
96 |
+
/**
|
97 |
+
* Contains the plain text part of the email
|
98 |
+
*
|
99 |
+
* @var string
|
100 |
+
* @access private
|
101 |
+
*/
|
102 |
+
var $_txtbody;
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Contains the html part of the email
|
106 |
+
*
|
107 |
+
* @var string
|
108 |
+
* @access private
|
109 |
+
*/
|
110 |
+
var $_htmlbody;
|
111 |
+
|
112 |
+
/**
|
113 |
+
* contains the mime encoded text
|
114 |
+
*
|
115 |
+
* @var string
|
116 |
+
* @access private
|
117 |
+
*/
|
118 |
+
var $_mime;
|
119 |
+
|
120 |
+
/**
|
121 |
+
* contains the multipart content
|
122 |
+
*
|
123 |
+
* @var string
|
124 |
+
* @access private
|
125 |
+
*/
|
126 |
+
var $_multipart;
|
127 |
+
|
128 |
+
/**
|
129 |
+
* list of the attached images
|
130 |
+
*
|
131 |
+
* @var array
|
132 |
+
* @access private
|
133 |
+
*/
|
134 |
+
var $_html_images = array();
|
135 |
+
|
136 |
+
/**
|
137 |
+
* list of the attachements
|
138 |
+
*
|
139 |
+
* @var array
|
140 |
+
* @access private
|
141 |
+
*/
|
142 |
+
var $_parts = array();
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Build parameters
|
146 |
+
*
|
147 |
+
* @var array
|
148 |
+
* @access private
|
149 |
+
*/
|
150 |
+
var $_build_params = array();
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Headers for the mail
|
154 |
+
*
|
155 |
+
* @var array
|
156 |
+
* @access private
|
157 |
+
*/
|
158 |
+
var $_headers = array();
|
159 |
+
|
160 |
+
/**
|
161 |
+
* End Of Line sequence (for serialize)
|
162 |
+
*
|
163 |
+
* @var string
|
164 |
+
* @access private
|
165 |
+
*/
|
166 |
+
var $_eol;
|
167 |
+
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Constructor function.
|
171 |
+
*
|
172 |
+
* @param string $crlf what type of linebreak to use.
|
173 |
+
* Defaults to "\r\n"
|
174 |
+
*
|
175 |
+
* @return void
|
176 |
+
*
|
177 |
+
* @access public
|
178 |
+
*/
|
179 |
+
function Mail_mime($crlf = "\r\n")
|
180 |
+
{
|
181 |
+
$this->_setEOL($crlf);
|
182 |
+
$this->_build_params = array(
|
183 |
+
'head_encoding' => 'quoted-printable',
|
184 |
+
'text_encoding' => '7bit',
|
185 |
+
'html_encoding' => 'quoted-printable',
|
186 |
+
'7bit_wrap' => 998,
|
187 |
+
'html_charset' => 'ISO-8859-1',
|
188 |
+
'text_charset' => 'ISO-8859-1',
|
189 |
+
'head_charset' => 'ISO-8859-1'
|
190 |
+
);
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* wakeup function called by unserialize. It re-sets the EOL constant
|
195 |
+
*
|
196 |
+
* @access private
|
197 |
+
* @return void
|
198 |
+
*/
|
199 |
+
function __wakeup()
|
200 |
+
{
|
201 |
+
$this->_setEOL($this->_eol);
|
202 |
+
}
|
203 |
+
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Accessor function to set the body text. Body text is used if
|
207 |
+
* it's not an html mail being sent or else is used to fill the
|
208 |
+
* text/plain part that emails clients who don't support
|
209 |
+
* html should show.
|
210 |
+
*
|
211 |
+
* @param string $data Either a string or
|
212 |
+
* the file name with the contents
|
213 |
+
* @param bool $isfile If true the first param should be treated
|
214 |
+
* as a file name, else as a string (default)
|
215 |
+
* @param bool $append If true the text or file is appended to
|
216 |
+
* the existing body, else the old body is
|
217 |
+
* overwritten
|
218 |
+
*
|
219 |
+
* @return mixed true on success or PEAR_Error object
|
220 |
+
* @access public
|
221 |
+
*/
|
222 |
+
function setTXTBody($data, $isfile = false, $append = false)
|
223 |
+
{
|
224 |
+
if (!$isfile) {
|
225 |
+
if (!$append) {
|
226 |
+
$this->_txtbody = $data;
|
227 |
+
} else {
|
228 |
+
$this->_txtbody .= $data;
|
229 |
+
}
|
230 |
+
} else {
|
231 |
+
$cont = $this->_file2str($data);
|
232 |
+
if (PEAR::isError($cont)) {
|
233 |
+
return $cont;
|
234 |
+
}
|
235 |
+
if (!$append) {
|
236 |
+
$this->_txtbody = $cont;
|
237 |
+
} else {
|
238 |
+
$this->_txtbody .= $cont;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
return true;
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Adds a html part to the mail.
|
246 |
+
*
|
247 |
+
* @param string $data either a string or the file name with the
|
248 |
+
* contents
|
249 |
+
* @param bool $isfile a flag that determines whether $data is a
|
250 |
+
* filename, or a string(false, default)
|
251 |
+
*
|
252 |
+
* @return bool true on success
|
253 |
+
* @access public
|
254 |
+
*/
|
255 |
+
function setHTMLBody($data, $isfile = false)
|
256 |
+
{
|
257 |
+
if (!$isfile) {
|
258 |
+
$this->_htmlbody = $data;
|
259 |
+
} else {
|
260 |
+
$cont = $this->_file2str($data);
|
261 |
+
if (PEAR::isError($cont)) {
|
262 |
+
return $cont;
|
263 |
+
}
|
264 |
+
$this->_htmlbody = $cont;
|
265 |
+
}
|
266 |
+
|
267 |
+
return true;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Adds an image to the list of embedded images.
|
272 |
+
*
|
273 |
+
* @param string $file the image file name OR image data itself
|
274 |
+
* @param string $c_type the content type
|
275 |
+
* @param string $name the filename of the image.
|
276 |
+
* Only used if $file is the image data.
|
277 |
+
* @param bool $isfile whether $file is a filename or not.
|
278 |
+
* Defaults to true
|
279 |
+
*
|
280 |
+
* @return bool true on success
|
281 |
+
* @access public
|
282 |
+
*/
|
283 |
+
function addHTMLImage($file, $c_type='application/octet-stream',
|
284 |
+
$name = '', $isfile = true)
|
285 |
+
{
|
286 |
+
$filedata = ($isfile === true) ? $this->_file2str($file)
|
287 |
+
: $file;
|
288 |
+
if ($isfile === true) {
|
289 |
+
$filename = ($name == '' ? $file : $name);
|
290 |
+
} else {
|
291 |
+
$filename = $name;
|
292 |
+
}
|
293 |
+
if (PEAR::isError($filedata)) {
|
294 |
+
return $filedata;
|
295 |
+
}
|
296 |
+
$this->_html_images[] = array(
|
297 |
+
'body' => $filedata,
|
298 |
+
'name' => $filename,
|
299 |
+
'c_type' => $c_type,
|
300 |
+
'cid' => md5(uniqid(time()))
|
301 |
+
);
|
302 |
+
return true;
|
303 |
+
}
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Adds a file to the list of attachments.
|
307 |
+
*
|
308 |
+
* @param string $file The file name of the file to attach
|
309 |
+
* OR the file contents itself
|
310 |
+
* @param string $c_type The content type
|
311 |
+
* @param string $name The filename of the attachment
|
312 |
+
* Only use if $file is the contents
|
313 |
+
* @param bool $isfile Whether $file is a filename or not
|
314 |
+
* Defaults to true
|
315 |
+
* @param string $encoding The type of encoding to use.
|
316 |
+
* Defaults to base64.
|
317 |
+
* Possible values: 7bit, 8bit, base64,
|
318 |
+
* or quoted-printable.
|
319 |
+
* @param string $disposition The content-disposition of this file
|
320 |
+
* Defaults to attachment.
|
321 |
+
* Possible values: attachment, inline.
|
322 |
+
* @param string $charset The character set used in the filename
|
323 |
+
* of this attachment.
|
324 |
+
* @param string $language The language of the attachment
|
325 |
+
* @param string $location The RFC 2557.4 location of the attachment
|
326 |
+
*
|
327 |
+
* @return mixed true on success or PEAR_Error object
|
328 |
+
* @access public
|
329 |
+
*/
|
330 |
+
function addAttachment($file,
|
331 |
+
$c_type = 'application/octet-stream',
|
332 |
+
$name = '',
|
333 |
+
$isfile = true,
|
334 |
+
$encoding = 'base64',
|
335 |
+
$disposition = 'attachment',
|
336 |
+
$charset = '',
|
337 |
+
$language = '',
|
338 |
+
$location = '')
|
339 |
+
{
|
340 |
+
$filedata = ($isfile === true) ? $this->_file2str($file)
|
341 |
+
: $file;
|
342 |
+
if ($isfile === true) {
|
343 |
+
// Force the name the user supplied, otherwise use $file
|
344 |
+
$filename = (strlen($name)) ? $name : $file;
|
345 |
+
} else {
|
346 |
+
$filename = $name;
|
347 |
+
}
|
348 |
+
if (!strlen($filename)) {
|
349 |
+
$msg = "The supplied filename for the attachment can't be empty";
|
350 |
+
$err = PEAR::raiseError($msg);
|
351 |
+
return $err;
|
352 |
+
}
|
353 |
+
$filename = basename($filename);
|
354 |
+
if (PEAR::isError($filedata)) {
|
355 |
+
return $filedata;
|
356 |
+
}
|
357 |
+
|
358 |
+
$this->_parts[] = array(
|
359 |
+
'body' => $filedata,
|
360 |
+
'name' => $filename,
|
361 |
+
'c_type' => $c_type,
|
362 |
+
'encoding' => $encoding,
|
363 |
+
'charset' => $charset,
|
364 |
+
'language' => $language,
|
365 |
+
'location' => $location,
|
366 |
+
'disposition' => $disposition
|
367 |
+
);
|
368 |
+
return true;
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Get the contents of the given file name as string
|
373 |
+
*
|
374 |
+
* @param string $file_name path of file to process
|
375 |
+
*
|
376 |
+
* @return string contents of $file_name
|
377 |
+
* @access private
|
378 |
+
*/
|
379 |
+
function &_file2str($file_name)
|
380 |
+
{
|
381 |
+
if (!is_readable($file_name)) {
|
382 |
+
$err = PEAR::raiseError('File is not readable ' . $file_name);
|
383 |
+
return $err;
|
384 |
+
}
|
385 |
+
if (!$fd = fopen($file_name, 'rb')) {
|
386 |
+
$err = PEAR::raiseError('Could not open ' . $file_name);
|
387 |
+
return $err;
|
388 |
+
}
|
389 |
+
$filesize = filesize($file_name);
|
390 |
+
if ($filesize == 0) {
|
391 |
+
$cont = "";
|
392 |
+
} else {
|
393 |
+
if ($magic_quote_setting = get_magic_quotes_runtime()) {
|
394 |
+
set_magic_quotes_runtime(0);
|
395 |
+
}
|
396 |
+
$cont = fread($fd, $filesize);
|
397 |
+
if ($magic_quote_setting) {
|
398 |
+
set_magic_quotes_runtime($magic_quote_setting);
|
399 |
+
}
|
400 |
+
}
|
401 |
+
fclose($fd);
|
402 |
+
return $cont;
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Adds a text subpart to the mimePart object and
|
407 |
+
* returns it during the build process.
|
408 |
+
*
|
409 |
+
* @param mixed &$obj The object to add the part to, or
|
410 |
+
* null if a new object is to be created.
|
411 |
+
* @param string $text The text to add.
|
412 |
+
*
|
413 |
+
* @return object The text mimePart object
|
414 |
+
* @access private
|
415 |
+
*/
|
416 |
+
function &_addTextPart(&$obj, $text)
|
417 |
+
{
|
418 |
+
$params['content_type'] = 'text/plain';
|
419 |
+
$params['encoding'] = $this->_build_params['text_encoding'];
|
420 |
+
$params['charset'] = $this->_build_params['text_charset'];
|
421 |
+
if (is_object($obj)) {
|
422 |
+
$ret = $obj->addSubpart($text, $params);
|
423 |
+
return $ret;
|
424 |
+
} else {
|
425 |
+
$ret = new Mail_mimePart($text, $params);
|
426 |
+
return $ret;
|
427 |
+
}
|
428 |
+
}
|
429 |
+
|
430 |
+
/**
|
431 |
+
* Adds a html subpart to the mimePart object and
|
432 |
+
* returns it during the build process.
|
433 |
+
*
|
434 |
+
* @param mixed &$obj The object to add the part to, or
|
435 |
+
* null if a new object is to be created.
|
436 |
+
*
|
437 |
+
* @return object The html mimePart object
|
438 |
+
* @access private
|
439 |
+
*/
|
440 |
+
function &_addHtmlPart(&$obj)
|
441 |
+
{
|
442 |
+
$params['content_type'] = 'text/html';
|
443 |
+
$params['encoding'] = $this->_build_params['html_encoding'];
|
444 |
+
$params['charset'] = $this->_build_params['html_charset'];
|
445 |
+
if (is_object($obj)) {
|
446 |
+
$ret = $obj->addSubpart($this->_htmlbody, $params);
|
447 |
+
return $ret;
|
448 |
+
} else {
|
449 |
+
$ret = new Mail_mimePart($this->_htmlbody, $params);
|
450 |
+
return $ret;
|
451 |
+
}
|
452 |
+
}
|
453 |
+
|
454 |
+
/**
|
455 |
+
* Creates a new mimePart object, using multipart/mixed as
|
456 |
+
* the initial content-type and returns it during the
|
457 |
+
* build process.
|
458 |
+
*
|
459 |
+
* @return object The multipart/mixed mimePart object
|
460 |
+
* @access private
|
461 |
+
*/
|
462 |
+
function &_addMixedPart()
|
463 |
+
{
|
464 |
+
$params = array();
|
465 |
+
$params['content_type'] = 'multipart/mixed';
|
466 |
+
|
467 |
+
//Create empty multipart/mixed Mail_mimePart object to return
|
468 |
+
$ret = new Mail_mimePart('', $params);
|
469 |
+
return $ret;
|
470 |
+
}
|
471 |
+
|
472 |
+
/**
|
473 |
+
* Adds a multipart/alternative part to a mimePart
|
474 |
+
* object (or creates one), and returns it during
|
475 |
+
* the build process.
|
476 |
+
*
|
477 |
+
* @param mixed &$obj The object to add the part to, or
|
478 |
+
* null if a new object is to be created.
|
479 |
+
*
|
480 |
+
* @return object The multipart/mixed mimePart object
|
481 |
+
* @access private
|
482 |
+
*/
|
483 |
+
function &_addAlternativePart(&$obj)
|
484 |
+
{
|
485 |
+
$params['content_type'] = 'multipart/alternative';
|
486 |
+
if (is_object($obj)) {
|
487 |
+
return $obj->addSubpart('', $params);
|
488 |
+
} else {
|
489 |
+
$ret = new Mail_mimePart('', $params);
|
490 |
+
return $ret;
|
491 |
+
}
|
492 |
+
}
|
493 |
+
|
494 |
+
/**
|
495 |
+
* Adds a multipart/related part to a mimePart
|
496 |
+
* object (or creates one), and returns it during
|
497 |
+
* the build process.
|
498 |
+
*
|
499 |
+
* @param mixed &$obj The object to add the part to, or
|
500 |
+
* null if a new object is to be created
|
501 |
+
*
|
502 |
+
* @return object The multipart/mixed mimePart object
|
503 |
+
* @access private
|
504 |
+
*/
|
505 |
+
function &_addRelatedPart(&$obj)
|
506 |
+
{
|
507 |
+
$params['content_type'] = 'multipart/related';
|
508 |
+
if (is_object($obj)) {
|
509 |
+
return $obj->addSubpart('', $params);
|
510 |
+
} else {
|
511 |
+
$ret = new Mail_mimePart('', $params);
|
512 |
+
return $ret;
|
513 |
+
}
|
514 |
+
}
|
515 |
+
|
516 |
+
/**
|
517 |
+
* Adds an html image subpart to a mimePart object
|
518 |
+
* and returns it during the build process.
|
519 |
+
*
|
520 |
+
* @param object &$obj The mimePart to add the image to
|
521 |
+
* @param array $value The image information
|
522 |
+
*
|
523 |
+
* @return object The image mimePart object
|
524 |
+
* @access private
|
525 |
+
*/
|
526 |
+
function &_addHtmlImagePart(&$obj, $value)
|
527 |
+
{
|
528 |
+
$params['content_type'] = $value['c_type'];
|
529 |
+
$params['encoding'] = 'base64';
|
530 |
+
$params['disposition'] = 'inline';
|
531 |
+
$params['dfilename'] = $value['name'];
|
532 |
+
$params['cid'] = $value['cid'];
|
533 |
+
|
534 |
+
$ret = $obj->addSubpart($value['body'], $params);
|
535 |
+
return $ret;
|
536 |
+
|
537 |
+
}
|
538 |
+
|
539 |
+
/**
|
540 |
+
* Adds an attachment subpart to a mimePart object
|
541 |
+
* and returns it during the build process.
|
542 |
+
*
|
543 |
+
* @param object &$obj The mimePart to add the image to
|
544 |
+
* @param array $value The attachment information
|
545 |
+
*
|
546 |
+
* @return object The image mimePart object
|
547 |
+
* @access private
|
548 |
+
*/
|
549 |
+
function &_addAttachmentPart(&$obj, $value)
|
550 |
+
{
|
551 |
+
$params['dfilename'] = $value['name'];
|
552 |
+
$params['encoding'] = $value['encoding'];
|
553 |
+
if ($value['charset']) {
|
554 |
+
$params['charset'] = $value['charset'];
|
555 |
+
}
|
556 |
+
if ($value['language']) {
|
557 |
+
$params['language'] = $value['language'];
|
558 |
+
}
|
559 |
+
if ($value['location']) {
|
560 |
+
$params['location'] = $value['location'];
|
561 |
+
}
|
562 |
+
$params['content_type'] = $value['c_type'];
|
563 |
+
$params['disposition'] = isset($value['disposition']) ?
|
564 |
+
$value['disposition'] : 'attachment';
|
565 |
+
$ret = $obj->addSubpart($value['body'], $params);
|
566 |
+
return $ret;
|
567 |
+
}
|
568 |
+
|
569 |
+
/**
|
570 |
+
* Returns the complete e-mail, ready to send using an alternative
|
571 |
+
* mail delivery method. Note that only the mailpart that is made
|
572 |
+
* with Mail_Mime is created. This means that,
|
573 |
+
* YOU WILL HAVE NO TO: HEADERS UNLESS YOU SET IT YOURSELF
|
574 |
+
* using the $xtra_headers parameter!
|
575 |
+
*
|
576 |
+
* @param string $separation The separation etween these two parts.
|
577 |
+
* @param array $build_params The Build parameters passed to the
|
578 |
+
* &get() function. See &get for more info.
|
579 |
+
* @param array $xtra_headers The extra headers that should be passed
|
580 |
+
* to the &headers() function.
|
581 |
+
* See that function for more info.
|
582 |
+
* @param bool $overwrite Overwrite the existing headers with new.
|
583 |
+
*
|
584 |
+
* @return string The complete e-mail.
|
585 |
+
* @access public
|
586 |
+
*/
|
587 |
+
function getMessage(
|
588 |
+
$separation = null,
|
589 |
+
$build_params = null,
|
590 |
+
$xtra_headers = null,
|
591 |
+
$overwrite = false
|
592 |
+
)
|
593 |
+
{
|
594 |
+
if ($separation === null) {
|
595 |
+
$separation = MAIL_MIME_CRLF;
|
596 |
+
}
|
597 |
+
$body = $this->get($build_params);
|
598 |
+
$head = $this->txtHeaders($xtra_headers, $overwrite);
|
599 |
+
$mail = $head . $separation . $body;
|
600 |
+
return $mail;
|
601 |
+
}
|
602 |
+
|
603 |
+
|
604 |
+
/**
|
605 |
+
* Builds the multipart message from the list ($this->_parts) and
|
606 |
+
* returns the mime content.
|
607 |
+
*
|
608 |
+
* @param array $build_params Build parameters that change the way the email
|
609 |
+
* is built. Should be associative. Can contain:
|
610 |
+
* head_encoding - What encoding to use for the headers.
|
611 |
+
* Options: quoted-printable or base64
|
612 |
+
* Default is quoted-printable
|
613 |
+
* text_encoding - What encoding to use for plain text
|
614 |
+
* Options: 7bit, 8bit,
|
615 |
+
* base64, or quoted-printable
|
616 |
+
* Default is 7bit
|
617 |
+
* html_encoding - What encoding to use for html
|
618 |
+
* Options: 7bit, 8bit,
|
619 |
+
* base64, or quoted-printable
|
620 |
+
* Default is quoted-printable
|
621 |
+
* 7bit_wrap - Number of characters before text is
|
622 |
+
* wrapped in 7bit encoding
|
623 |
+
* Default is 998
|
624 |
+
* html_charset - The character set to use for html.
|
625 |
+
* Default is iso-8859-1
|
626 |
+
* text_charset - The character set to use for text.
|
627 |
+
* Default is iso-8859-1
|
628 |
+
* head_charset - The character set to use for headers.
|
629 |
+
* Default is iso-8859-1
|
630 |
+
*
|
631 |
+
* @return string The mime content
|
632 |
+
* @access public
|
633 |
+
*/
|
634 |
+
function &get($build_params = null)
|
635 |
+
{
|
636 |
+
if (isset($build_params)) {
|
637 |
+
while (list($key, $value) = each($build_params)) {
|
638 |
+
$this->_build_params[$key] = $value;
|
639 |
+
}
|
640 |
+
}
|
641 |
+
|
642 |
+
if (isset($this->_headers['From'])){
|
643 |
+
$domain = @strstr($this->_headers['From'],'@');
|
644 |
+
//Bug #11381: Illegal characters in domain ID
|
645 |
+
$domain = str_replace(array("<", ">", "&", "(", ")", " ", "\"", "'"), "", $domain);
|
646 |
+
$domain = urlencode($domain);
|
647 |
+
foreach($this->_html_images as $i => $img){
|
648 |
+
$this->_html_images[$i]['cid'] = $this->_html_images[$i]['cid'] . $domain;
|
649 |
+
}
|
650 |
+
}
|
651 |
+
|
652 |
+
if (count($this->_html_images) AND isset($this->_htmlbody)) {
|
653 |
+
foreach ($this->_html_images as $key => $value) {
|
654 |
+
$regex = array();
|
655 |
+
$regex[] = '#(\s)((?i)src|background|href(?-i))\s*=\s*(["\']?)' .
|
656 |
+
preg_quote($value['name'], '#') . '\3#';
|
657 |
+
$regex[] = '#(?i)url(?-i)\(\s*(["\']?)' .
|
658 |
+
preg_quote($value['name'], '#') . '\1\s*\)#';
|
659 |
+
|
660 |
+
$rep = array();
|
661 |
+
$rep[] = '\1\2=\3cid:' . $value['cid'] .'\3';
|
662 |
+
$rep[] = 'url(\1cid:' . $value['cid'] . '\2)';
|
663 |
+
|
664 |
+
$this->_htmlbody = preg_replace($regex, $rep, $this->_htmlbody);
|
665 |
+
$this->_html_images[$key]['name'] =
|
666 |
+
basename($this->_html_images[$key]['name']);
|
667 |
+
}
|
668 |
+
}
|
669 |
+
|
670 |
+
$null = null;
|
671 |
+
$attachments = count($this->_parts) ? true : false;
|
672 |
+
$html_images = count($this->_html_images) ? true : false;
|
673 |
+
$html = strlen($this->_htmlbody) ? true : false;
|
674 |
+
$text = (!$html AND strlen($this->_txtbody)) ? true : false;
|
675 |
+
|
676 |
+
switch (true) {
|
677 |
+
case $text AND !$attachments:
|
678 |
+
$message =& $this->_addTextPart($null, $this->_txtbody);
|
679 |
+
break;
|
680 |
+
|
681 |
+
case !$text AND !$html AND $attachments:
|
682 |
+
$message =& $this->_addMixedPart();
|
683 |
+
for ($i = 0; $i < count($this->_parts); $i++) {
|
684 |
+
$this->_addAttachmentPart($message, $this->_parts[$i]);
|
685 |
+
}
|
686 |
+
break;
|
687 |
+
|
688 |
+
case $text AND $attachments:
|
689 |
+
$message =& $this->_addMixedPart();
|
690 |
+
$this->_addTextPart($message, $this->_txtbody);
|
691 |
+
for ($i = 0; $i < count($this->_parts); $i++) {
|
692 |
+
$this->_addAttachmentPart($message, $this->_parts[$i]);
|
693 |
+
}
|
694 |
+
break;
|
695 |
+
|
696 |
+
case $html AND !$attachments AND !$html_images:
|
697 |
+
if (isset($this->_txtbody)) {
|
698 |
+
$message =& $this->_addAlternativePart($null);
|
699 |
+
$this->_addTextPart($message, $this->_txtbody);
|
700 |
+
$this->_addHtmlPart($message);
|
701 |
+
} else {
|
702 |
+
$message =& $this->_addHtmlPart($null);
|
703 |
+
}
|
704 |
+
break;
|
705 |
+
|
706 |
+
case $html AND !$attachments AND $html_images:
|
707 |
+
$message =& $this->_addRelatedPart($null);
|
708 |
+
if (isset($this->_txtbody)) {
|
709 |
+
$alt =& $this->_addAlternativePart($message);
|
710 |
+
$this->_addTextPart($alt, $this->_txtbody);
|
711 |
+
$this->_addHtmlPart($alt);
|
712 |
+
} else {
|
713 |
+
$this->_addHtmlPart($message);
|
714 |
+
}
|
715 |
+
for ($i = 0; $i < count($this->_html_images); $i++) {
|
716 |
+
$this->_addHtmlImagePart($message, $this->_html_images[$i]);
|
717 |
+
}
|
718 |
+
break;
|
719 |
+
|
720 |
+
case $html AND $attachments AND !$html_images:
|
721 |
+
$message =& $this->_addMixedPart();
|
722 |
+
if (isset($this->_txtbody)) {
|
723 |
+
$alt =& $this->_addAlternativePart($message);
|
724 |
+
$this->_addTextPart($alt, $this->_txtbody);
|
725 |
+
$this->_addHtmlPart($alt);
|
726 |
+
} else {
|
727 |
+
$this->_addHtmlPart($message);
|
728 |
+
}
|
729 |
+
for ($i = 0; $i < count($this->_parts); $i++) {
|
730 |
+
$this->_addAttachmentPart($message, $this->_parts[$i]);
|
731 |
+
}
|
732 |
+
break;
|
733 |
+
|
734 |
+
case $html AND $attachments AND $html_images:
|
735 |
+
$message =& $this->_addMixedPart();
|
736 |
+
if (isset($this->_txtbody)) {
|
737 |
+
$alt =& $this->_addAlternativePart($message);
|
738 |
+
$this->_addTextPart($alt, $this->_txtbody);
|
739 |
+
$rel =& $this->_addRelatedPart($alt);
|
740 |
+
} else {
|
741 |
+
$rel =& $this->_addRelatedPart($message);
|
742 |
+
}
|
743 |
+
$this->_addHtmlPart($rel);
|
744 |
+
for ($i = 0; $i < count($this->_html_images); $i++) {
|
745 |
+
$this->_addHtmlImagePart($rel, $this->_html_images[$i]);
|
746 |
+
}
|
747 |
+
for ($i = 0; $i < count($this->_parts); $i++) {
|
748 |
+
$this->_addAttachmentPart($message, $this->_parts[$i]);
|
749 |
+
}
|
750 |
+
break;
|
751 |
+
|
752 |
+
}
|
753 |
+
|
754 |
+
if (isset($message)) {
|
755 |
+
$output = $message->encode();
|
756 |
+
|
757 |
+
$this->_headers = array_merge($this->_headers,
|
758 |
+
$output['headers']);
|
759 |
+
$body = $output['body'];
|
760 |
+
return $body;
|
761 |
+
|
762 |
+
} else {
|
763 |
+
$ret = false;
|
764 |
+
return $ret;
|
765 |
+
}
|
766 |
+
}
|
767 |
+
|
768 |
+
/**
|
769 |
+
* Returns an array with the headers needed to prepend to the email
|
770 |
+
* (MIME-Version and Content-Type). Format of argument is:
|
771 |
+
* $array['header-name'] = 'header-value';
|
772 |
+
*
|
773 |
+
* @param array $xtra_headers Assoc array with any extra headers.
|
774 |
+
* Optional.
|
775 |
+
* @param bool $overwrite Overwrite already existing headers.
|
776 |
+
*
|
777 |
+
* @return array Assoc array with the mime headers
|
778 |
+
* @access public
|
779 |
+
*/
|
780 |
+
function &headers($xtra_headers = null, $overwrite = false)
|
781 |
+
{
|
782 |
+
// Content-Type header should already be present,
|
783 |
+
// So just add mime version header
|
784 |
+
$headers['MIME-Version'] = '1.0';
|
785 |
+
if (isset($xtra_headers)) {
|
786 |
+
$headers = array_merge($headers, $xtra_headers);
|
787 |
+
}
|
788 |
+
if ($overwrite) {
|
789 |
+
$this->_headers = array_merge($this->_headers, $headers);
|
790 |
+
} else {
|
791 |
+
$this->_headers = array_merge($headers, $this->_headers);
|
792 |
+
}
|
793 |
+
|
794 |
+
$encodedHeaders = $this->_encodeHeaders($this->_headers);
|
795 |
+
return $encodedHeaders;
|
796 |
+
}
|
797 |
+
|
798 |
+
/**
|
799 |
+
* Get the text version of the headers
|
800 |
+
* (usefull if you want to use the PHP mail() function)
|
801 |
+
*
|
802 |
+
* @param array $xtra_headers Assoc array with any extra headers.
|
803 |
+
* Optional.
|
804 |
+
* @param bool $overwrite Overwrite the existing heaers with new.
|
805 |
+
*
|
806 |
+
* @return string Plain text headers
|
807 |
+
* @access public
|
808 |
+
*/
|
809 |
+
function txtHeaders($xtra_headers = null, $overwrite = false)
|
810 |
+
{
|
811 |
+
$headers = $this->headers($xtra_headers, $overwrite);
|
812 |
+
|
813 |
+
$ret = '';
|
814 |
+
foreach ($headers as $key => $val) {
|
815 |
+
$ret .= "$key: $val" . MAIL_MIME_CRLF;
|
816 |
+
}
|
817 |
+
return $ret;
|
818 |
+
}
|
819 |
+
|
820 |
+
/**
|
821 |
+
* Sets the Subject header
|
822 |
+
*
|
823 |
+
* @param string $subject String to set the subject to.
|
824 |
+
*
|
825 |
+
* @return void
|
826 |
+
* @access public
|
827 |
+
*/
|
828 |
+
function setSubject($subject)
|
829 |
+
{
|
830 |
+
$this->_headers['Subject'] = $subject;
|
831 |
+
}
|
832 |
+
|
833 |
+
/**
|
834 |
+
* Set an email to the From (the sender) header
|
835 |
+
*
|
836 |
+
* @param string $email The email address to use
|
837 |
+
*
|
838 |
+
* @return void
|
839 |
+
* @access public
|
840 |
+
*/
|
841 |
+
function setFrom($email)
|
842 |
+
{
|
843 |
+
$this->_headers['From'] = $email;
|
844 |
+
}
|
845 |
+
|
846 |
+
/**
|
847 |
+
* Add an email to the Cc (carbon copy) header
|
848 |
+
* (multiple calls to this method are allowed)
|
849 |
+
*
|
850 |
+
* @param string $email The email direction to add
|
851 |
+
*
|
852 |
+
* @return void
|
853 |
+
* @access public
|
854 |
+
*/
|
855 |
+
function addCc($email)
|
856 |
+
{
|
857 |
+
if (isset($this->_headers['Cc'])) {
|
858 |
+
$this->_headers['Cc'] .= ", $email";
|
859 |
+
} else {
|
860 |
+
$this->_headers['Cc'] = $email;
|
861 |
+
}
|
862 |
+
}
|
863 |
+
|
864 |
+
/**
|
865 |
+
* Add an email to the Bcc (blank carbon copy) header
|
866 |
+
* (multiple calls to this method are allowed)
|
867 |
+
*
|
868 |
+
* @param string $email The email direction to add
|
869 |
+
*
|
870 |
+
* @return void
|
871 |
+
* @access public
|
872 |
+
*/
|
873 |
+
function addBcc($email)
|
874 |
+
{
|
875 |
+
if (isset($this->_headers['Bcc'])) {
|
876 |
+
$this->_headers['Bcc'] .= ", $email";
|
877 |
+
} else {
|
878 |
+
$this->_headers['Bcc'] = $email;
|
879 |
+
}
|
880 |
+
}
|
881 |
+
|
882 |
+
/**
|
883 |
+
* Since the PHP send function requires you to specifiy
|
884 |
+
* recipients (To: header) separately from the other
|
885 |
+
* headers, the To: header is not properly encoded.
|
886 |
+
* To fix this, you can use this public method to
|
887 |
+
* encode your recipients before sending to the send
|
888 |
+
* function
|
889 |
+
*
|
890 |
+
* @param string $recipients A comma-delimited list of recipients
|
891 |
+
*
|
892 |
+
* @return string Encoded data
|
893 |
+
* @access public
|
894 |
+
*/
|
895 |
+
function encodeRecipients($recipients)
|
896 |
+
{
|
897 |
+
$input = array("To" => $recipients);
|
898 |
+
$retval = $this->_encodeHeaders($input);
|
899 |
+
return $retval["To"] ;
|
900 |
+
}
|
901 |
+
|
902 |
+
/**
|
903 |
+
* Encodes a header as per RFC2047
|
904 |
+
*
|
905 |
+
* @param array $input The header data to encode
|
906 |
+
* @param array $params Extra build parameters
|
907 |
+
*
|
908 |
+
* @return array Encoded data
|
909 |
+
* @access private
|
910 |
+
*/
|
911 |
+
function _encodeHeaders($input, $params = array())
|
912 |
+
{
|
913 |
+
|
914 |
+
$build_params = $this->_build_params;
|
915 |
+
while (list($key, $value) = each($params)) {
|
916 |
+
$build_params[$key] = $value;
|
917 |
+
}
|
918 |
+
//$hdr_name: Name of the heaer
|
919 |
+
//$hdr_value: Full line of header value.
|
920 |
+
//$hdr_value_out: The recombined $hdr_val-atoms, or the encoded string.
|
921 |
+
|
922 |
+
$useIconv = true;
|
923 |
+
if (isset($build_params['ignore-iconv'])) {
|
924 |
+
$useIconv = !$build_params['ignore-iconv'];
|
925 |
+
}
|
926 |
+
foreach ($input as $hdr_name => $hdr_value) {
|
927 |
+
if (preg_match('#([\x80-\xFF]){1}#', $hdr_value)) {
|
928 |
+
if (function_exists('iconv_mime_encode') && $useIconv) {
|
929 |
+
$imePrefs = array();
|
930 |
+
if ($build_params['head_encoding'] == 'base64') {
|
931 |
+
$imePrefs['scheme'] = 'B';
|
932 |
+
} else {
|
933 |
+
$imePrefs['scheme'] = 'Q';
|
934 |
+
}
|
935 |
+
$imePrefs['input-charset'] = $build_params['head_charset'];
|
936 |
+
$imePrefs['output-charset'] = $build_params['head_charset'];
|
937 |
+
$imePrefs['line-length'] = 74;
|
938 |
+
$imePrefs['line-break-chars'] = "\r\n"; //Specified in RFC2047
|
939 |
+
|
940 |
+
$hdr_value = iconv_mime_encode($hdr_name, $hdr_value, $imePrefs);
|
941 |
+
$hdr_value = preg_replace("#^{$hdr_name}\:\ #", "", $hdr_value);
|
942 |
+
} elseif ($build_params['head_encoding'] == 'base64') {
|
943 |
+
//Base64 encoding has been selected.
|
944 |
+
//Base64 encode the entire string
|
945 |
+
$hdr_value = base64_encode($hdr_value);
|
946 |
+
|
947 |
+
//Generate the header using the specified params and dynamicly
|
948 |
+
//determine the maximum length of such strings.
|
949 |
+
//75 is the value specified in the RFC. The first -2 is there so
|
950 |
+
//the later regexp doesn't break any of the translated chars.
|
951 |
+
//The -2 on the first line-regexp is to compensate for the ": "
|
952 |
+
//between the header-name and the header value
|
953 |
+
$prefix = '=?' . $build_params['head_charset'] . '?B?';
|
954 |
+
$suffix = '?=';
|
955 |
+
$maxLength = 75 - strlen($prefix . $suffix) - 2;
|
956 |
+
$maxLength1stLine = $maxLength - strlen($hdr_name) - 2;
|
957 |
+
|
958 |
+
//We can cut base4 every 4 characters, so the real max
|
959 |
+
//we can get must be rounded down.
|
960 |
+
$maxLength = $maxLength - ($maxLength % 4);
|
961 |
+
$maxLength1stLine = $maxLength1stLine - ($maxLength1stLine % 4);
|
962 |
+
|
963 |
+
$cutpoint = $maxLength1stLine;
|
964 |
+
$hdr_value_out = $hdr_value;
|
965 |
+
$output = "";
|
966 |
+
while ($hdr_value_out) {
|
967 |
+
//Split translated string at every $maxLength
|
968 |
+
$part = substr($hdr_value_out, 0, $cutpoint);
|
969 |
+
$hdr_value_out = substr($hdr_value_out, $cutpoint);
|
970 |
+
$cutpoint = $maxLength;
|
971 |
+
//RFC 2047 specifies that any split header should
|
972 |
+
//be seperated by a CRLF SPACE.
|
973 |
+
if ($output) {
|
974 |
+
$output .= "\r\n ";
|
975 |
+
}
|
976 |
+
$output .= $prefix . $part . $suffix;
|
977 |
+
}
|
978 |
+
$hdr_value = $output;
|
979 |
+
} else {
|
980 |
+
//quoted-printable encoding has been selected
|
981 |
+
|
982 |
+
//Fix for Bug #10298, Ota Mares <om@viazenetti.de>
|
983 |
+
//Check if there is a double quote at beginning or end of
|
984 |
+
//the string to prevent that an open or closing quote gets
|
985 |
+
//ignored because it is encapsuled by an encoding pre/suffix.
|
986 |
+
//Remove the double quote and set the specific prefix or
|
987 |
+
//suffix variable so that we can concat the encoded string and
|
988 |
+
//the double quotes back together to get the intended string.
|
989 |
+
$quotePrefix = $quoteSuffix = '';
|
990 |
+
if ($hdr_value{0} == '"') {
|
991 |
+
$hdr_value = substr($hdr_value, 1);
|
992 |
+
$quotePrefix = '"';
|
993 |
+
}
|
994 |
+
if ($hdr_value{strlen($hdr_value)-1} == '"') {
|
995 |
+
$hdr_value = substr($hdr_value, 0, -1);
|
996 |
+
$quoteSuffix = '"';
|
997 |
+
}
|
998 |
+
|
999 |
+
//Generate the header using the specified params and dynamicly
|
1000 |
+
//determine the maximum length of such strings.
|
1001 |
+
//75 is the value specified in the RFC. The -2 is there so
|
1002 |
+
//the later regexp doesn't break any of the translated chars.
|
1003 |
+
//The -2 on the first line-regexp is to compensate for the ": "
|
1004 |
+
//between the header-name and the header value
|
1005 |
+
$prefix = '=?' . $build_params['head_charset'] . '?Q?';
|
1006 |
+
$suffix = '?=';
|
1007 |
+
$maxLength = 75 - strlen($prefix . $suffix) - 2 - 1;
|
1008 |
+
$maxLength1stLine = $maxLength - strlen($hdr_name) - 2;
|
1009 |
+
$maxLength = $maxLength - 1;
|
1010 |
+
|
1011 |
+
//Replace all special characters used by the encoder.
|
1012 |
+
$search = array('=', '_', '?', ' ');
|
1013 |
+
$replace = array('=3D', '=5F', '=3F', '_');
|
1014 |
+
$hdr_value = str_replace($search, $replace, $hdr_value);
|
1015 |
+
|
1016 |
+
//Replace all extended characters (\x80-xFF) with their
|
1017 |
+
//ASCII values.
|
1018 |
+
$hdr_value = preg_replace('#([\x80-\xFF])#e',
|
1019 |
+
'"=" . strtoupper(dechex(ord("\1")))',
|
1020 |
+
$hdr_value);
|
1021 |
+
|
1022 |
+
//This regexp will break QP-encoded text at every $maxLength
|
1023 |
+
//but will not break any encoded letters.
|
1024 |
+
$reg1st = "|(.{0,$maxLength1stLine}[^\=][^\=])|";
|
1025 |
+
$reg2nd = "|(.{0,$maxLength}[^\=][^\=])|";
|
1026 |
+
//Fix for Bug #10298, Ota Mares <om@viazenetti.de>
|
1027 |
+
//Concat the double quotes and encoded string together
|
1028 |
+
$hdr_value = $quotePrefix . $hdr_value . $quoteSuffix;
|
1029 |
+
|
1030 |
+
|
1031 |
+
$hdr_value_out = $hdr_value;
|
1032 |
+
$realMax = $maxLength1stLine + strlen($prefix . $suffix);
|
1033 |
+
if (strlen($hdr_value_out) >= $realMax) {
|
1034 |
+
//Begin with the regexp for the first line.
|
1035 |
+
$reg = $reg1st;
|
1036 |
+
$output = "";
|
1037 |
+
while ($hdr_value_out) {
|
1038 |
+
//Split translated string at every $maxLength
|
1039 |
+
//But make sure not to break any translated chars.
|
1040 |
+
$found = preg_match($reg, $hdr_value_out, $matches);
|
1041 |
+
|
1042 |
+
//After this first line, we need to use a different
|
1043 |
+
//regexp for the first line.
|
1044 |
+
$reg = $reg2nd;
|
1045 |
+
|
1046 |
+
//Save the found part and encapsulate it in the
|
1047 |
+
//prefix & suffix. Then remove the part from the
|
1048 |
+
//$hdr_value_out variable.
|
1049 |
+
if ($found) {
|
1050 |
+
$part = $matches[0];
|
1051 |
+
$len = strlen($matches[0]);
|
1052 |
+
$hdr_value_out = substr($hdr_value_out, $len);
|
1053 |
+
} else {
|
1054 |
+
$part = $hdr_value_out;
|
1055 |
+
$hdr_value_out = "";
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
//RFC 2047 specifies that any split header should
|
1059 |
+
//be seperated by a CRLF SPACE
|
1060 |
+
if ($output) {
|
1061 |
+
$output .= "\r\n ";
|
1062 |
+
}
|
1063 |
+
$output .= $prefix . $part . $suffix;
|
1064 |
+
}
|
1065 |
+
$hdr_value_out = $output;
|
1066 |
+
} else {
|
1067 |
+
$hdr_value_out = $prefix . $hdr_value_out . $suffix;
|
1068 |
+
}
|
1069 |
+
$hdr_value = $hdr_value_out;
|
1070 |
+
}
|
1071 |
+
}
|
1072 |
+
$input[$hdr_name] = $hdr_value;
|
1073 |
+
}
|
1074 |
+
return $input;
|
1075 |
+
}
|
1076 |
+
|
1077 |
+
/**
|
1078 |
+
* Set the object's end-of-line and define the constant if applicable.
|
1079 |
+
*
|
1080 |
+
* @param string $eol End Of Line sequence
|
1081 |
+
*
|
1082 |
+
* @return void
|
1083 |
+
* @access private
|
1084 |
+
*/
|
1085 |
+
function _setEOL($eol)
|
1086 |
+
{
|
1087 |
+
$this->_eol = $eol;
|
1088 |
+
if (!defined('MAIL_MIME_CRLF')) {
|
1089 |
+
define('MAIL_MIME_CRLF', $this->_eol, true);
|
1090 |
+
}
|
1091 |
+
}
|
1092 |
+
|
1093 |
+
|
1094 |
+
|
1095 |
+
} // End of class
|
lib/PEAR/Mail/mimeDecode.php
ADDED
@@ -0,0 +1,849 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The Mail_mimeDecode class is used to decode mail/mime messages
|
4 |
+
*
|
5 |
+
* This class will parse a raw mime email and return
|
6 |
+
* the structure. Returned structure is similar to
|
7 |
+
* that returned by imap_fetchstructure().
|
8 |
+
*
|
9 |
+
* +----------------------------- IMPORTANT ------------------------------+
|
10 |
+
* | Usage of this class compared to native php extensions such as |
|
11 |
+
* | mailparse or imap, is slow and may be feature deficient. If available|
|
12 |
+
* | you are STRONGLY recommended to use the php extensions. |
|
13 |
+
* +----------------------------------------------------------------------+
|
14 |
+
*
|
15 |
+
* Compatible with PHP versions 4 and 5
|
16 |
+
*
|
17 |
+
* LICENSE: This LICENSE is in the BSD license style.
|
18 |
+
* Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
|
19 |
+
* Copyright (c) 2003-2006, PEAR <pear-group@php.net>
|
20 |
+
* All rights reserved.
|
21 |
+
*
|
22 |
+
* Redistribution and use in source and binary forms, with or
|
23 |
+
* without modification, are permitted provided that the following
|
24 |
+
* conditions are met:
|
25 |
+
*
|
26 |
+
* - Redistributions of source code must retain the above copyright
|
27 |
+
* notice, this list of conditions and the following disclaimer.
|
28 |
+
* - Redistributions in binary form must reproduce the above copyright
|
29 |
+
* notice, this list of conditions and the following disclaimer in the
|
30 |
+
* documentation and/or other materials provided with the distribution.
|
31 |
+
* - Neither the name of the authors, nor the names of its contributors
|
32 |
+
* may be used to endorse or promote products derived from this
|
33 |
+
* software without specific prior written permission.
|
34 |
+
*
|
35 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
36 |
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
37 |
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
38 |
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
39 |
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
40 |
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
41 |
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
42 |
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
43 |
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
44 |
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
45 |
+
* THE POSSIBILITY OF SUCH DAMAGE.
|
46 |
+
*
|
47 |
+
* @category Mail
|
48 |
+
* @package Mail_Mime
|
49 |
+
* @author Richard Heyes <richard@phpguru.org>
|
50 |
+
* @author George Schlossnagle <george@omniti.com>
|
51 |
+
* @author Cipriano Groenendal <cipri@php.net>
|
52 |
+
* @author Sean Coates <sean@php.net>
|
53 |
+
* @copyright 2003-2006 PEAR <pear-group@php.net>
|
54 |
+
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
55 |
+
* @version CVS: $Id: mimeDecode.php,v 1.48 2006/12/03 13:43:33 cipri Exp $
|
56 |
+
* @link http://pear.php.net/package/Mail_mime
|
57 |
+
*/
|
58 |
+
|
59 |
+
|
60 |
+
/**
|
61 |
+
* require PEAR
|
62 |
+
*
|
63 |
+
* This package depends on PEAR to raise errors.
|
64 |
+
*/
|
65 |
+
require_once 'PEAR.php';
|
66 |
+
|
67 |
+
|
68 |
+
/**
|
69 |
+
* The Mail_mimeDecode class is used to decode mail/mime messages
|
70 |
+
*
|
71 |
+
* This class will parse a raw mime email and return the structure.
|
72 |
+
* Returned structure is similar to that returned by imap_fetchstructure().
|
73 |
+
*
|
74 |
+
* +----------------------------- IMPORTANT ------------------------------+
|
75 |
+
* | Usage of this class compared to native php extensions such as |
|
76 |
+
* | mailparse or imap, is slow and may be feature deficient. If available|
|
77 |
+
* | you are STRONGLY recommended to use the php extensions. |
|
78 |
+
* +----------------------------------------------------------------------+
|
79 |
+
*
|
80 |
+
* @category Mail
|
81 |
+
* @package Mail_Mime
|
82 |
+
* @author Richard Heyes <richard@phpguru.org>
|
83 |
+
* @author George Schlossnagle <george@omniti.com>
|
84 |
+
* @author Cipriano Groenendal <cipri@php.net>
|
85 |
+
* @author Sean Coates <sean@php.net>
|
86 |
+
* @copyright 2003-2006 PEAR <pear-group@php.net>
|
87 |
+
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
88 |
+
* @version Release: @package_version@
|
89 |
+
* @link http://pear.php.net/package/Mail_mime
|
90 |
+
*/
|
91 |
+
class Mail_mimeDecode extends PEAR
|
92 |
+
{
|
93 |
+
/**
|
94 |
+
* The raw email to decode
|
95 |
+
*
|
96 |
+
* @var string
|
97 |
+
* @access private
|
98 |
+
*/
|
99 |
+
var $_input;
|
100 |
+
|
101 |
+
/**
|
102 |
+
* The header part of the input
|
103 |
+
*
|
104 |
+
* @var string
|
105 |
+
* @access private
|
106 |
+
*/
|
107 |
+
var $_header;
|
108 |
+
|
109 |
+
/**
|
110 |
+
* The body part of the input
|
111 |
+
*
|
112 |
+
* @var string
|
113 |
+
* @access private
|
114 |
+
*/
|
115 |
+
var $_body;
|
116 |
+
|
117 |
+
/**
|
118 |
+
* If an error occurs, this is used to store the message
|
119 |
+
*
|
120 |
+
* @var string
|
121 |
+
* @access private
|
122 |
+
*/
|
123 |
+
var $_error;
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Flag to determine whether to include bodies in the
|
127 |
+
* returned object.
|
128 |
+
*
|
129 |
+
* @var boolean
|
130 |
+
* @access private
|
131 |
+
*/
|
132 |
+
var $_include_bodies;
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Flag to determine whether to decode bodies
|
136 |
+
*
|
137 |
+
* @var boolean
|
138 |
+
* @access private
|
139 |
+
*/
|
140 |
+
var $_decode_bodies;
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Flag to determine whether to decode headers
|
144 |
+
*
|
145 |
+
* @var boolean
|
146 |
+
* @access private
|
147 |
+
*/
|
148 |
+
var $_decode_headers;
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Constructor.
|
152 |
+
*
|
153 |
+
* Sets up the object, initialise the variables, and splits and
|
154 |
+
* stores the header and body of the input.
|
155 |
+
*
|
156 |
+
* @param string The input to decode
|
157 |
+
* @access public
|
158 |
+
*/
|
159 |
+
function Mail_mimeDecode($input)
|
160 |
+
{
|
161 |
+
list($header, $body) = $this->_splitBodyHeader($input);
|
162 |
+
|
163 |
+
$this->_input = $input;
|
164 |
+
$this->_header = $header;
|
165 |
+
$this->_body = $body;
|
166 |
+
$this->_decode_bodies = false;
|
167 |
+
$this->_include_bodies = true;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Begins the decoding process. If called statically
|
172 |
+
* it will create an object and call the decode() method
|
173 |
+
* of it.
|
174 |
+
*
|
175 |
+
* @param array An array of various parameters that determine
|
176 |
+
* various things:
|
177 |
+
* include_bodies - Whether to include the body in the returned
|
178 |
+
* object.
|
179 |
+
* decode_bodies - Whether to decode the bodies
|
180 |
+
* of the parts. (Transfer encoding)
|
181 |
+
* decode_headers - Whether to decode headers
|
182 |
+
* input - If called statically, this will be treated
|
183 |
+
* as the input
|
184 |
+
* @return object Decoded results
|
185 |
+
* @access public
|
186 |
+
*/
|
187 |
+
function decode($params = null)
|
188 |
+
{
|
189 |
+
// determine if this method has been called statically
|
190 |
+
$isStatic = !(isset($this) && get_class($this) == __CLASS__);
|
191 |
+
|
192 |
+
// Have we been called statically?
|
193 |
+
// If so, create an object and pass details to that.
|
194 |
+
if ($isStatic AND isset($params['input'])) {
|
195 |
+
|
196 |
+
$obj = new Mail_mimeDecode($params['input']);
|
197 |
+
$structure = $obj->decode($params);
|
198 |
+
|
199 |
+
// Called statically but no input
|
200 |
+
} elseif ($isStatic) {
|
201 |
+
return PEAR::raiseError('Called statically and no input given');
|
202 |
+
|
203 |
+
// Called via an object
|
204 |
+
} else {
|
205 |
+
$this->_include_bodies = isset($params['include_bodies']) ?
|
206 |
+
$params['include_bodies'] : false;
|
207 |
+
$this->_decode_bodies = isset($params['decode_bodies']) ?
|
208 |
+
$params['decode_bodies'] : false;
|
209 |
+
$this->_decode_headers = isset($params['decode_headers']) ?
|
210 |
+
$params['decode_headers'] : false;
|
211 |
+
|
212 |
+
$structure = $this->_decode($this->_header, $this->_body);
|
213 |
+
if ($structure === false) {
|
214 |
+
$structure = $this->raiseError($this->_error);
|
215 |
+
}
|
216 |
+
}
|
217 |
+
|
218 |
+
return $structure;
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Performs the decoding. Decodes the body string passed to it
|
223 |
+
* If it finds certain content-types it will call itself in a
|
224 |
+
* recursive fashion
|
225 |
+
*
|
226 |
+
* @param string Header section
|
227 |
+
* @param string Body section
|
228 |
+
* @return object Results of decoding process
|
229 |
+
* @access private
|
230 |
+
*/
|
231 |
+
function _decode($headers, $body, $default_ctype = 'text/plain')
|
232 |
+
{
|
233 |
+
$return = new stdClass;
|
234 |
+
$return->headers = array();
|
235 |
+
$headers = $this->_parseHeaders($headers);
|
236 |
+
|
237 |
+
foreach ($headers as $value) {
|
238 |
+
if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
|
239 |
+
$return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
|
240 |
+
$return->headers[strtolower($value['name'])][] = $value['value'];
|
241 |
+
|
242 |
+
} elseif (isset($return->headers[strtolower($value['name'])])) {
|
243 |
+
$return->headers[strtolower($value['name'])][] = $value['value'];
|
244 |
+
|
245 |
+
} else {
|
246 |
+
$return->headers[strtolower($value['name'])] = $value['value'];
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
reset($headers);
|
251 |
+
while (list($key, $value) = each($headers)) {
|
252 |
+
$headers[$key]['name'] = strtolower($headers[$key]['name']);
|
253 |
+
switch ($headers[$key]['name']) {
|
254 |
+
|
255 |
+
case 'content-type':
|
256 |
+
$content_type = $this->_parseHeaderValue($headers[$key]['value']);
|
257 |
+
|
258 |
+
if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
|
259 |
+
$return->ctype_primary = $regs[1];
|
260 |
+
$return->ctype_secondary = $regs[2];
|
261 |
+
}
|
262 |
+
|
263 |
+
if (isset($content_type['other'])) {
|
264 |
+
while (list($p_name, $p_value) = each($content_type['other'])) {
|
265 |
+
$return->ctype_parameters[$p_name] = $p_value;
|
266 |
+
}
|
267 |
+
}
|
268 |
+
break;
|
269 |
+
|
270 |
+
case 'content-disposition':
|
271 |
+
$content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
|
272 |
+
$return->disposition = $content_disposition['value'];
|
273 |
+
if (isset($content_disposition['other'])) {
|
274 |
+
while (list($p_name, $p_value) = each($content_disposition['other'])) {
|
275 |
+
$return->d_parameters[$p_name] = $p_value;
|
276 |
+
}
|
277 |
+
}
|
278 |
+
break;
|
279 |
+
|
280 |
+
case 'content-transfer-encoding':
|
281 |
+
$content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
|
282 |
+
break;
|
283 |
+
}
|
284 |
+
}
|
285 |
+
|
286 |
+
if (isset($content_type)) {
|
287 |
+
switch (strtolower($content_type['value'])) {
|
288 |
+
case 'text/plain':
|
289 |
+
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
|
290 |
+
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
|
291 |
+
break;
|
292 |
+
|
293 |
+
case 'text/html':
|
294 |
+
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
|
295 |
+
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
|
296 |
+
break;
|
297 |
+
|
298 |
+
case 'multipart/parallel':
|
299 |
+
case 'multipart/appledouble': // Appledouble mail
|
300 |
+
case 'multipart/report': // RFC1892
|
301 |
+
case 'multipart/signed': // PGP
|
302 |
+
case 'multipart/digest':
|
303 |
+
case 'multipart/alternative':
|
304 |
+
case 'multipart/related':
|
305 |
+
case 'multipart/mixed':
|
306 |
+
if(!isset($content_type['other']['boundary'])){
|
307 |
+
$this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
|
308 |
+
return false;
|
309 |
+
}
|
310 |
+
|
311 |
+
$default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
|
312 |
+
|
313 |
+
$parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
|
314 |
+
for ($i = 0; $i < count($parts); $i++) {
|
315 |
+
list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
|
316 |
+
$part = $this->_decode($part_header, $part_body, $default_ctype);
|
317 |
+
if($part === false)
|
318 |
+
$part = $this->raiseError($this->_error);
|
319 |
+
$return->parts[] = $part;
|
320 |
+
}
|
321 |
+
break;
|
322 |
+
|
323 |
+
case 'message/rfc822':
|
324 |
+
$obj = new Mail_mimeDecode($body);
|
325 |
+
$return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
|
326 |
+
'decode_bodies' => $this->_decode_bodies,
|
327 |
+
'decode_headers' => $this->_decode_headers));
|
328 |
+
unset($obj);
|
329 |
+
break;
|
330 |
+
|
331 |
+
default:
|
332 |
+
if(!isset($content_transfer_encoding['value']))
|
333 |
+
$content_transfer_encoding['value'] = '7bit';
|
334 |
+
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;
|
335 |
+
break;
|
336 |
+
}
|
337 |
+
|
338 |
+
} else {
|
339 |
+
$ctype = explode('/', $default_ctype);
|
340 |
+
$return->ctype_primary = $ctype[0];
|
341 |
+
$return->ctype_secondary = $ctype[1];
|
342 |
+
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
|
343 |
+
}
|
344 |
+
|
345 |
+
return $return;
|
346 |
+
}
|
347 |
+
|
348 |
+
/**
|
349 |
+
* Given the output of the above function, this will return an
|
350 |
+
* array of references to the parts, indexed by mime number.
|
351 |
+
*
|
352 |
+
* @param object $structure The structure to go through
|
353 |
+
* @param string $mime_number Internal use only.
|
354 |
+
* @return array Mime numbers
|
355 |
+
*/
|
356 |
+
function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
|
357 |
+
{
|
358 |
+
$return = array();
|
359 |
+
if (!empty($structure->parts)) {
|
360 |
+
if ($mime_number != '') {
|
361 |
+
$structure->mime_id = $prepend . $mime_number;
|
362 |
+
$return[$prepend . $mime_number] = &$structure;
|
363 |
+
}
|
364 |
+
for ($i = 0; $i < count($structure->parts); $i++) {
|
365 |
+
|
366 |
+
|
367 |
+
if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
|
368 |
+
$prepend = $prepend . $mime_number . '.';
|
369 |
+
$_mime_number = '';
|
370 |
+
} else {
|
371 |
+
$_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
|
372 |
+
}
|
373 |
+
|
374 |
+
$arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
|
375 |
+
foreach ($arr as $key => $val) {
|
376 |
+
$no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
|
377 |
+
}
|
378 |
+
}
|
379 |
+
} else {
|
380 |
+
if ($mime_number == '') {
|
381 |
+
$mime_number = '1';
|
382 |
+
}
|
383 |
+
$structure->mime_id = $prepend . $mime_number;
|
384 |
+
$no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
|
385 |
+
}
|
386 |
+
|
387 |
+
return $return;
|
388 |
+
}
|
389 |
+
|
390 |
+
/**
|
391 |
+
* Given a string containing a header and body
|
392 |
+
* section, this function will split them (at the first
|
393 |
+
* blank line) and return them.
|
394 |
+
*
|
395 |
+
* @param string Input to split apart
|
396 |
+
* @return array Contains header and body section
|
397 |
+
* @access private
|
398 |
+
*/
|
399 |
+
function _splitBodyHeader($input)
|
400 |
+
{
|
401 |
+
if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
|
402 |
+
return array($match[1], $match[2]);
|
403 |
+
}
|
404 |
+
$this->_error = 'Could not split header and body';
|
405 |
+
return false;
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Parse headers given in $input and return
|
410 |
+
* as assoc array.
|
411 |
+
*
|
412 |
+
* @param string Headers to parse
|
413 |
+
* @return array Contains parsed headers
|
414 |
+
* @access private
|
415 |
+
*/
|
416 |
+
function _parseHeaders($input)
|
417 |
+
{
|
418 |
+
|
419 |
+
if ($input !== '') {
|
420 |
+
// Unfold the input
|
421 |
+
$input = preg_replace("/\r?\n/", "\r\n", $input);
|
422 |
+
$input = preg_replace("/\r\n(\t| )+/", ' ', $input);
|
423 |
+
$headers = explode("\r\n", trim($input));
|
424 |
+
|
425 |
+
foreach ($headers as $value) {
|
426 |
+
$hdr_name = substr($value, 0, $pos = strpos($value, ':'));
|
427 |
+
$hdr_value = substr($value, $pos+1);
|
428 |
+
if($hdr_value[0] == ' ')
|
429 |
+
$hdr_value = substr($hdr_value, 1);
|
430 |
+
|
431 |
+
$return[] = array(
|
432 |
+
'name' => $hdr_name,
|
433 |
+
'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
|
434 |
+
);
|
435 |
+
}
|
436 |
+
} else {
|
437 |
+
$return = array();
|
438 |
+
}
|
439 |
+
|
440 |
+
return $return;
|
441 |
+
}
|
442 |
+
|
443 |
+
/**
|
444 |
+
* Function to parse a header value,
|
445 |
+
* extract first part, and any secondary
|
446 |
+
* parts (after ;) This function is not as
|
447 |
+
* robust as it could be. Eg. header comments
|
448 |
+
* in the wrong place will probably break it.
|
449 |
+
*
|
450 |
+
* @param string Header value to parse
|
451 |
+
* @return array Contains parsed result
|
452 |
+
* @access private
|
453 |
+
*/
|
454 |
+
function _parseHeaderValue($input)
|
455 |
+
{
|
456 |
+
|
457 |
+
if (($pos = strpos($input, ';')) !== false) {
|
458 |
+
|
459 |
+
$return['value'] = trim(substr($input, 0, $pos));
|
460 |
+
$input = trim(substr($input, $pos+1));
|
461 |
+
|
462 |
+
if (strlen($input) > 0) {
|
463 |
+
|
464 |
+
// This splits on a semi-colon, if there's no preceeding backslash
|
465 |
+
// Now works with quoted values; had to glue the \; breaks in PHP
|
466 |
+
// the regex is already bordering on incomprehensible
|
467 |
+
$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
|
468 |
+
preg_match_all($splitRegex, $input, $matches);
|
469 |
+
$parameters = array();
|
470 |
+
for ($i=0; $i<count($matches[0]); $i++) {
|
471 |
+
$param = $matches[0][$i];
|
472 |
+
while (substr($param, -2) == '\;') {
|
473 |
+
$param .= $matches[0][++$i];
|
474 |
+
}
|
475 |
+
$parameters[] = $param;
|
476 |
+
}
|
477 |
+
|
478 |
+
for ($i = 0; $i < count($parameters); $i++) {
|
479 |
+
$param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
|
480 |
+
$param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
|
481 |
+
if ($param_value[0] == '"') {
|
482 |
+
$param_value = substr($param_value, 1, -1);
|
483 |
+
}
|
484 |
+
$return['other'][$param_name] = $param_value;
|
485 |
+
$return['other'][strtolower($param_name)] = $param_value;
|
486 |
+
}
|
487 |
+
}
|
488 |
+
} else {
|
489 |
+
$return['value'] = trim($input);
|
490 |
+
}
|
491 |
+
|
492 |
+
return $return;
|
493 |
+
}
|
494 |
+
|
495 |
+
/**
|
496 |
+
* This function splits the input based
|
497 |
+
* on the given boundary
|
498 |
+
*
|
499 |
+
* @param string Input to parse
|
500 |
+
* @return array Contains array of resulting mime parts
|
501 |
+
* @access private
|
502 |
+
*/
|
503 |
+
function _boundarySplit($input, $boundary)
|
504 |
+
{
|
505 |
+
$parts = array();
|
506 |
+
|
507 |
+
$bs_possible = substr($boundary, 2, -2);
|
508 |
+
$bs_check = '\"' . $bs_possible . '\"';
|
509 |
+
|
510 |
+
if ($boundary == $bs_check) {
|
511 |
+
$boundary = $bs_possible;
|
512 |
+
}
|
513 |
+
|
514 |
+
$tmp = explode('--' . $boundary, $input);
|
515 |
+
|
516 |
+
for ($i = 1; $i < count($tmp) - 1; $i++) {
|
517 |
+
$parts[] = $tmp[$i];
|
518 |
+
}
|
519 |
+
|
520 |
+
return $parts;
|
521 |
+
}
|
522 |
+
|
523 |
+
/**
|
524 |
+
* Given a header, this function will decode it
|
525 |
+
* according to RFC2047. Probably not *exactly*
|
526 |
+
* conformant, but it does pass all the given
|
527 |
+
* examples (in RFC2047).
|
528 |
+
*
|
529 |
+
* @param string Input header value to decode
|
530 |
+
* @return string Decoded header value
|
531 |
+
* @access private
|
532 |
+
*/
|
533 |
+
function _decodeHeader($input)
|
534 |
+
{
|
535 |
+
// Remove white space between encoded-words
|
536 |
+
$input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
|
537 |
+
|
538 |
+
// For each encoded-word...
|
539 |
+
while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
|
540 |
+
|
541 |
+
$encoded = $matches[1];
|
542 |
+
$charset = $matches[2];
|
543 |
+
$encoding = $matches[3];
|
544 |
+
$text = $matches[4];
|
545 |
+
|
546 |
+
switch (strtolower($encoding)) {
|
547 |
+
case 'b':
|
548 |
+
$text = base64_decode($text);
|
549 |
+
break;
|
550 |
+
|
551 |
+
case 'q':
|
552 |
+
$text = str_replace('_', ' ', $text);
|
553 |
+
preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
|
554 |
+
foreach($matches[1] as $value)
|
555 |
+
$text = str_replace('='.$value, chr(hexdec($value)), $text);
|
556 |
+
break;
|
557 |
+
}
|
558 |
+
|
559 |
+
$input = str_replace($encoded, $text, $input);
|
560 |
+
}
|
561 |
+
|
562 |
+
return $input;
|
563 |
+
}
|
564 |
+
|
565 |
+
/**
|
566 |
+
* Given a body string and an encoding type,
|
567 |
+
* this function will decode and return it.
|
568 |
+
*
|
569 |
+
* @param string Input body to decode
|
570 |
+
* @param string Encoding type to use.
|
571 |
+
* @return string Decoded body
|
572 |
+
* @access private
|
573 |
+
*/
|
574 |
+
function _decodeBody($input, $encoding = '7bit')
|
575 |
+
{
|
576 |
+
switch (strtolower($encoding)) {
|
577 |
+
case '7bit':
|
578 |
+
return $input;
|
579 |
+
break;
|
580 |
+
|
581 |
+
case 'quoted-printable':
|
582 |
+
return $this->_quotedPrintableDecode($input);
|
583 |
+
break;
|
584 |
+
|
585 |
+
case 'base64':
|
586 |
+
return base64_decode($input);
|
587 |
+
break;
|
588 |
+
|
589 |
+
default:
|
590 |
+
return $input;
|
591 |
+
}
|
592 |
+
}
|
593 |
+
|
594 |
+
/**
|
595 |
+
* Given a quoted-printable string, this
|
596 |
+
* function will decode and return it.
|
597 |
+
*
|
598 |
+
* @param string Input body to decode
|
599 |
+
* @return string Decoded body
|
600 |
+
* @access private
|
601 |
+
*/
|
602 |
+
function _quotedPrintableDecode($input)
|
603 |
+
{
|
604 |
+
// Remove soft line breaks
|
605 |
+
$input = preg_replace("/=\r?\n/", '', $input);
|
606 |
+
|
607 |
+
// Replace encoded characters
|
608 |
+
$input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
|
609 |
+
|
610 |
+
return $input;
|
611 |
+
}
|
612 |
+
|
613 |
+
/**
|
614 |
+
* Checks the input for uuencoded files and returns
|
615 |
+
* an array of them. Can be called statically, eg:
|
616 |
+
*
|
617 |
+
* $files =& Mail_mimeDecode::uudecode($some_text);
|
618 |
+
*
|
619 |
+
* It will check for the begin 666 ... end syntax
|
620 |
+
* however and won't just blindly decode whatever you
|
621 |
+
* pass it.
|
622 |
+
*
|
623 |
+
* @param string Input body to look for attahcments in
|
624 |
+
* @return array Decoded bodies, filenames and permissions
|
625 |
+
* @access public
|
626 |
+
* @author Unknown
|
627 |
+
*/
|
628 |
+
function &uudecode($input)
|
629 |
+
{
|
630 |
+
// Find all uuencoded sections
|
631 |
+
preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
|
632 |
+
|
633 |
+
for ($j = 0; $j < count($matches[3]); $j++) {
|
634 |
+
|
635 |
+
$str = $matches[3][$j];
|
636 |
+
$filename = $matches[2][$j];
|
637 |
+
$fileperm = $matches[1][$j];
|
638 |
+
|
639 |
+
$file = '';
|
640 |
+
$str = preg_split("/\r?\n/", trim($str));
|
641 |
+
$strlen = count($str);
|
642 |
+
|
643 |
+
for ($i = 0; $i < $strlen; $i++) {
|
644 |
+
$pos = 1;
|
645 |
+
$d = 0;
|
646 |
+
$len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
|
647 |
+
|
648 |
+
while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
|
649 |
+
$c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
|
650 |
+
$c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
|
651 |
+
$c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
|
652 |
+
$c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
|
653 |
+
$file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
|
654 |
+
|
655 |
+
$file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
|
656 |
+
|
657 |
+
$file .= chr(((($c2 - ' ') & 077) << 6) | (($c3 - ' ') & 077));
|
658 |
+
|
659 |
+
$pos += 4;
|
660 |
+
$d += 3;
|
661 |
+
}
|
662 |
+
|
663 |
+
if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
|
664 |
+
$c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
|
665 |
+
$c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
|
666 |
+
$c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
|
667 |
+
$file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
|
668 |
+
|
669 |
+
$file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
|
670 |
+
|
671 |
+
$pos += 3;
|
672 |
+
$d += 2;
|
673 |
+
}
|
674 |
+
|
675 |
+
if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
|
676 |
+
$c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
|
677 |
+
$c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
|
678 |
+
$file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
|
679 |
+
|
680 |
+
}
|
681 |
+
}
|
682 |
+
$files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
|
683 |
+
}
|
684 |
+
|
685 |
+
return $files;
|
686 |
+
}
|
687 |
+
|
688 |
+
/**
|
689 |
+
* getSendArray() returns the arguments required for Mail::send()
|
690 |
+
* used to build the arguments for a mail::send() call
|
691 |
+
*
|
692 |
+
* Usage:
|
693 |
+
* $mailtext = Full email (for example generated by a template)
|
694 |
+
* $decoder = new Mail_mimeDecode($mailtext);
|
695 |
+
* $parts = $decoder->getSendArray();
|
696 |
+
* if (!PEAR::isError($parts) {
|
697 |
+
* list($recipents,$headers,$body) = $parts;
|
698 |
+
* $mail = Mail::factory('smtp');
|
699 |
+
* $mail->send($recipents,$headers,$body);
|
700 |
+
* } else {
|
701 |
+
* echo $parts->message;
|
702 |
+
* }
|
703 |
+
* @return mixed array of recipeint, headers,body or Pear_Error
|
704 |
+
* @access public
|
705 |
+
* @author Alan Knowles <alan@akbkhome.com>
|
706 |
+
*/
|
707 |
+
function getSendArray()
|
708 |
+
{
|
709 |
+
// prevent warning if this is not set
|
710 |
+
$this->_decode_headers = FALSE;
|
711 |
+
$headerlist =$this->_parseHeaders($this->_header);
|
712 |
+
$to = "";
|
713 |
+
if (!$headerlist) {
|
714 |
+
return $this->raiseError("Message did not contain headers");
|
715 |
+
}
|
716 |
+
foreach($headerlist as $item) {
|
717 |
+
$header[$item['name']] = $item['value'];
|
718 |
+
switch (strtolower($item['name'])) {
|
719 |
+
case "to":
|
720 |
+
case "cc":
|
721 |
+
case "bcc":
|
722 |
+
$to = ",".$item['value'];
|
723 |
+
default:
|
724 |
+
break;
|
725 |
+
}
|
726 |
+
}
|
727 |
+
if ($to == "") {
|
728 |
+
return $this->raiseError("Message did not contain any recipents");
|
729 |
+
}
|
730 |
+
$to = substr($to,1);
|
731 |
+
return array($to,$header,$this->_body);
|
732 |
+
}
|
733 |
+
|
734 |
+
/**
|
735 |
+
* Returns a xml copy of the output of
|
736 |
+
* Mail_mimeDecode::decode. Pass the output in as the
|
737 |
+
* argument. This function can be called statically. Eg:
|
738 |
+
*
|
739 |
+
* $output = $obj->decode();
|
740 |
+
* $xml = Mail_mimeDecode::getXML($output);
|
741 |
+
*
|
742 |
+
* The DTD used for this should have been in the package. Or
|
743 |
+
* alternatively you can get it from cvs, or here:
|
744 |
+
* http://www.phpguru.org/xmail/xmail.dtd.
|
745 |
+
*
|
746 |
+
* @param object Input to convert to xml. This should be the
|
747 |
+
* output of the Mail_mimeDecode::decode function
|
748 |
+
* @return string XML version of input
|
749 |
+
* @access public
|
750 |
+
*/
|
751 |
+
function getXML($input)
|
752 |
+
{
|
753 |
+
$crlf = "\r\n";
|
754 |
+
$output = '<?xml version=\'1.0\'?>' . $crlf .
|
755 |
+
'<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .
|
756 |
+
'<email>' . $crlf .
|
757 |
+
Mail_mimeDecode::_getXML($input) .
|
758 |
+
'</email>';
|
759 |
+
|
760 |
+
return $output;
|
761 |
+
}
|
762 |
+
|
763 |
+
/**
|
764 |
+
* Function that does the actual conversion to xml. Does a single
|
765 |
+
* mimepart at a time.
|
766 |
+
*
|
767 |
+
* @param object Input to convert to xml. This is a mimepart object.
|
768 |
+
* It may or may not contain subparts.
|
769 |
+
* @param integer Number of tabs to indent
|
770 |
+
* @return string XML version of input
|
771 |
+
* @access private
|
772 |
+
*/
|
773 |
+
function _getXML($input, $indent = 1)
|
774 |
+
{
|
775 |
+
$htab = "\t";
|
776 |
+
$crlf = "\r\n";
|
777 |
+
$output = '';
|
778 |
+
$headers = @(array)$input->headers;
|
779 |
+
|
780 |
+
foreach ($headers as $hdr_name => $hdr_value) {
|
781 |
+
|
782 |
+
// Multiple headers with this name
|
783 |
+
if (is_array($headers[$hdr_name])) {
|
784 |
+
for ($i = 0; $i < count($hdr_value); $i++) {
|
785 |
+
$output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
|
786 |
+
}
|
787 |
+
|
788 |
+
// Only one header of this sort
|
789 |
+
} else {
|
790 |
+
$output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
|
791 |
+
}
|
792 |
+
}
|
793 |
+
|
794 |
+
if (!empty($input->parts)) {
|
795 |
+
for ($i = 0; $i < count($input->parts); $i++) {
|
796 |
+
$output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
|
797 |
+
Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
|
798 |
+
str_repeat($htab, $indent) . '</mimepart>' . $crlf;
|
799 |
+
}
|
800 |
+
} elseif (isset($input->body)) {
|
801 |
+
$output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
|
802 |
+
$input->body . ']]></body>' . $crlf;
|
803 |
+
}
|
804 |
+
|
805 |
+
return $output;
|
806 |
+
}
|
807 |
+
|
808 |
+
/**
|
809 |
+
* Helper function to _getXML(). Returns xml of a header.
|
810 |
+
*
|
811 |
+
* @param string Name of header
|
812 |
+
* @param string Value of header
|
813 |
+
* @param integer Number of tabs to indent
|
814 |
+
* @return string XML version of input
|
815 |
+
* @access private
|
816 |
+
*/
|
817 |
+
function _getXML_helper($hdr_name, $hdr_value, $indent)
|
818 |
+
{
|
819 |
+
$htab = "\t";
|
820 |
+
$crlf = "\r\n";
|
821 |
+
$return = '';
|
822 |
+
|
823 |
+
$new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
|
824 |
+
$new_hdr_name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
|
825 |
+
|
826 |
+
// Sort out any parameters
|
827 |
+
if (!empty($new_hdr_value['other'])) {
|
828 |
+
foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
|
829 |
+
$params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
|
830 |
+
str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
|
831 |
+
str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
|
832 |
+
str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
|
833 |
+
}
|
834 |
+
|
835 |
+
$params = implode('', $params);
|
836 |
+
} else {
|
837 |
+
$params = '';
|
838 |
+
}
|
839 |
+
|
840 |
+
$return = str_repeat($htab, $indent) . '<header>' . $crlf .
|
841 |
+
str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
|
842 |
+
str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
|
843 |
+
$params .
|
844 |
+
str_repeat($htab, $indent) . '</header>' . $crlf;
|
845 |
+
|
846 |
+
return $return;
|
847 |
+
}
|
848 |
+
|
849 |
+
} // End of class
|
lib/PEAR/Mail/mimePart.php
ADDED
@@ -0,0 +1,439 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The Mail_mimePart class is used to create MIME E-mail messages
|
4 |
+
*
|
5 |
+
* This class enables you to manipulate and build a mime email
|
6 |
+
* from the ground up. The Mail_Mime class is a userfriendly api
|
7 |
+
* to this class for people who aren't interested in the internals
|
8 |
+
* of mime mail.
|
9 |
+
* This class however allows full control over the email.
|
10 |
+
*
|
11 |
+
* Compatible with PHP versions 4 and 5
|
12 |
+
*
|
13 |
+
* LICENSE: This LICENSE is in the BSD license style.
|
14 |
+
* Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
|
15 |
+
* Copyright (c) 2003-2006, PEAR <pear-group@php.net>
|
16 |
+
* All rights reserved.
|
17 |
+
*
|
18 |
+
* Redistribution and use in source and binary forms, with or
|
19 |
+
* without modification, are permitted provided that the following
|
20 |
+
* conditions are met:
|
21 |
+
*
|
22 |
+
* - Redistributions of source code must retain the above copyright
|
23 |
+
* notice, this list of conditions and the following disclaimer.
|
24 |
+
* - Redistributions in binary form must reproduce the above copyright
|
25 |
+
* notice, this list of conditions and the following disclaimer in the
|
26 |
+
* documentation and/or other materials provided with the distribution.
|
27 |
+
* - Neither the name of the authors, nor the names of its contributors
|
28 |
+
* may be used to endorse or promote products derived from this
|
29 |
+
* software without specific prior written permission.
|
30 |
+
*
|
31 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
32 |
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
33 |
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
34 |
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
35 |
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
36 |
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
37 |
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
38 |
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
39 |
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
40 |
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
41 |
+
* THE POSSIBILITY OF SUCH DAMAGE.
|
42 |
+
*
|
43 |
+
* @category Mail
|
44 |
+
* @package Mail_Mime
|
45 |
+
* @author Richard Heyes <richard@phpguru.org>
|
46 |
+
* @author Cipriano Groenendal <cipri@php.net>
|
47 |
+
* @author Sean Coates <sean@php.net>
|
48 |
+
* @copyright 2003-2006 PEAR <pear-group@php.net>
|
49 |
+
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
50 |
+
* @version CVS: $Id: mimePart.php,v 1.25 2007/05/14 21:43:08 cipri Exp $
|
51 |
+
* @link http://pear.php.net/package/Mail_mime
|
52 |
+
*/
|
53 |
+
|
54 |
+
|
55 |
+
/**
|
56 |
+
* The Mail_mimePart class is used to create MIME E-mail messages
|
57 |
+
*
|
58 |
+
* This class enables you to manipulate and build a mime email
|
59 |
+
* from the ground up. The Mail_Mime class is a userfriendly api
|
60 |
+
* to this class for people who aren't interested in the internals
|
61 |
+
* of mime mail.
|
62 |
+
* This class however allows full control over the email.
|
63 |
+
*
|
64 |
+
* @category Mail
|
65 |
+
* @package Mail_Mime
|
66 |
+
* @author Richard Heyes <richard@phpguru.org>
|
67 |
+
* @author Cipriano Groenendal <cipri@php.net>
|
68 |
+
* @author Sean Coates <sean@php.net>
|
69 |
+
* @copyright 2003-2006 PEAR <pear-group@php.net>
|
70 |
+
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
71 |
+
* @version Release: @package_version@
|
72 |
+
* @link http://pear.php.net/package/Mail_mime
|
73 |
+
*/
|
74 |
+
class Mail_mimePart {
|
75 |
+
|
76 |
+
/**
|
77 |
+
* The encoding type of this part
|
78 |
+
*
|
79 |
+
* @var string
|
80 |
+
* @access private
|
81 |
+
*/
|
82 |
+
var $_encoding;
|
83 |
+
|
84 |
+
/**
|
85 |
+
* An array of subparts
|
86 |
+
*
|
87 |
+
* @var array
|
88 |
+
* @access private
|
89 |
+
*/
|
90 |
+
var $_subparts;
|
91 |
+
|
92 |
+
/**
|
93 |
+
* The output of this part after being built
|
94 |
+
*
|
95 |
+
* @var string
|
96 |
+
* @access private
|
97 |
+
*/
|
98 |
+
var $_encoded;
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Headers for this part
|
102 |
+
*
|
103 |
+
* @var array
|
104 |
+
* @access private
|
105 |
+
*/
|
106 |
+
var $_headers;
|
107 |
+
|
108 |
+
/**
|
109 |
+
* The body of this part (not encoded)
|
110 |
+
*
|
111 |
+
* @var string
|
112 |
+
* @access private
|
113 |
+
*/
|
114 |
+
var $_body;
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Constructor.
|
118 |
+
*
|
119 |
+
* Sets up the object.
|
120 |
+
*
|
121 |
+
* @param $body - The body of the mime part if any.
|
122 |
+
* @param $params - An associative array of parameters:
|
123 |
+
* content_type - The content type for this part eg multipart/mixed
|
124 |
+
* encoding - The encoding to use, 7bit, 8bit, base64, or quoted-printable
|
125 |
+
* cid - Content ID to apply
|
126 |
+
* disposition - Content disposition, inline or attachment
|
127 |
+
* dfilename - Optional filename parameter for content disposition
|
128 |
+
* description - Content description
|
129 |
+
* charset - Character set to use
|
130 |
+
* @access public
|
131 |
+
*/
|
132 |
+
function Mail_mimePart($body = '', $params = array())
|
133 |
+
{
|
134 |
+
if (!defined('MAIL_MIMEPART_CRLF')) {
|
135 |
+
define('MAIL_MIMEPART_CRLF', defined('MAIL_MIME_CRLF') ? MAIL_MIME_CRLF : "\r\n", TRUE);
|
136 |
+
}
|
137 |
+
|
138 |
+
$contentType = array();
|
139 |
+
$contentDisp = array();
|
140 |
+
foreach ($params as $key => $value) {
|
141 |
+
switch ($key) {
|
142 |
+
case 'content_type':
|
143 |
+
$contentType['type'] = $value;
|
144 |
+
//$headers['Content-Type'] = $value . (isset($charset) ? '; charset="' . $charset . '"' : '');
|
145 |
+
break;
|
146 |
+
|
147 |
+
case 'encoding':
|
148 |
+
$this->_encoding = $value;
|
149 |
+
$headers['Content-Transfer-Encoding'] = $value;
|
150 |
+
break;
|
151 |
+
|
152 |
+
case 'cid':
|
153 |
+
$headers['Content-ID'] = '<' . $value . '>';
|
154 |
+
break;
|
155 |
+
|
156 |
+
case 'disposition':
|
157 |
+
$contentDisp['disp'] = $value;
|
158 |
+
break;
|
159 |
+
|
160 |
+
case 'dfilename':
|
161 |
+
$contentDisp['filename'] = $value;
|
162 |
+
$contentType['name'] = $value;
|
163 |
+
break;
|
164 |
+
|
165 |
+
case 'description':
|
166 |
+
$headers['Content-Description'] = $value;
|
167 |
+
break;
|
168 |
+
|
169 |
+
case 'charset':
|
170 |
+
$contentType['charset'] = $value;
|
171 |
+
$contentDisp['charset'] = $value;
|
172 |
+
break;
|
173 |
+
|
174 |
+
case 'language':
|
175 |
+
$contentType['language'] = $value;
|
176 |
+
$contentDisp['language'] = $value;
|
177 |
+
break;
|
178 |
+
|
179 |
+
case 'location':
|
180 |
+
$headers['Content-Location'] = $value;
|
181 |
+
break;
|
182 |
+
|
183 |
+
}
|
184 |
+
}
|
185 |
+
if (isset($contentType['type'])) {
|
186 |
+
$headers['Content-Type'] = $contentType['type'];
|
187 |
+
if (isset($contentType['name'])) {
|
188 |
+
$headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF;
|
189 |
+
$headers['Content-Type'] .= $this->_buildHeaderParam('name', $contentType['name'],
|
190 |
+
isset($contentType['charset']) ? $contentType['charset'] : 'US-ASCII',
|
191 |
+
isset($contentType['language']) ? $contentType['language'] : NULL);
|
192 |
+
} elseif (isset($contentType['charset'])) {
|
193 |
+
$headers['Content-Type'] .= "; charset=\"{$contentType['charset']}\"";
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
|
198 |
+
if (isset($contentDisp['disp'])) {
|
199 |
+
$headers['Content-Disposition'] = $contentDisp['disp'];
|
200 |
+
if (isset($contentDisp['filename'])) {
|
201 |
+
$headers['Content-Disposition'] .= ';' . MAIL_MIMEPART_CRLF;
|
202 |
+
$headers['Content-Disposition'] .= $this->_buildHeaderParam('filename', $contentDisp['filename'],
|
203 |
+
isset($contentDisp['charset']) ? $contentDisp['charset'] : 'US-ASCII',
|
204 |
+
isset($contentDisp['language']) ? $contentDisp['language'] : NULL);
|
205 |
+
}
|
206 |
+
}
|
207 |
+
|
208 |
+
|
209 |
+
|
210 |
+
|
211 |
+
// Default content-type
|
212 |
+
if (!isset($headers['Content-Type'])) {
|
213 |
+
$headers['Content-Type'] = 'text/plain';
|
214 |
+
}
|
215 |
+
|
216 |
+
//Default encoding
|
217 |
+
if (!isset($this->_encoding)) {
|
218 |
+
$this->_encoding = '7bit';
|
219 |
+
}
|
220 |
+
|
221 |
+
// Assign stuff to member variables
|
222 |
+
$this->_encoded = array();
|
223 |
+
$this->_headers = $headers;
|
224 |
+
$this->_body = $body;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* encode()
|
229 |
+
*
|
230 |
+
* Encodes and returns the email. Also stores
|
231 |
+
* it in the encoded member variable
|
232 |
+
*
|
233 |
+
* @return An associative array containing two elements,
|
234 |
+
* body and headers. The headers element is itself
|
235 |
+
* an indexed array.
|
236 |
+
* @access public
|
237 |
+
*/
|
238 |
+
function encode()
|
239 |
+
{
|
240 |
+
$encoded =& $this->_encoded;
|
241 |
+
|
242 |
+
if (count($this->_subparts)) {
|
243 |
+
srand((double)microtime()*1000000);
|
244 |
+
$boundary = '=_' . md5(rand() . microtime());
|
245 |
+
$this->_headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF . "\t" . 'boundary="' . $boundary . '"';
|
246 |
+
|
247 |
+
// Add body parts to $subparts
|
248 |
+
for ($i = 0; $i < count($this->_subparts); $i++) {
|
249 |
+
$headers = array();
|
250 |
+
$tmp = $this->_subparts[$i]->encode();
|
251 |
+
foreach ($tmp['headers'] as $key => $value) {
|
252 |
+
$headers[] = $key . ': ' . $value;
|
253 |
+
}
|
254 |
+
$subparts[] = implode(MAIL_MIMEPART_CRLF, $headers) . MAIL_MIMEPART_CRLF . MAIL_MIMEPART_CRLF . $tmp['body'] . MAIL_MIMEPART_CRLF;
|
255 |
+
}
|
256 |
+
|
257 |
+
$encoded['body'] = '--' . $boundary . MAIL_MIMEPART_CRLF .
|
258 |
+
rtrim(implode('--' . $boundary . MAIL_MIMEPART_CRLF , $subparts), MAIL_MIMEPART_CRLF) . MAIL_MIMEPART_CRLF .
|
259 |
+
'--' . $boundary.'--' . MAIL_MIMEPART_CRLF;
|
260 |
+
|
261 |
+
} else {
|
262 |
+
$encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding);
|
263 |
+
}
|
264 |
+
|
265 |
+
// Add headers to $encoded
|
266 |
+
$encoded['headers'] =& $this->_headers;
|
267 |
+
|
268 |
+
return $encoded;
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* &addSubPart()
|
273 |
+
*
|
274 |
+
* Adds a subpart to current mime part and returns
|
275 |
+
* a reference to it
|
276 |
+
*
|
277 |
+
* @param $body The body of the subpart, if any.
|
278 |
+
* @param $params The parameters for the subpart, same
|
279 |
+
* as the $params argument for constructor.
|
280 |
+
* @return A reference to the part you just added. It is
|
281 |
+
* crucial if using multipart/* in your subparts that
|
282 |
+
* you use =& in your script when calling this function,
|
283 |
+
* otherwise you will not be able to add further subparts.
|
284 |
+
* @access public
|
285 |
+
*/
|
286 |
+
function &addSubPart($body, $params)
|
287 |
+
{
|
288 |
+
$this->_subparts[] = new Mail_mimePart($body, $params);
|
289 |
+
return $this->_subparts[count($this->_subparts) - 1];
|
290 |
+
}
|
291 |
+
|
292 |
+
/**
|
293 |
+
* _getEncodedData()
|
294 |
+
*
|
295 |
+
* Returns encoded data based upon encoding passed to it
|
296 |
+
*
|
297 |
+
* @param $data The data to encode.
|
298 |
+
* @param $encoding The encoding type to use, 7bit, base64,
|
299 |
+
* or quoted-printable.
|
300 |
+
* @access private
|
301 |
+
*/
|
302 |
+
function _getEncodedData($data, $encoding)
|
303 |
+
{
|
304 |
+
switch ($encoding) {
|
305 |
+
case '8bit':
|
306 |
+
case '7bit':
|
307 |
+
return $data;
|
308 |
+
break;
|
309 |
+
|
310 |
+
case 'quoted-printable':
|
311 |
+
return $this->_quotedPrintableEncode($data);
|
312 |
+
break;
|
313 |
+
|
314 |
+
case 'base64':
|
315 |
+
return rtrim(chunk_split(base64_encode($data), 76, MAIL_MIMEPART_CRLF));
|
316 |
+
break;
|
317 |
+
|
318 |
+
default:
|
319 |
+
return $data;
|
320 |
+
}
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* quotedPrintableEncode()
|
325 |
+
*
|
326 |
+
* Encodes data to quoted-printable standard.
|
327 |
+
*
|
328 |
+
* @param $input The data to encode
|
329 |
+
* @param $line_max Optional max line length. Should
|
330 |
+
* not be more than 76 chars
|
331 |
+
*
|
332 |
+
* @access private
|
333 |
+
*/
|
334 |
+
function _quotedPrintableEncode($input , $line_max = 76)
|
335 |
+
{
|
336 |
+
$lines = preg_split("/\r?\n/", $input);
|
337 |
+
$eol = MAIL_MIMEPART_CRLF;
|
338 |
+
$escape = '=';
|
339 |
+
$output = '';
|
340 |
+
|
341 |
+
while (list(, $line) = each($lines)) {
|
342 |
+
|
343 |
+
$line = preg_split('||', $line, -1, PREG_SPLIT_NO_EMPTY);
|
344 |
+
$linlen = count($line);
|
345 |
+
$newline = '';
|
346 |
+
|
347 |
+
for ($i = 0; $i < $linlen; $i++) {
|
348 |
+
$char = $line[$i];
|
349 |
+
$dec = ord($char);
|
350 |
+
|
351 |
+
if (($dec == 32) AND ($i == ($linlen - 1))) { // convert space at eol only
|
352 |
+
$char = '=20';
|
353 |
+
|
354 |
+
} elseif (($dec == 9) AND ($i == ($linlen - 1))) { // convert tab at eol only
|
355 |
+
$char = '=09';
|
356 |
+
} elseif ($dec == 9) {
|
357 |
+
; // Do nothing if a tab.
|
358 |
+
} elseif (($dec == 61) OR ($dec < 32 ) OR ($dec > 126)) {
|
359 |
+
$char = $escape . strtoupper(sprintf('%02s', dechex($dec)));
|
360 |
+
} elseif (($dec == 46) AND ($newline == '')) {
|
361 |
+
//Bug #9722: convert full-stop at bol
|
362 |
+
//Some Windows servers need this, won't break anything (cipri)
|
363 |
+
$char = '=2E';
|
364 |
+
}
|
365 |
+
|
366 |
+
if ((strlen($newline) + strlen($char)) >= $line_max) { // MAIL_MIMEPART_CRLF is not counted
|
367 |
+
$output .= $newline . $escape . $eol; // soft line break; " =\r\n" is okay
|
368 |
+
$newline = '';
|
369 |
+
}
|
370 |
+
$newline .= $char;
|
371 |
+
} // end of for
|
372 |
+
$output .= $newline . $eol;
|
373 |
+
}
|
374 |
+
$output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf
|
375 |
+
return $output;
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* _buildHeaderParam()
|
380 |
+
*
|
381 |
+
* Encodes the paramater of a header.
|
382 |
+
*
|
383 |
+
* @param $name The name of the header-parameter
|
384 |
+
* @param $value The value of the paramter
|
385 |
+
* @param $charset The characterset of $value
|
386 |
+
* @param $language The language used in $value
|
387 |
+
* @param $maxLength The maximum length of a line. Defauls to 75
|
388 |
+
*
|
389 |
+
* @access private
|
390 |
+
*/
|
391 |
+
function _buildHeaderParam($name, $value, $charset=NULL, $language=NULL, $maxLength=75)
|
392 |
+
{
|
393 |
+
//If we find chars to encode, or if charset or language
|
394 |
+
//is not any of the defaults, we need to encode the value.
|
395 |
+
$shouldEncode = 0;
|
396 |
+
$secondAsterisk = '';
|
397 |
+
if (preg_match('#([\x80-\xFF]){1}#', $value)) {
|
398 |
+
$shouldEncode = 1;
|
399 |
+
} elseif ($charset && (strtolower($charset) != 'us-ascii')) {
|
400 |
+
$shouldEncode = 1;
|
401 |
+
} elseif ($language && ($language != 'en' && $language != 'en-us')) {
|
402 |
+
$shouldEncode = 1;
|
403 |
+
}
|
404 |
+
if ($shouldEncode) {
|
405 |
+
$search = array('%', ' ', "\t");
|
406 |
+
$replace = array('%25', '%20', '%09');
|
407 |
+
$encValue = str_replace($search, $replace, $value);
|
408 |
+
$encValue = preg_replace('#([\x80-\xFF])#e', '"%" . strtoupper(dechex(ord("\1")))', $encValue);
|
409 |
+
$value = "$charset'$language'$encValue";
|
410 |
+
$secondAsterisk = '*';
|
411 |
+
}
|
412 |
+
$header = " {$name}{$secondAsterisk}=\"{$value}\"; ";
|
413 |
+
if (strlen($header) <= $maxLength) {
|
414 |
+
return $header;
|
415 |
+
}
|
416 |
+
|
417 |
+
$preLength = strlen(" {$name}*0{$secondAsterisk}=\"");
|
418 |
+
$sufLength = strlen("\";");
|
419 |
+
$maxLength = MAX(16, $maxLength - $preLength - $sufLength - 2);
|
420 |
+
$maxLengthReg = "|(.{0,$maxLength}[^\%][^\%])|";
|
421 |
+
|
422 |
+
$headers = array();
|
423 |
+
$headCount = 0;
|
424 |
+
while ($value) {
|
425 |
+
$matches = array();
|
426 |
+
$found = preg_match($maxLengthReg, $value, $matches);
|
427 |
+
if ($found) {
|
428 |
+
$headers[] = " {$name}*{$headCount}{$secondAsterisk}=\"{$matches[0]}\"";
|
429 |
+
$value = substr($value, strlen($matches[0]));
|
430 |
+
} else {
|
431 |
+
$headers[] = " {$name}*{$headCount}{$secondAsterisk}=\"{$value}\"";
|
432 |
+
$value = "";
|
433 |
+
}
|
434 |
+
$headCount++;
|
435 |
+
}
|
436 |
+
$headers = implode(MAIL_MIMEPART_CRLF, $headers) . ';';
|
437 |
+
return $headers;
|
438 |
+
}
|
439 |
+
} // End of class
|
lib/PEAR/Mail/xmail.dtd
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2 |
+
|
3 |
+
<!ENTITY lt "&#60;">
|
4 |
+
<!ENTITY gt ">">
|
5 |
+
<!ENTITY amp "&#38;">
|
6 |
+
<!ENTITY apos "'">
|
7 |
+
<!ENTITY quot """>
|
8 |
+
<!ENTITY crlf " ">
|
9 |
+
|
10 |
+
<!ELEMENT email (header+, (body | mimepart+))>
|
11 |
+
<!ELEMENT mimepart (header+, (body | mimepart+))>
|
12 |
+
<!ELEMENT body (#PCDATA)>
|
13 |
+
<!ELEMENT header ((headername|headervalue|parameter)*)>
|
14 |
+
<!ELEMENT headername (#PCDATA)>
|
15 |
+
<!ELEMENT headervalue (#PCDATA)>
|
16 |
+
<!ELEMENT parameter ((paramname|paramvalue)+)>
|
17 |
+
<!ELEMENT paramvalue (#PCDATA)>
|
18 |
+
<!ELEMENT paramname (#PCDATA)>
|
19 |
+
|
lib/PEAR/Mail/xmail.xsl
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2 |
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
|
3 |
+
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
|
4 |
+
<xsl:preserve-space elements="headervalue paramvalue body"/>
|
5 |
+
|
6 |
+
<xsl:template name="mimepart">
|
7 |
+
|
8 |
+
<xsl:variable name="boundary">
|
9 |
+
<xsl:for-each select="./header">
|
10 |
+
<xsl:if test="string(./headername) = 'Content-Type'">
|
11 |
+
<xsl:for-each select="./parameter">
|
12 |
+
<xsl:if test="string(./paramname) = 'boundary'">
|
13 |
+
<xsl:value-of select="paramvalue"/>
|
14 |
+
</xsl:if>
|
15 |
+
</xsl:for-each>
|
16 |
+
</xsl:if>
|
17 |
+
</xsl:for-each>
|
18 |
+
</xsl:variable>
|
19 |
+
|
20 |
+
<xsl:for-each select="header">
|
21 |
+
|
22 |
+
<xsl:value-of select="headername"/>
|
23 |
+
<xsl:text>: </xsl:text>
|
24 |
+
<xsl:value-of select="headervalue"/>
|
25 |
+
|
26 |
+
<xsl:if test="count(./parameter) = 0">
|
27 |
+
<xsl:text> </xsl:text>
|
28 |
+
</xsl:if>
|
29 |
+
|
30 |
+
<xsl:for-each select="parameter">
|
31 |
+
<xsl:text>; 	</xsl:text>
|
32 |
+
<xsl:value-of select="paramname"/>
|
33 |
+
<xsl:text>="</xsl:text>
|
34 |
+
<xsl:value-of select="paramvalue"/>
|
35 |
+
<xsl:text>"</xsl:text>
|
36 |
+
</xsl:for-each>
|
37 |
+
|
38 |
+
<xsl:if test="count(./parameter) > 0">
|
39 |
+
<xsl:text> </xsl:text>
|
40 |
+
</xsl:if>
|
41 |
+
|
42 |
+
</xsl:for-each>
|
43 |
+
|
44 |
+
<xsl:text> </xsl:text>
|
45 |
+
|
46 |
+
<!-- Which to do, print a body or process subparts? -->
|
47 |
+
<xsl:choose>
|
48 |
+
<xsl:when test="count(./mimepart) = 0">
|
49 |
+
<xsl:value-of select="body"/>
|
50 |
+
<xsl:text> </xsl:text>
|
51 |
+
</xsl:when>
|
52 |
+
|
53 |
+
<xsl:otherwise>
|
54 |
+
<xsl:for-each select="mimepart">
|
55 |
+
<xsl:text>--</xsl:text><xsl:value-of select="$boundary"/><xsl:text> </xsl:text>
|
56 |
+
<xsl:call-template name="mimepart"/>
|
57 |
+
</xsl:for-each>
|
58 |
+
|
59 |
+
<xsl:text>--</xsl:text><xsl:value-of select="$boundary"/><xsl:text>-- </xsl:text>
|
60 |
+
|
61 |
+
</xsl:otherwise>
|
62 |
+
</xsl:choose>
|
63 |
+
</xsl:template>
|
64 |
+
|
65 |
+
<!-- This is where the stylesheet really starts, matching the top level email element -->
|
66 |
+
<xsl:template match="email">
|
67 |
+
<xsl:call-template name="mimepart"/>
|
68 |
+
</xsl:template>
|
69 |
+
|
70 |
+
</xsl:stylesheet>
|
lib/PEAR/Net/Socket.php
ADDED
@@ -0,0 +1,592 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
//
|
3 |
+
// +----------------------------------------------------------------------+
|
4 |
+
// | PHP Version 4 |
|
5 |
+
// +----------------------------------------------------------------------+
|
6 |
+
// | Copyright (c) 1997-2003 The PHP Group |
|
7 |
+
// +----------------------------------------------------------------------+
|
8 |
+
// | This source file is subject to version 2.0 of the PHP license, |
|
9 |
+
// | that is bundled with this package in the file LICENSE, and is |
|
10 |
+
// | available at through the world-wide-web at |
|
11 |
+
// | http://www.php.net/license/2_02.txt. |
|
12 |
+
// | If you did not receive a copy of the PHP license and are unable to |
|
13 |
+
// | obtain it through the world-wide-web, please send a note to |
|
14 |
+
// | license@php.net so we can mail you a copy immediately. |
|
15 |
+
// +----------------------------------------------------------------------+
|
16 |
+
// | Authors: Stig Bakken <ssb@php.net> |
|
17 |
+
// | Chuck Hagenbuch <chuck@horde.org> |
|
18 |
+
// +----------------------------------------------------------------------+
|
19 |
+
//
|
20 |
+
// $Id: Socket.php,v 1.38 2008/02/15 18:24:17 chagenbu Exp $
|
21 |
+
|
22 |
+
require_once 'PEAR.php';
|
23 |
+
|
24 |
+
define('NET_SOCKET_READ', 1);
|
25 |
+
define('NET_SOCKET_WRITE', 2);
|
26 |
+
define('NET_SOCKET_ERROR', 4);
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Generalized Socket class.
|
30 |
+
*
|
31 |
+
* @version 1.1
|
32 |
+
* @author Stig Bakken <ssb@php.net>
|
33 |
+
* @author Chuck Hagenbuch <chuck@horde.org>
|
34 |
+
*/
|
35 |
+
class Net_Socket extends PEAR {
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Socket file pointer.
|
39 |
+
* @var resource $fp
|
40 |
+
*/
|
41 |
+
var $fp = null;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Whether the socket is blocking. Defaults to true.
|
45 |
+
* @var boolean $blocking
|
46 |
+
*/
|
47 |
+
var $blocking = true;
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Whether the socket is persistent. Defaults to false.
|
51 |
+
* @var boolean $persistent
|
52 |
+
*/
|
53 |
+
var $persistent = false;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* The IP address to connect to.
|
57 |
+
* @var string $addr
|
58 |
+
*/
|
59 |
+
var $addr = '';
|
60 |
+
|
61 |
+
/**
|
62 |
+
* The port number to connect to.
|
63 |
+
* @var integer $port
|
64 |
+
*/
|
65 |
+
var $port = 0;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Number of seconds to wait on socket connections before assuming
|
69 |
+
* there's no more data. Defaults to no timeout.
|
70 |
+
* @var integer $timeout
|
71 |
+
*/
|
72 |
+
var $timeout = false;
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Number of bytes to read at a time in readLine() and
|
76 |
+
* readAll(). Defaults to 2048.
|
77 |
+
* @var integer $lineLength
|
78 |
+
*/
|
79 |
+
var $lineLength = 2048;
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Connect to the specified port. If called when the socket is
|
83 |
+
* already connected, it disconnects and connects again.
|
84 |
+
*
|
85 |
+
* @param string $addr IP address or host name.
|
86 |
+
* @param integer $port TCP port number.
|
87 |
+
* @param boolean $persistent (optional) Whether the connection is
|
88 |
+
* persistent (kept open between requests
|
89 |
+
* by the web server).
|
90 |
+
* @param integer $timeout (optional) How long to wait for data.
|
91 |
+
* @param array $options See options for stream_context_create.
|
92 |
+
*
|
93 |
+
* @access public
|
94 |
+
*
|
95 |
+
* @return boolean | PEAR_Error True on success or a PEAR_Error on failure.
|
96 |
+
*/
|
97 |
+
function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)
|
98 |
+
{
|
99 |
+
if (is_resource($this->fp)) {
|
100 |
+
@fclose($this->fp);
|
101 |
+
$this->fp = null;
|
102 |
+
}
|
103 |
+
|
104 |
+
if (!$addr) {
|
105 |
+
return $this->raiseError('$addr cannot be empty');
|
106 |
+
} elseif (strspn($addr, '.0123456789') == strlen($addr) ||
|
107 |
+
strstr($addr, '/') !== false) {
|
108 |
+
$this->addr = $addr;
|
109 |
+
} else {
|
110 |
+
$this->addr = @gethostbyname($addr);
|
111 |
+
}
|
112 |
+
|
113 |
+
$this->port = $port % 65536;
|
114 |
+
|
115 |
+
if ($persistent !== null) {
|
116 |
+
$this->persistent = $persistent;
|
117 |
+
}
|
118 |
+
|
119 |
+
if ($timeout !== null) {
|
120 |
+
$this->timeout = $timeout;
|
121 |
+
}
|
122 |
+
|
123 |
+
$openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
|
124 |
+
$errno = 0;
|
125 |
+
$errstr = '';
|
126 |
+
$old_track_errors = @ini_set('track_errors', 1);
|
127 |
+
if ($options && function_exists('stream_context_create')) {
|
128 |
+
if ($this->timeout) {
|
129 |
+
$timeout = $this->timeout;
|
130 |
+
} else {
|
131 |
+
$timeout = 0;
|
132 |
+
}
|
133 |
+
$context = stream_context_create($options);
|
134 |
+
|
135 |
+
// Since PHP 5 fsockopen doesn't allow context specification
|
136 |
+
if (function_exists('stream_socket_client')) {
|
137 |
+
$flags = $this->persistent ? STREAM_CLIENT_PERSISTENT : STREAM_CLIENT_CONNECT;
|
138 |
+
$addr = $this->addr . ':' . $this->port;
|
139 |
+
$fp = stream_socket_client($addr, $errno, $errstr, $timeout, $flags, $context);
|
140 |
+
} else {
|
141 |
+
$fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
|
142 |
+
}
|
143 |
+
} else {
|
144 |
+
if ($this->timeout) {
|
145 |
+
$fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
|
146 |
+
} else {
|
147 |
+
$fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
|
148 |
+
}
|
149 |
+
}
|
150 |
+
|
151 |
+
if (!$fp) {
|
152 |
+
if ($errno == 0 && isset($php_errormsg)) {
|
153 |
+
$errstr = $php_errormsg;
|
154 |
+
}
|
155 |
+
@ini_set('track_errors', $old_track_errors);
|
156 |
+
return $this->raiseError($errstr, $errno);
|
157 |
+
}
|
158 |
+
|
159 |
+
@ini_set('track_errors', $old_track_errors);
|
160 |
+
$this->fp = $fp;
|
161 |
+
|
162 |
+
return $this->setBlocking($this->blocking);
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Disconnects from the peer, closes the socket.
|
167 |
+
*
|
168 |
+
* @access public
|
169 |
+
* @return mixed true on success or a PEAR_Error instance otherwise
|
170 |
+
*/
|
171 |
+
function disconnect()
|
172 |
+
{
|
173 |
+
if (!is_resource($this->fp)) {
|
174 |
+
return $this->raiseError('not connected');
|
175 |
+
}
|
176 |
+
|
177 |
+
@fclose($this->fp);
|
178 |
+
$this->fp = null;
|
179 |
+
return true;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Find out if the socket is in blocking mode.
|
184 |
+
*
|
185 |
+
* @access public
|
186 |
+
* @return boolean The current blocking mode.
|
187 |
+
*/
|
188 |
+
function isBlocking()
|
189 |
+
{
|
190 |
+
return $this->blocking;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Sets whether the socket connection should be blocking or
|
195 |
+
* not. A read call to a non-blocking socket will return immediately
|
196 |
+
* if there is no data available, whereas it will block until there
|
197 |
+
* is data for blocking sockets.
|
198 |
+
*
|
199 |
+
* @param boolean $mode True for blocking sockets, false for nonblocking.
|
200 |
+
* @access public
|
201 |
+
* @return mixed true on success or a PEAR_Error instance otherwise
|
202 |
+
*/
|
203 |
+
function setBlocking($mode)
|
204 |
+
{
|
205 |
+
if (!is_resource($this->fp)) {
|
206 |
+
return $this->raiseError('not connected');
|
207 |
+
}
|
208 |
+
|
209 |
+
$this->blocking = $mode;
|
210 |
+
socket_set_blocking($this->fp, $this->blocking);
|
211 |
+
return true;
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Sets the timeout value on socket descriptor,
|
216 |
+
* expressed in the sum of seconds and microseconds
|
217 |
+
*
|
218 |
+
* @param integer $seconds Seconds.
|
219 |
+
* @param integer $microseconds Microseconds.
|
220 |
+
* @access public
|
221 |
+
* @return mixed true on success or a PEAR_Error instance otherwise
|
222 |
+
*/
|
223 |
+
function setTimeout($seconds, $microseconds)
|
224 |
+
{
|
225 |
+
if (!is_resource($this->fp)) {
|
226 |
+
return $this->raiseError('not connected');
|
227 |
+
}
|
228 |
+
|
229 |
+
return socket_set_timeout($this->fp, $seconds, $microseconds);
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Sets the file buffering size on the stream.
|
234 |
+
* See php's stream_set_write_buffer for more information.
|
235 |
+
*
|
236 |
+
* @param integer $size Write buffer size.
|
237 |
+
* @access public
|
238 |
+
* @return mixed on success or an PEAR_Error object otherwise
|
239 |
+
*/
|
240 |
+
function setWriteBuffer($size)
|
241 |
+
{
|
242 |
+
if (!is_resource($this->fp)) {
|
243 |
+
return $this->raiseError('not connected');
|
244 |
+
}
|
245 |
+
|
246 |
+
$returned = stream_set_write_buffer($this->fp, $size);
|
247 |
+
if ($returned == 0) {
|
248 |
+
return true;
|
249 |
+
}
|
250 |
+
return $this->raiseError('Cannot set write buffer.');
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Returns information about an existing socket resource.
|
255 |
+
* Currently returns four entries in the result array:
|
256 |
+
*
|
257 |
+
* <p>
|
258 |
+
* timed_out (bool) - The socket timed out waiting for data<br>
|
259 |
+
* blocked (bool) - The socket was blocked<br>
|
260 |
+
* eof (bool) - Indicates EOF event<br>
|
261 |
+
* unread_bytes (int) - Number of bytes left in the socket buffer<br>
|
262 |
+
* </p>
|
263 |
+
*
|
264 |
+
* @access public
|
265 |
+
* @return mixed Array containing information about existing socket resource or a PEAR_Error instance otherwise
|
266 |
+
*/
|
267 |
+
function getStatus()
|
268 |
+
{
|
269 |
+
if (!is_resource($this->fp)) {
|
270 |
+
return $this->raiseError('not connected');
|
271 |
+
}
|
272 |
+
|
273 |
+
return socket_get_status($this->fp);
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* Get a specified line of data
|
278 |
+
*
|
279 |
+
* @access public
|
280 |
+
* @return $size bytes of data from the socket, or a PEAR_Error if
|
281 |
+
* not connected.
|
282 |
+
*/
|
283 |
+
function gets($size)
|
284 |
+
{
|
285 |
+
if (!is_resource($this->fp)) {
|
286 |
+
return $this->raiseError('not connected');
|
287 |
+
}
|
288 |
+
|
289 |
+
return @fgets($this->fp, $size);
|
290 |
+
}
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Read a specified amount of data. This is guaranteed to return,
|
294 |
+
* and has the added benefit of getting everything in one fread()
|
295 |
+
* chunk; if you know the size of the data you're getting
|
296 |
+
* beforehand, this is definitely the way to go.
|
297 |
+
*
|
298 |
+
* @param integer $size The number of bytes to read from the socket.
|
299 |
+
* @access public
|
300 |
+
* @return $size bytes of data from the socket, or a PEAR_Error if
|
301 |
+
* not connected.
|
302 |
+
*/
|
303 |
+
function read($size)
|
304 |
+
{
|
305 |
+
if (!is_resource($this->fp)) {
|
306 |
+
return $this->raiseError('not connected');
|
307 |
+
}
|
308 |
+
|
309 |
+
return @fread($this->fp, $size);
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Write a specified amount of data.
|
314 |
+
*
|
315 |
+
* @param string $data Data to write.
|
316 |
+
* @param integer $blocksize Amount of data to write at once.
|
317 |
+
* NULL means all at once.
|
318 |
+
*
|
319 |
+
* @access public
|
320 |
+
* @return mixed If the socket is not connected, returns an instance of PEAR_Error
|
321 |
+
* If the write succeeds, returns the number of bytes written
|
322 |
+
* If the write fails, returns false.
|
323 |
+
*/
|
324 |
+
function write($data, $blocksize = null)
|
325 |
+
{
|
326 |
+
if (!is_resource($this->fp)) {
|
327 |
+
return $this->raiseError('not connected');
|
328 |
+
}
|
329 |
+
|
330 |
+
if (is_null($blocksize) && !OS_WINDOWS) {
|
331 |
+
return @fwrite($this->fp, $data);
|
332 |
+
} else {
|
333 |
+
if (is_null($blocksize)) {
|
334 |
+
$blocksize = 1024;
|
335 |
+
}
|
336 |
+
|
337 |
+
$pos = 0;
|
338 |
+
$size = strlen($data);
|
339 |
+
while ($pos < $size) {
|
340 |
+
$written = @fwrite($this->fp, substr($data, $pos, $blocksize));
|
341 |
+
if ($written === false) {
|
342 |
+
return false;
|
343 |
+
}
|
344 |
+
$pos += $written;
|
345 |
+
}
|
346 |
+
|
347 |
+
return $pos;
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Write a line of data to the socket, followed by a trailing "\r\n".
|
353 |
+
*
|
354 |
+
* @access public
|
355 |
+
* @return mixed fputs result, or an error
|
356 |
+
*/
|
357 |
+
function writeLine($data)
|
358 |
+
{
|
359 |
+
if (!is_resource($this->fp)) {
|
360 |
+
return $this->raiseError('not connected');
|
361 |
+
}
|
362 |
+
|
363 |
+
return fwrite($this->fp, $data . "\r\n");
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* Tests for end-of-file on a socket descriptor.
|
368 |
+
*
|
369 |
+
* Also returns true if the socket is disconnected.
|
370 |
+
*
|
371 |
+
* @access public
|
372 |
+
* @return bool
|
373 |
+
*/
|
374 |
+
function eof()
|
375 |
+
{
|
376 |
+
return (!is_resource($this->fp) || feof($this->fp));
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Reads a byte of data
|
381 |
+
*
|
382 |
+
* @access public
|
383 |
+
* @return 1 byte of data from the socket, or a PEAR_Error if
|
384 |
+
* not connected.
|
385 |
+
*/
|
386 |
+
function readByte()
|
387 |
+
{
|
388 |
+
if (!is_resource($this->fp)) {
|
389 |
+
return $this->raiseError('not connected');
|
390 |
+
}
|
391 |
+
|
392 |
+
return ord(@fread($this->fp, 1));
|
393 |
+
}
|
394 |
+
|
395 |
+
/**
|
396 |
+
* Reads a word of data
|
397 |
+
*
|
398 |
+
* @access public
|
399 |
+
* @return 1 word of data from the socket, or a PEAR_Error if
|
400 |
+
* not connected.
|
401 |
+
*/
|
402 |
+
function readWord()
|
403 |
+
{
|
404 |
+
if (!is_resource($this->fp)) {
|
405 |
+
return $this->raiseError('not connected');
|
406 |
+
}
|
407 |
+
|
408 |
+
$buf = @fread($this->fp, 2);
|
409 |
+
return (ord($buf[0]) + (ord($buf[1]) << 8));
|
410 |
+
}
|
411 |
+
|
412 |
+
/**
|
413 |
+
* Reads an int of data
|
414 |
+
*
|
415 |
+
* @access public
|
416 |
+
* @return integer 1 int of data from the socket, or a PEAR_Error if
|
417 |
+
* not connected.
|
418 |
+
*/
|
419 |
+
function readInt()
|
420 |
+
{
|
421 |
+
if (!is_resource($this->fp)) {
|
422 |
+
return $this->raiseError('not connected');
|
423 |
+
}
|
424 |
+
|
425 |
+
$buf = @fread($this->fp, 4);
|
426 |
+
return (ord($buf[0]) + (ord($buf[1]) << 8) +
|
427 |
+
(ord($buf[2]) << 16) + (ord($buf[3]) << 24));
|
428 |
+
}
|
429 |
+
|
430 |
+
/**
|
431 |
+
* Reads a zero-terminated string of data
|
432 |
+
*
|
433 |
+
* @access public
|
434 |
+
* @return string, or a PEAR_Error if
|
435 |
+
* not connected.
|
436 |
+
*/
|
437 |
+
function readString()
|
438 |
+
{
|
439 |
+
if (!is_resource($this->fp)) {
|
440 |
+
return $this->raiseError('not connected');
|
441 |
+
}
|
442 |
+
|
443 |
+
$string = '';
|
444 |
+
while (($char = @fread($this->fp, 1)) != "\x00") {
|
445 |
+
$string .= $char;
|
446 |
+
}
|
447 |
+
return $string;
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* Reads an IP Address and returns it in a dot formatted string
|
452 |
+
*
|
453 |
+
* @access public
|
454 |
+
* @return Dot formatted string, or a PEAR_Error if
|
455 |
+
* not connected.
|
456 |
+
*/
|
457 |
+
function readIPAddress()
|
458 |
+
{
|
459 |
+
if (!is_resource($this->fp)) {
|
460 |
+
return $this->raiseError('not connected');
|
461 |
+
}
|
462 |
+
|
463 |
+
$buf = @fread($this->fp, 4);
|
464 |
+
return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]),
|
465 |
+
ord($buf[2]), ord($buf[3]));
|
466 |
+
}
|
467 |
+
|
468 |
+
/**
|
469 |
+
* Read until either the end of the socket or a newline, whichever
|
470 |
+
* comes first. Strips the trailing newline from the returned data.
|
471 |
+
*
|
472 |
+
* @access public
|
473 |
+
* @return All available data up to a newline, without that
|
474 |
+
* newline, or until the end of the socket, or a PEAR_Error if
|
475 |
+
* not connected.
|
476 |
+
*/
|
477 |
+
function readLine()
|
478 |
+
{
|
479 |
+
if (!is_resource($this->fp)) {
|
480 |
+
return $this->raiseError('not connected');
|
481 |
+
}
|
482 |
+
|
483 |
+
$line = '';
|
484 |
+
$timeout = time() + $this->timeout;
|
485 |
+
while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
|
486 |
+
$line .= @fgets($this->fp, $this->lineLength);
|
487 |
+
if (substr($line, -1) == "\n") {
|
488 |
+
return rtrim($line, "\r\n");
|
489 |
+
}
|
490 |
+
}
|
491 |
+
return $line;
|
492 |
+
}
|
493 |
+
|
494 |
+
/**
|
495 |
+
* Read until the socket closes, or until there is no more data in
|
496 |
+
* the inner PHP buffer. If the inner buffer is empty, in blocking
|
497 |
+
* mode we wait for at least 1 byte of data. Therefore, in
|
498 |
+
* blocking mode, if there is no data at all to be read, this
|
499 |
+
* function will never exit (unless the socket is closed on the
|
500 |
+
* remote end).
|
501 |
+
*
|
502 |
+
* @access public
|
503 |
+
*
|
504 |
+
* @return string All data until the socket closes, or a PEAR_Error if
|
505 |
+
* not connected.
|
506 |
+
*/
|
507 |
+
function readAll()
|
508 |
+
{
|
509 |
+
if (!is_resource($this->fp)) {
|
510 |
+
return $this->raiseError('not connected');
|
511 |
+
}
|
512 |
+
|
513 |
+
$data = '';
|
514 |
+
while (!feof($this->fp)) {
|
515 |
+
$data .= @fread($this->fp, $this->lineLength);
|
516 |
+
}
|
517 |
+
return $data;
|
518 |
+
}
|
519 |
+
|
520 |
+
/**
|
521 |
+
* Runs the equivalent of the select() system call on the socket
|
522 |
+
* with a timeout specified by tv_sec and tv_usec.
|
523 |
+
*
|
524 |
+
* @param integer $state Which of read/write/error to check for.
|
525 |
+
* @param integer $tv_sec Number of seconds for timeout.
|
526 |
+
* @param integer $tv_usec Number of microseconds for timeout.
|
527 |
+
*
|
528 |
+
* @access public
|
529 |
+
* @return False if select fails, integer describing which of read/write/error
|
530 |
+
* are ready, or PEAR_Error if not connected.
|
531 |
+
*/
|
532 |
+
function select($state, $tv_sec, $tv_usec = 0)
|
533 |
+
{
|
534 |
+
if (!is_resource($this->fp)) {
|
535 |
+
return $this->raiseError('not connected');
|
536 |
+
}
|
537 |
+
|
538 |
+
$read = null;
|
539 |
+
$write = null;
|
540 |
+
$except = null;
|
541 |
+
if ($state & NET_SOCKET_READ) {
|
542 |
+
$read[] = $this->fp;
|
543 |
+
}
|
544 |
+
if ($state & NET_SOCKET_WRITE) {
|
545 |
+
$write[] = $this->fp;
|
546 |
+
}
|
547 |
+
if ($state & NET_SOCKET_ERROR) {
|
548 |
+
$except[] = $this->fp;
|
549 |
+
}
|
550 |
+
if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {
|
551 |
+
return false;
|
552 |
+
}
|
553 |
+
|
554 |
+
$result = 0;
|
555 |
+
if (count($read)) {
|
556 |
+
$result |= NET_SOCKET_READ;
|
557 |
+
}
|
558 |
+
if (count($write)) {
|
559 |
+
$result |= NET_SOCKET_WRITE;
|
560 |
+
}
|
561 |
+
if (count($except)) {
|
562 |
+
$result |= NET_SOCKET_ERROR;
|
563 |
+
}
|
564 |
+
return $result;
|
565 |
+
}
|
566 |
+
|
567 |
+
/**
|
568 |
+
* Turns encryption on/off on a connected socket.
|
569 |
+
*
|
570 |
+
* @param bool $enabled Set this parameter to true to enable encryption
|
571 |
+
* and false to disable encryption.
|
572 |
+
* @param integer $type Type of encryption. See
|
573 |
+
* http://se.php.net/manual/en/function.stream-socket-enable-crypto.php for values.
|
574 |
+
*
|
575 |
+
* @access public
|
576 |
+
* @return false on error, true on success and 0 if there isn't enough data and the
|
577 |
+
* user should try again (non-blocking sockets only). A PEAR_Error object
|
578 |
+
* is returned if the socket is not connected
|
579 |
+
*/
|
580 |
+
function enableCrypto($enabled, $type)
|
581 |
+
{
|
582 |
+
if (version_compare(phpversion(), "5.1.0", ">=")) {
|
583 |
+
if (!is_resource($this->fp)) {
|
584 |
+
return $this->raiseError('not connected');
|
585 |
+
}
|
586 |
+
return @stream_socket_enable_crypto($this->fp, $enabled, $type);
|
587 |
+
} else {
|
588 |
+
return $this->raiseError('Net_Socket::enableCrypto() requires php version >= 5.1.0');
|
589 |
+
}
|
590 |
+
}
|
591 |
+
|
592 |
+
}
|
lib/PEAR/Net/URL.php
ADDED
@@ -0,0 +1,485 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// +-----------------------------------------------------------------------+
|
3 |
+
// | Copyright (c) 2002-2004, Richard Heyes |
|
4 |
+
// | All rights reserved. |
|
5 |
+
// | |
|
6 |
+
// | Redistribution and use in source and binary forms, with or without |
|
7 |
+
// | modification, are permitted provided that the following conditions |
|
8 |
+
// | are met: |
|
9 |
+
// | |
|
10 |
+
// | o Redistributions of source code must retain the above copyright |
|
11 |
+
// | notice, this list of conditions and the following disclaimer. |
|
12 |
+
// | o Redistributions in binary form must reproduce the above copyright |
|
13 |
+
// | notice, this list of conditions and the following disclaimer in the |
|
14 |
+
// | documentation and/or other materials provided with the distribution.|
|
15 |
+
// | o The names of the authors may not be used to endorse or promote |
|
16 |
+
// | products derived from this software without specific prior written |
|
17 |
+
// | permission. |
|
18 |
+
// | |
|
19 |
+
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
20 |
+
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
21 |
+
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
22 |
+
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
23 |
+
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
24 |
+
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
25 |
+
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
26 |
+
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
27 |
+
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
28 |
+
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
29 |
+
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 |
+
// | |
|
31 |
+
// +-----------------------------------------------------------------------+
|
32 |
+
// | Author: Richard Heyes <richard at php net> |
|
33 |
+
// +-----------------------------------------------------------------------+
|
34 |
+
//
|
35 |
+
// $Id: URL.php,v 1.49 2007/06/28 14:43:07 davidc Exp $
|
36 |
+
//
|
37 |
+
// Net_URL Class
|
38 |
+
|
39 |
+
|
40 |
+
class Net_URL
|
41 |
+
{
|
42 |
+
var $options = array('encode_query_keys' => false);
|
43 |
+
/**
|
44 |
+
* Full url
|
45 |
+
* @var string
|
46 |
+
*/
|
47 |
+
var $url;
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Protocol
|
51 |
+
* @var string
|
52 |
+
*/
|
53 |
+
var $protocol;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Username
|
57 |
+
* @var string
|
58 |
+
*/
|
59 |
+
var $username;
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Password
|
63 |
+
* @var string
|
64 |
+
*/
|
65 |
+
var $password;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Host
|
69 |
+
* @var string
|
70 |
+
*/
|
71 |
+
var $host;
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Port
|
75 |
+
* @var integer
|
76 |
+
*/
|
77 |
+
var $port;
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Path
|
81 |
+
* @var string
|
82 |
+
*/
|
83 |
+
var $path;
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Query string
|
87 |
+
* @var array
|
88 |
+
*/
|
89 |
+
var $querystring;
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Anchor
|
93 |
+
* @var string
|
94 |
+
*/
|
95 |
+
var $anchor;
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Whether to use []
|
99 |
+
* @var bool
|
100 |
+
*/
|
101 |
+
var $useBrackets;
|
102 |
+
|
103 |
+
/**
|
104 |
+
* PHP4 Constructor
|
105 |
+
*
|
106 |
+
* @see __construct()
|
107 |
+
*/
|
108 |
+
// function Net_URL($url = null, $useBrackets = true)
|
109 |
+
// {
|
110 |
+
// $this->__construct($url, $useBrackets);
|
111 |
+
// }
|
112 |
+
|
113 |
+
/**
|
114 |
+
* PHP5 Constructor
|
115 |
+
*
|
116 |
+
* Parses the given url and stores the various parts
|
117 |
+
* Defaults are used in certain cases
|
118 |
+
*
|
119 |
+
* @param string $url Optional URL
|
120 |
+
* @param bool $useBrackets Whether to use square brackets when
|
121 |
+
* multiple querystrings with the same name
|
122 |
+
* exist
|
123 |
+
*/
|
124 |
+
function __construct($url = null, $useBrackets = true)
|
125 |
+
{
|
126 |
+
$this->url = $url;
|
127 |
+
$this->useBrackets = $useBrackets;
|
128 |
+
|
129 |
+
$this->initialize();
|
130 |
+
}
|
131 |
+
|
132 |
+
function initialize()
|
133 |
+
{
|
134 |
+
$HTTP_SERVER_VARS = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
|
135 |
+
|
136 |
+
$this->user = '';
|
137 |
+
$this->pass = '';
|
138 |
+
$this->host = '';
|
139 |
+
$this->port = 80;
|
140 |
+
$this->path = '';
|
141 |
+
$this->querystring = array();
|
142 |
+
$this->anchor = '';
|
143 |
+
|
144 |
+
// Only use defaults if not an absolute URL given
|
145 |
+
if (!preg_match('/^[a-z0-9]+:\/\//i', $this->url)) {
|
146 |
+
$this->protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https' : 'http');
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Figure out host/port
|
150 |
+
*/
|
151 |
+
if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) &&
|
152 |
+
preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches))
|
153 |
+
{
|
154 |
+
$host = $matches[1];
|
155 |
+
if (!empty($matches[3])) {
|
156 |
+
$port = $matches[3];
|
157 |
+
} else {
|
158 |
+
$port = $this->getStandardPort($this->protocol);
|
159 |
+
}
|
160 |
+
}
|
161 |
+
|
162 |
+
$this->user = '';
|
163 |
+
$this->pass = '';
|
164 |
+
$this->host = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
|
165 |
+
$this->port = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
|
166 |
+
$this->path = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
|
167 |
+
$this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
|
168 |
+
$this->anchor = '';
|
169 |
+
}
|
170 |
+
|
171 |
+
// Parse the url and store the various parts
|
172 |
+
if (!empty($this->url)) {
|
173 |
+
$urlinfo = parse_url($this->url);
|
174 |
+
|
175 |
+
// Default querystring
|
176 |
+
$this->querystring = array();
|
177 |
+
|
178 |
+
foreach ($urlinfo as $key => $value) {
|
179 |
+
switch ($key) {
|
180 |
+
case 'scheme':
|
181 |
+
$this->protocol = $value;
|
182 |
+
$this->port = $this->getStandardPort($value);
|
183 |
+
break;
|
184 |
+
|
185 |
+
case 'user':
|
186 |
+
case 'pass':
|
187 |
+
case 'host':
|
188 |
+
case 'port':
|
189 |
+
$this->$key = $value;
|
190 |
+
break;
|
191 |
+
|
192 |
+
case 'path':
|
193 |
+
if ($value{0} == '/') {
|
194 |
+
$this->path = $value;
|
195 |
+
} else {
|
196 |
+
$path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
|
197 |
+
$this->path = sprintf('%s/%s', $path, $value);
|
198 |
+
}
|
199 |
+
break;
|
200 |
+
|
201 |
+
case 'query':
|
202 |
+
$this->querystring = $this->_parseRawQueryString($value);
|
203 |
+
break;
|
204 |
+
|
205 |
+
case 'fragment':
|
206 |
+
$this->anchor = $value;
|
207 |
+
break;
|
208 |
+
}
|
209 |
+
}
|
210 |
+
}
|
211 |
+
}
|
212 |
+
/**
|
213 |
+
* Returns full url
|
214 |
+
*
|
215 |
+
* @return string Full url
|
216 |
+
* @access public
|
217 |
+
*/
|
218 |
+
function getURL()
|
219 |
+
{
|
220 |
+
$querystring = $this->getQueryString();
|
221 |
+
|
222 |
+
$this->url = $this->protocol . '://'
|
223 |
+
. $this->user . (!empty($this->pass) ? ':' : '')
|
224 |
+
. $this->pass . (!empty($this->user) ? '@' : '')
|
225 |
+
. $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
|
226 |
+
. $this->path
|
227 |
+
. (!empty($querystring) ? '?' . $querystring : '')
|
228 |
+
. (!empty($this->anchor) ? '#' . $this->anchor : '');
|
229 |
+
|
230 |
+
return $this->url;
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Adds or updates a querystring item (URL parameter).
|
235 |
+
* Automatically encodes parameters with rawurlencode() if $preencoded
|
236 |
+
* is false.
|
237 |
+
* You can pass an array to $value, it gets mapped via [] in the URL if
|
238 |
+
* $this->useBrackets is activated.
|
239 |
+
*
|
240 |
+
* @param string $name Name of item
|
241 |
+
* @param string $value Value of item
|
242 |
+
* @param bool $preencoded Whether value is urlencoded or not, default = not
|
243 |
+
* @access public
|
244 |
+
*/
|
245 |
+
function addQueryString($name, $value, $preencoded = false)
|
246 |
+
{
|
247 |
+
if ($this->getOption('encode_query_keys')) {
|
248 |
+
$name = rawurlencode($name);
|
249 |
+
}
|
250 |
+
|
251 |
+
if ($preencoded) {
|
252 |
+
$this->querystring[$name] = $value;
|
253 |
+
} else {
|
254 |
+
$this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* Removes a querystring item
|
260 |
+
*
|
261 |
+
* @param string $name Name of item
|
262 |
+
* @access public
|
263 |
+
*/
|
264 |
+
function removeQueryString($name)
|
265 |
+
{
|
266 |
+
if ($this->getOption('encode_query_keys')) {
|
267 |
+
$name = rawurlencode($name);
|
268 |
+
}
|
269 |
+
|
270 |
+
if (isset($this->querystring[$name])) {
|
271 |
+
unset($this->querystring[$name]);
|
272 |
+
}
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Sets the querystring to literally what you supply
|
277 |
+
*
|
278 |
+
* @param string $querystring The querystring data. Should be of the format foo=bar&x=y etc
|
279 |
+
* @access public
|
280 |
+
*/
|
281 |
+
function addRawQueryString($querystring)
|
282 |
+
{
|
283 |
+
$this->querystring = $this->_parseRawQueryString($querystring);
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Returns flat querystring
|
288 |
+
*
|
289 |
+
* @return string Querystring
|
290 |
+
* @access public
|
291 |
+
*/
|
292 |
+
function getQueryString()
|
293 |
+
{
|
294 |
+
if (!empty($this->querystring)) {
|
295 |
+
foreach ($this->querystring as $name => $value) {
|
296 |
+
// Encode var name
|
297 |
+
$name = rawurlencode($name);
|
298 |
+
|
299 |
+
if (is_array($value)) {
|
300 |
+
foreach ($value as $k => $v) {
|
301 |
+
$querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
|
302 |
+
}
|
303 |
+
} elseif (!is_null($value)) {
|
304 |
+
$querystring[] = $name . '=' . $value;
|
305 |
+
} else {
|
306 |
+
$querystring[] = $name;
|
307 |
+
}
|
308 |
+
}
|
309 |
+
$querystring = implode(ini_get('arg_separator.output'), $querystring);
|
310 |
+
} else {
|
311 |
+
$querystring = '';
|
312 |
+
}
|
313 |
+
|
314 |
+
return $querystring;
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* Parses raw querystring and returns an array of it
|
319 |
+
*
|
320 |
+
* @param string $querystring The querystring to parse
|
321 |
+
* @return array An array of the querystring data
|
322 |
+
* @access private
|
323 |
+
*/
|
324 |
+
function _parseRawQuerystring($querystring)
|
325 |
+
{
|
326 |
+
$parts = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
|
327 |
+
$return = array();
|
328 |
+
|
329 |
+
foreach ($parts as $part) {
|
330 |
+
if (strpos($part, '=') !== false) {
|
331 |
+
$value = substr($part, strpos($part, '=') + 1);
|
332 |
+
$key = substr($part, 0, strpos($part, '='));
|
333 |
+
} else {
|
334 |
+
$value = null;
|
335 |
+
$key = $part;
|
336 |
+
}
|
337 |
+
|
338 |
+
if (!$this->getOption('encode_query_keys')) {
|
339 |
+
$key = rawurldecode($key);
|
340 |
+
}
|
341 |
+
|
342 |
+
if (preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) {
|
343 |
+
$key = $matches[1];
|
344 |
+
$idx = $matches[2];
|
345 |
+
|
346 |
+
// Ensure is an array
|
347 |
+
if (empty($return[$key]) || !is_array($return[$key])) {
|
348 |
+
$return[$key] = array();
|
349 |
+
}
|
350 |
+
|
351 |
+
// Add data
|
352 |
+
if ($idx === '') {
|
353 |
+
$return[$key][] = $value;
|
354 |
+
} else {
|
355 |
+
$return[$key][$idx] = $value;
|
356 |
+
}
|
357 |
+
} elseif (!$this->useBrackets AND !empty($return[$key])) {
|
358 |
+
$return[$key] = (array)$return[$key];
|
359 |
+
$return[$key][] = $value;
|
360 |
+
} else {
|
361 |
+
$return[$key] = $value;
|
362 |
+
}
|
363 |
+
}
|
364 |
+
|
365 |
+
return $return;
|
366 |
+
}
|
367 |
+
|
368 |
+
/**
|
369 |
+
* Resolves //, ../ and ./ from a path and returns
|
370 |
+
* the result. Eg:
|
371 |
+
*
|
372 |
+
* /foo/bar/../boo.php => /foo/boo.php
|
373 |
+
* /foo/bar/../../boo.php => /boo.php
|
374 |
+
* /foo/bar/.././/boo.php => /foo/boo.php
|
375 |
+
*
|
376 |
+
* This method can also be called statically.
|
377 |
+
*
|
378 |
+
* @param string $path URL path to resolve
|
379 |
+
* @return string The result
|
380 |
+
*/
|
381 |
+
function resolvePath($path)
|
382 |
+
{
|
383 |
+
$path = explode('/', str_replace('//', '/', $path));
|
384 |
+
|
385 |
+
for ($i=0; $i<count($path); $i++) {
|
386 |
+
if ($path[$i] == '.') {
|
387 |
+
unset($path[$i]);
|
388 |
+
$path = array_values($path);
|
389 |
+
$i--;
|
390 |
+
|
391 |
+
} elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {
|
392 |
+
unset($path[$i]);
|
393 |
+
unset($path[$i-1]);
|
394 |
+
$path = array_values($path);
|
395 |
+
$i -= 2;
|
396 |
+
|
397 |
+
} elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
|
398 |
+
unset($path[$i]);
|
399 |
+
$path = array_values($path);
|
400 |
+
$i--;
|
401 |
+
|
402 |
+
} else {
|
403 |
+
continue;
|
404 |
+
}
|
405 |
+
}
|
406 |
+
|
407 |
+
return implode('/', $path);
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Returns the standard port number for a protocol
|
412 |
+
*
|
413 |
+
* @param string $scheme The protocol to lookup
|
414 |
+
* @return integer Port number or NULL if no scheme matches
|
415 |
+
*
|
416 |
+
* @author Philippe Jausions <Philippe.Jausions@11abacus.com>
|
417 |
+
*/
|
418 |
+
function getStandardPort($scheme)
|
419 |
+
{
|
420 |
+
switch (strtolower($scheme)) {
|
421 |
+
case 'http': return 80;
|
422 |
+
case 'https': return 443;
|
423 |
+
case 'ftp': return 21;
|
424 |
+
case 'imap': return 143;
|
425 |
+
case 'imaps': return 993;
|
426 |
+
case 'pop3': return 110;
|
427 |
+
case 'pop3s': return 995;
|
428 |
+
default: return null;
|
429 |
+
}
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* Forces the URL to a particular protocol
|
434 |
+
*
|
435 |
+
* @param string $protocol Protocol to force the URL to
|
436 |
+
* @param integer $port Optional port (standard port is used by default)
|
437 |
+
*/
|
438 |
+
function setProtocol($protocol, $port = null)
|
439 |
+
{
|
440 |
+
$this->protocol = $protocol;
|
441 |
+
$this->port = is_null($port) ? $this->getStandardPort($protocol) : $port;
|
442 |
+
}
|
443 |
+
|
444 |
+
/**
|
445 |
+
* Set an option
|
446 |
+
*
|
447 |
+
* This function set an option
|
448 |
+
* to be used thorough the script.
|
449 |
+
*
|
450 |
+
* @access public
|
451 |
+
* @param string $optionName The optionname to set
|
452 |
+
* @param string $value The value of this option.
|
453 |
+
*/
|
454 |
+
function setOption($optionName, $value)
|
455 |
+
{
|
456 |
+
if (!array_key_exists($optionName, $this->options)) {
|
457 |
+
return false;
|
458 |
+
}
|
459 |
+
|
460 |
+
$this->options[$optionName] = $value;
|
461 |
+
$this->initialize();
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Get an option
|
466 |
+
*
|
467 |
+
* This function gets an option
|
468 |
+
* from the $this->options array
|
469 |
+
* and return it's value.
|
470 |
+
*
|
471 |
+
* @access public
|
472 |
+
* @param string $opionName The name of the option to retrieve
|
473 |
+
* @see $this->options
|
474 |
+
*/
|
475 |
+
function getOption($optionName)
|
476 |
+
{
|
477 |
+
if (!isset($this->options[$optionName])) {
|
478 |
+
return false;
|
479 |
+
}
|
480 |
+
|
481 |
+
return $this->options[$optionName];
|
482 |
+
}
|
483 |
+
|
484 |
+
}
|
485 |
+
?>
|
lib/PEAR/PEAR.php
ADDED
@@ -0,0 +1,1118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* PEAR, the PHP Extension and Application Repository
|
4 |
+
*
|
5 |
+
* PEAR class and PEAR_Error class
|
6 |
+
*
|
7 |
+
* PHP versions 4 and 5
|
8 |
+
*
|
9 |
+
* LICENSE: This source file is subject to version 3.0 of the PHP license
|
10 |
+
* that is available through the world-wide-web at the following URI:
|
11 |
+
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
|
12 |
+
* the PHP License and are unable to obtain it through the web, please
|
13 |
+
* send a note to license@php.net so we can mail you a copy immediately.
|
14 |
+
*
|
15 |
+
* @category pear
|
16 |
+
* @package PEAR
|
17 |
+
* @author Sterling Hughes <sterling@php.net>
|
18 |
+
* @author Stig Bakken <ssb@php.net>
|
19 |
+
* @author Tomas V.V.Cox <cox@idecnet.com>
|
20 |
+
* @author Greg Beaver <cellog@php.net>
|
21 |
+
* @copyright 1997-2008 The PHP Group
|
22 |
+
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
23 |
+
* @version CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
|
24 |
+
* @link http://pear.php.net/package/PEAR
|
25 |
+
* @since File available since Release 0.1
|
26 |
+
*/
|
27 |
+
|
28 |
+
/**#@+
|
29 |
+
* ERROR constants
|
30 |
+
*/
|
31 |
+
define('PEAR_ERROR_RETURN', 1);
|
32 |
+
define('PEAR_ERROR_PRINT', 2);
|
33 |
+
define('PEAR_ERROR_TRIGGER', 4);
|
34 |
+
define('PEAR_ERROR_DIE', 8);
|
35 |
+
define('PEAR_ERROR_CALLBACK', 16);
|
36 |
+
/**
|
37 |
+
* WARNING: obsolete
|
38 |
+
* @deprecated
|
39 |
+
*/
|
40 |
+
define('PEAR_ERROR_EXCEPTION', 32);
|
41 |
+
/**#@-*/
|
42 |
+
define('PEAR_ZE2', (function_exists('version_compare') &&
|
43 |
+
version_compare(zend_version(), "2-dev", "ge")));
|
44 |
+
|
45 |
+
if (substr(PHP_OS, 0, 3) == 'WIN') {
|
46 |
+
define('OS_WINDOWS', true);
|
47 |
+
define('OS_UNIX', false);
|
48 |
+
define('PEAR_OS', 'Windows');
|
49 |
+
} else {
|
50 |
+
define('OS_WINDOWS', false);
|
51 |
+
define('OS_UNIX', true);
|
52 |
+
define('PEAR_OS', 'Unix'); // blatant assumption
|
53 |
+
}
|
54 |
+
|
55 |
+
// instant backwards compatibility
|
56 |
+
if (!defined('PATH_SEPARATOR')) {
|
57 |
+
if (OS_WINDOWS) {
|
58 |
+
define('PATH_SEPARATOR', ';');
|
59 |
+
} else {
|
60 |
+
define('PATH_SEPARATOR', ':');
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
|
65 |
+
$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
|
66 |
+
$GLOBALS['_PEAR_destructor_object_list'] = array();
|
67 |
+
$GLOBALS['_PEAR_shutdown_funcs'] = array();
|
68 |
+
$GLOBALS['_PEAR_error_handler_stack'] = array();
|
69 |
+
|
70 |
+
@ini_set('track_errors', true);
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Base class for other PEAR classes. Provides rudimentary
|
74 |
+
* emulation of destructors.
|
75 |
+
*
|
76 |
+
* If you want a destructor in your class, inherit PEAR and make a
|
77 |
+
* destructor method called _yourclassname (same name as the
|
78 |
+
* constructor, but with a "_" prefix). Also, in your constructor you
|
79 |
+
* have to call the PEAR constructor: $this->PEAR();.
|
80 |
+
* The destructor method will be called without parameters. Note that
|
81 |
+
* at in some SAPI implementations (such as Apache), any output during
|
82 |
+
* the request shutdown (in which destructors are called) seems to be
|
83 |
+
* discarded. If you need to get any debug information from your
|
84 |
+
* destructor, use error_log(), syslog() or something similar.
|
85 |
+
*
|
86 |
+
* IMPORTANT! To use the emulated destructors you need to create the
|
87 |
+
* objects by reference: $obj =& new PEAR_child;
|
88 |
+
*
|
89 |
+
* @category pear
|
90 |
+
* @package PEAR
|
91 |
+
* @author Stig Bakken <ssb@php.net>
|
92 |
+
* @author Tomas V.V. Cox <cox@idecnet.com>
|
93 |
+
* @author Greg Beaver <cellog@php.net>
|
94 |
+
* @copyright 1997-2006 The PHP Group
|
95 |
+
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
96 |
+
* @version Release: 1.7.2
|
97 |
+
* @link http://pear.php.net/package/PEAR
|
98 |
+
* @see PEAR_Error
|
99 |
+
* @since Class available since PHP 4.0.2
|
100 |
+
* @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
|
101 |
+
*/
|
102 |
+
class PEAR
|
103 |
+
{
|
104 |
+
// {{{ properties
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Whether to enable internal debug messages.
|
108 |
+
*
|
109 |
+
* @var bool
|
110 |
+
* @access private
|
111 |
+
*/
|
112 |
+
var $_debug = false;
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Default error mode for this object.
|
116 |
+
*
|
117 |
+
* @var int
|
118 |
+
* @access private
|
119 |
+
*/
|
120 |
+
var $_default_error_mode = null;
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Default error options used for this object when error mode
|
124 |
+
* is PEAR_ERROR_TRIGGER.
|
125 |
+
*
|
126 |
+
* @var int
|
127 |
+
* @access private
|
128 |
+
*/
|
129 |
+
var $_default_error_options = null;
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Default error handler (callback) for this object, if error mode is
|
133 |
+
* PEAR_ERROR_CALLBACK.
|
134 |
+
*
|
135 |
+
* @var string
|
136 |
+
* @access private
|
137 |
+
*/
|
138 |
+
var $_default_error_handler = '';
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Which class to use for error objects.
|
142 |
+
*
|
143 |
+
* @var string
|
144 |
+
* @access private
|
145 |
+
*/
|
146 |
+
var $_error_class = 'PEAR_Error';
|
147 |
+
|
148 |
+
/**
|
149 |
+
* An array of expected errors.
|
150 |
+
*
|
151 |
+
* @var array
|
152 |
+
* @access private
|
153 |
+
*/
|
154 |
+
var $_expected_errors = array();
|
155 |
+
|
156 |
+
// }}}
|
157 |
+
|
158 |
+
// {{{ constructor
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Constructor. Registers this object in
|
162 |
+
* $_PEAR_destructor_object_list for destructor emulation if a
|
163 |
+
* destructor object exists.
|
164 |
+
*
|
165 |
+
* @param string $error_class (optional) which class to use for
|
166 |
+
* error objects, defaults to PEAR_Error.
|
167 |
+
* @access public
|
168 |
+
* @return void
|
169 |
+
*/
|
170 |
+
function PEAR($error_class = null)
|
171 |
+
{
|
172 |
+
$classname = strtolower(get_class($this));
|
173 |
+
if ($this->_debug) {
|
174 |
+
print "PEAR constructor called, class=$classname\n";
|
175 |
+
}
|
176 |
+
if ($error_class !== null) {
|
177 |
+
$this->_error_class = $error_class;
|
178 |
+
}
|
179 |
+
while ($classname && strcasecmp($classname, "pear")) {
|
180 |
+
$destructor = "_$classname";
|
181 |
+
if (method_exists($this, $destructor)) {
|
182 |
+
global $_PEAR_destructor_object_list;
|
183 |
+
$_PEAR_destructor_object_list[] = &$this;
|
184 |
+
if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
|
185 |
+
register_shutdown_function("_PEAR_call_destructors");
|
186 |
+
$GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
|
187 |
+
}
|
188 |
+
break;
|
189 |
+
} else {
|
190 |
+
$classname = get_parent_class($classname);
|
191 |
+
}
|
192 |
+
}
|
193 |
+
}
|
194 |
+
|
195 |
+
// }}}
|
196 |
+
// {{{ destructor
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Destructor (the emulated type of...). Does nothing right now,
|
200 |
+
* but is included for forward compatibility, so subclass
|
201 |
+
* destructors should always call it.
|
202 |
+
*
|
203 |
+
* See the note in the class desciption about output from
|
204 |
+
* destructors.
|
205 |
+
*
|
206 |
+
* @access public
|
207 |
+
* @return void
|
208 |
+
*/
|
209 |
+
function _PEAR() {
|
210 |
+
if ($this->_debug) {
|
211 |
+
printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
// }}}
|
216 |
+
// {{{ getStaticProperty()
|
217 |
+
|
218 |
+
/**
|
219 |
+
* If you have a class that's mostly/entirely static, and you need static
|
220 |
+
* properties, you can use this method to simulate them. Eg. in your method(s)
|
221 |
+
* do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
|
222 |
+
* You MUST use a reference, or they will not persist!
|
223 |
+
*
|
224 |
+
* @access public
|
225 |
+
* @param string $class The calling classname, to prevent clashes
|
226 |
+
* @param string $var The variable to retrieve.
|
227 |
+
* @return mixed A reference to the variable. If not set it will be
|
228 |
+
* auto initialised to NULL.
|
229 |
+
*/
|
230 |
+
public static function &getStaticProperty($class, $var)
|
231 |
+
{
|
232 |
+
static $properties;
|
233 |
+
if (!isset($properties[$class])) {
|
234 |
+
$properties[$class] = array();
|
235 |
+
}
|
236 |
+
if (!array_key_exists($var, $properties[$class])) {
|
237 |
+
$properties[$class][$var] = null;
|
238 |
+
}
|
239 |
+
return $properties[$class][$var];
|
240 |
+
}
|
241 |
+
|
242 |
+
// }}}
|
243 |
+
// {{{ registerShutdownFunc()
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Use this function to register a shutdown method for static
|
247 |
+
* classes.
|
248 |
+
*
|
249 |
+
* @access public
|
250 |
+
* @param mixed $func The function name (or array of class/method) to call
|
251 |
+
* @param mixed $args The arguments to pass to the function
|
252 |
+
* @return void
|
253 |
+
*/
|
254 |
+
function registerShutdownFunc($func, $args = array())
|
255 |
+
{
|
256 |
+
// if we are called statically, there is a potential
|
257 |
+
// that no shutdown func is registered. Bug #6445
|
258 |
+
if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
|
259 |
+
register_shutdown_function("_PEAR_call_destructors");
|
260 |
+
$GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
|
261 |
+
}
|
262 |
+
$GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
|
263 |
+
}
|
264 |
+
|
265 |
+
// }}}
|
266 |
+
// {{{ isError()
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Tell whether a value is a PEAR error.
|
270 |
+
*
|
271 |
+
* @param mixed $data the value to test
|
272 |
+
* @param int $code if $data is an error object, return true
|
273 |
+
* only if $code is a string and
|
274 |
+
* $obj->getMessage() == $code or
|
275 |
+
* $code is an integer and $obj->getCode() == $code
|
276 |
+
* @access public
|
277 |
+
* @return bool true if parameter is an error
|
278 |
+
*/
|
279 |
+
public static function isError($data, $code = null)
|
280 |
+
{
|
281 |
+
if ($data instanceof PEAR_Error) {
|
282 |
+
if (is_null($code)) {
|
283 |
+
return true;
|
284 |
+
} elseif (is_string($code)) {
|
285 |
+
return $data->getMessage() == $code;
|
286 |
+
} else {
|
287 |
+
return $data->getCode() == $code;
|
288 |
+
}
|
289 |
+
}
|
290 |
+
return false;
|
291 |
+
}
|
292 |
+
|
293 |
+
// }}}
|
294 |
+
// {{{ setErrorHandling()
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Sets how errors generated by this object should be handled.
|
298 |
+
* Can be invoked both in objects and statically. If called
|
299 |
+
* statically, setErrorHandling sets the default behaviour for all
|
300 |
+
* PEAR objects. If called in an object, setErrorHandling sets
|
301 |
+
* the default behaviour for that object.
|
302 |
+
*
|
303 |
+
* @param int $mode
|
304 |
+
* One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
|
305 |
+
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
|
306 |
+
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
|
307 |
+
*
|
308 |
+
* @param mixed $options
|
309 |
+
* When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
|
310 |
+
* of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
|
311 |
+
*
|
312 |
+
* When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
|
313 |
+
* to be the callback function or method. A callback
|
314 |
+
* function is a string with the name of the function, a
|
315 |
+
* callback method is an array of two elements: the element
|
316 |
+
* at index 0 is the object, and the element at index 1 is
|
317 |
+
* the name of the method to call in the object.
|
318 |
+
*
|
319 |
+
* When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
|
320 |
+
* a printf format string used when printing the error
|
321 |
+
* message.
|
322 |
+
*
|
323 |
+
* @access public
|
324 |
+
* @return void
|
325 |
+
* @see PEAR_ERROR_RETURN
|
326 |
+
* @see PEAR_ERROR_PRINT
|
327 |
+
* @see PEAR_ERROR_TRIGGER
|
328 |
+
* @see PEAR_ERROR_DIE
|
329 |
+
* @see PEAR_ERROR_CALLBACK
|
330 |
+
* @see PEAR_ERROR_EXCEPTION
|
331 |
+
*
|
332 |
+
* @since PHP 4.0.5
|
333 |
+
*/
|
334 |
+
|
335 |
+
function setErrorHandling($mode = null, $options = null)
|
336 |
+
{
|
337 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
338 |
+
$setmode = &$this->_default_error_mode;
|
339 |
+
$setoptions = &$this->_default_error_options;
|
340 |
+
} else {
|
341 |
+
$setmode = &$GLOBALS['_PEAR_default_error_mode'];
|
342 |
+
$setoptions = &$GLOBALS['_PEAR_default_error_options'];
|
343 |
+
}
|
344 |
+
|
345 |
+
switch ($mode) {
|
346 |
+
case PEAR_ERROR_EXCEPTION:
|
347 |
+
case PEAR_ERROR_RETURN:
|
348 |
+
case PEAR_ERROR_PRINT:
|
349 |
+
case PEAR_ERROR_TRIGGER:
|
350 |
+
case PEAR_ERROR_DIE:
|
351 |
+
case null:
|
352 |
+
$setmode = $mode;
|
353 |
+
$setoptions = $options;
|
354 |
+
break;
|
355 |
+
|
356 |
+
case PEAR_ERROR_CALLBACK:
|
357 |
+
$setmode = $mode;
|
358 |
+
// class/object method callback
|
359 |
+
if (is_callable($options)) {
|
360 |
+
$setoptions = $options;
|
361 |
+
} else {
|
362 |
+
trigger_error("invalid error callback", E_USER_WARNING);
|
363 |
+
}
|
364 |
+
break;
|
365 |
+
|
366 |
+
default:
|
367 |
+
trigger_error("invalid error mode", E_USER_WARNING);
|
368 |
+
break;
|
369 |
+
}
|
370 |
+
}
|
371 |
+
|
372 |
+
// }}}
|
373 |
+
// {{{ expectError()
|
374 |
+
|
375 |
+
/**
|
376 |
+
* This method is used to tell which errors you expect to get.
|
377 |
+
* Expected errors are always returned with error mode
|
378 |
+
* PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
|
379 |
+
* and this method pushes a new element onto it. The list of
|
380 |
+
* expected errors are in effect until they are popped off the
|
381 |
+
* stack with the popExpect() method.
|
382 |
+
*
|
383 |
+
* Note that this method can not be called statically
|
384 |
+
*
|
385 |
+
* @param mixed $code a single error code or an array of error codes to expect
|
386 |
+
*
|
387 |
+
* @return int the new depth of the "expected errors" stack
|
388 |
+
* @access public
|
389 |
+
*/
|
390 |
+
function expectError($code = '*')
|
391 |
+
{
|
392 |
+
if (is_array($code)) {
|
393 |
+
array_push($this->_expected_errors, $code);
|
394 |
+
} else {
|
395 |
+
array_push($this->_expected_errors, array($code));
|
396 |
+
}
|
397 |
+
return sizeof($this->_expected_errors);
|
398 |
+
}
|
399 |
+
|
400 |
+
// }}}
|
401 |
+
// {{{ popExpect()
|
402 |
+
|
403 |
+
/**
|
404 |
+
* This method pops one element off the expected error codes
|
405 |
+
* stack.
|
406 |
+
*
|
407 |
+
* @return array the list of error codes that were popped
|
408 |
+
*/
|
409 |
+
function popExpect()
|
410 |
+
{
|
411 |
+
return array_pop($this->_expected_errors);
|
412 |
+
}
|
413 |
+
|
414 |
+
// }}}
|
415 |
+
// {{{ _checkDelExpect()
|
416 |
+
|
417 |
+
/**
|
418 |
+
* This method checks unsets an error code if available
|
419 |
+
*
|
420 |
+
* @param mixed error code
|
421 |
+
* @return bool true if the error code was unset, false otherwise
|
422 |
+
* @access private
|
423 |
+
* @since PHP 4.3.0
|
424 |
+
*/
|
425 |
+
function _checkDelExpect($error_code)
|
426 |
+
{
|
427 |
+
$deleted = false;
|
428 |
+
|
429 |
+
foreach ($this->_expected_errors AS $key => $error_array) {
|
430 |
+
if (in_array($error_code, $error_array)) {
|
431 |
+
unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
|
432 |
+
$deleted = true;
|
433 |
+
}
|
434 |
+
|
435 |
+
// clean up empty arrays
|
436 |
+
if (0 == count($this->_expected_errors[$key])) {
|
437 |
+
unset($this->_expected_errors[$key]);
|
438 |
+
}
|
439 |
+
}
|
440 |
+
return $deleted;
|
441 |
+
}
|
442 |
+
|
443 |
+
// }}}
|
444 |
+
// {{{ delExpect()
|
445 |
+
|
446 |
+
/**
|
447 |
+
* This method deletes all occurences of the specified element from
|
448 |
+
* the expected error codes stack.
|
449 |
+
*
|
450 |
+
* @param mixed $error_code error code that should be deleted
|
451 |
+
* @return mixed list of error codes that were deleted or error
|
452 |
+
* @access public
|
453 |
+
* @since PHP 4.3.0
|
454 |
+
*/
|
455 |
+
function delExpect($error_code)
|
456 |
+
{
|
457 |
+
$deleted = false;
|
458 |
+
|
459 |
+
if ((is_array($error_code) && (0 != count($error_code)))) {
|
460 |
+
// $error_code is a non-empty array here;
|
461 |
+
// we walk through it trying to unset all
|
462 |
+
// values
|
463 |
+
foreach($error_code as $key => $error) {
|
464 |
+
if ($this->_checkDelExpect($error)) {
|
465 |
+
$deleted = true;
|
466 |
+
} else {
|
467 |
+
$deleted = false;
|
468 |
+
}
|
469 |
+
}
|
470 |
+
return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
|
471 |
+
} elseif (!empty($error_code)) {
|
472 |
+
// $error_code comes alone, trying to unset it
|
473 |
+
if ($this->_checkDelExpect($error_code)) {
|
474 |
+
return true;
|
475 |
+
} else {
|
476 |
+
return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
|
477 |
+
}
|
478 |
+
} else {
|
479 |
+
// $error_code is empty
|
480 |
+
return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
|
481 |
+
}
|
482 |
+
}
|
483 |
+
|
484 |
+
// }}}
|
485 |
+
// {{{ raiseError()
|
486 |
+
|
487 |
+
/**
|
488 |
+
* This method is a wrapper that returns an instance of the
|
489 |
+
* configured error class with this object's default error
|
490 |
+
* handling applied. If the $mode and $options parameters are not
|
491 |
+
* specified, the object's defaults are used.
|
492 |
+
*
|
493 |
+
* @param mixed $message a text error message or a PEAR error object
|
494 |
+
*
|
495 |
+
* @param int $code a numeric error code (it is up to your class
|
496 |
+
* to define these if you want to use codes)
|
497 |
+
*
|
498 |
+
* @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
|
499 |
+
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
|
500 |
+
* PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
|
501 |
+
*
|
502 |
+
* @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
|
503 |
+
* specifies the PHP-internal error level (one of
|
504 |
+
* E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
|
505 |
+
* If $mode is PEAR_ERROR_CALLBACK, this
|
506 |
+
* parameter specifies the callback function or
|
507 |
+
* method. In other error modes this parameter
|
508 |
+
* is ignored.
|
509 |
+
*
|
510 |
+
* @param string $userinfo If you need to pass along for example debug
|
511 |
+
* information, this parameter is meant for that.
|
512 |
+
*
|
513 |
+
* @param string $error_class The returned error object will be
|
514 |
+
* instantiated from this class, if specified.
|
515 |
+
*
|
516 |
+
* @param bool $skipmsg If true, raiseError will only pass error codes,
|
517 |
+
* the error message parameter will be dropped.
|
518 |
+
*
|
519 |
+
* @access public
|
520 |
+
* @return object a PEAR error object
|
521 |
+
* @see PEAR::setErrorHandling
|
522 |
+
* @since PHP 4.0.5
|
523 |
+
*/
|
524 |
+
public static function raiseError($message = null,
|
525 |
+
$code = null,
|
526 |
+
$mode = null,
|
527 |
+
$options = null,
|
528 |
+
$userinfo = null,
|
529 |
+
$error_class = null,
|
530 |
+
$skipmsg = false)
|
531 |
+
{
|
532 |
+
// The error is yet a PEAR error object
|
533 |
+
if (is_object($message)) {
|
534 |
+
$code = $message->getCode();
|
535 |
+
$userinfo = $message->getUserInfo();
|
536 |
+
$error_class = $message->getType();
|
537 |
+
$message->error_message_prefix = '';
|
538 |
+
$message = $message->getMessage();
|
539 |
+
}
|
540 |
+
|
541 |
+
if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
|
542 |
+
if ($exp[0] == "*" ||
|
543 |
+
(is_int(reset($exp)) && in_array($code, $exp)) ||
|
544 |
+
(is_string(reset($exp)) && in_array($message, $exp))) {
|
545 |
+
$mode = PEAR_ERROR_RETURN;
|
546 |
+
}
|
547 |
+
}
|
548 |
+
// No mode given, try global ones
|
549 |
+
if ($mode === null) {
|
550 |
+
// Class error handler
|
551 |
+
if (isset($this) && isset($this->_default_error_mode)) {
|
552 |
+
$mode = $this->_default_error_mode;
|
553 |
+
$options = $this->_default_error_options;
|
554 |
+
// Global error handler
|
555 |
+
} elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
|
556 |
+
$mode = $GLOBALS['_PEAR_default_error_mode'];
|
557 |
+
$options = $GLOBALS['_PEAR_default_error_options'];
|
558 |
+
}
|
559 |
+
}
|
560 |
+
|
561 |
+
if ($error_class !== null) {
|
562 |
+
$ec = $error_class;
|
563 |
+
} elseif (isset($this) && isset($this->_error_class)) {
|
564 |
+
$ec = $this->_error_class;
|
565 |
+
} else {
|
566 |
+
$ec = 'PEAR_Error';
|
567 |
+
}
|
568 |
+
if (intval(PHP_VERSION) < 5) {
|
569 |
+
// little non-eval hack to fix bug #12147
|
570 |
+
include 'PEAR/FixPHP5PEARWarnings.php';
|
571 |
+
return $a;
|
572 |
+
}
|
573 |
+
if ($skipmsg) {
|
574 |
+
$a = new $ec($code, $mode, $options, $userinfo);
|
575 |
+
} else {
|
576 |
+
$a = new $ec($message, $code, $mode, $options, $userinfo);
|
577 |
+
}
|
578 |
+
return $a;
|
579 |
+
}
|
580 |
+
|
581 |
+
// }}}
|
582 |
+
// {{{ throwError()
|
583 |
+
|
584 |
+
/**
|
585 |
+
* Simpler form of raiseError with fewer options. In most cases
|
586 |
+
* message, code and userinfo are enough.
|
587 |
+
*
|
588 |
+
* @param string $message
|
589 |
+
*
|
590 |
+
*/
|
591 |
+
function &throwError($message = null,
|
592 |
+
$code = null,
|
593 |
+
$userinfo = null)
|
594 |
+
{
|
595 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
596 |
+
$a = &$this->raiseError($message, $code, null, null, $userinfo);
|
597 |
+
return $a;
|
598 |
+
} else {
|
599 |
+
$a = &PEAR::raiseError($message, $code, null, null, $userinfo);
|
600 |
+
return $a;
|
601 |
+
}
|
602 |
+
}
|
603 |
+
|
604 |
+
// }}}
|
605 |
+
function staticPushErrorHandling($mode, $options = null)
|
606 |
+
{
|
607 |
+
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
|
608 |
+
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
|
609 |
+
$def_options = &$GLOBALS['_PEAR_default_error_options'];
|
610 |
+
$stack[] = array($def_mode, $def_options);
|
611 |
+
switch ($mode) {
|
612 |
+
case PEAR_ERROR_EXCEPTION:
|
613 |
+
case PEAR_ERROR_RETURN:
|
614 |
+
case PEAR_ERROR_PRINT:
|
615 |
+
case PEAR_ERROR_TRIGGER:
|
616 |
+
case PEAR_ERROR_DIE:
|
617 |
+
case null:
|
618 |
+
$def_mode = $mode;
|
619 |
+
$def_options = $options;
|
620 |
+
break;
|
621 |
+
|
622 |
+
case PEAR_ERROR_CALLBACK:
|
623 |
+
$def_mode = $mode;
|
624 |
+
// class/object method callback
|
625 |
+
if (is_callable($options)) {
|
626 |
+
$def_options = $options;
|
627 |
+
} else {
|
628 |
+
trigger_error("invalid error callback", E_USER_WARNING);
|
629 |
+
}
|
630 |
+
break;
|
631 |
+
|
632 |
+
default:
|
633 |
+
trigger_error("invalid error mode", E_USER_WARNING);
|
634 |
+
break;
|
635 |
+
}
|
636 |
+
$stack[] = array($mode, $options);
|
637 |
+
return true;
|
638 |
+
}
|
639 |
+
|
640 |
+
function staticPopErrorHandling()
|
641 |
+
{
|
642 |
+
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
|
643 |
+
$setmode = &$GLOBALS['_PEAR_default_error_mode'];
|
644 |
+
$setoptions = &$GLOBALS['_PEAR_default_error_options'];
|
645 |
+
array_pop($stack);
|
646 |
+
list($mode, $options) = $stack[sizeof($stack) - 1];
|
647 |
+
array_pop($stack);
|
648 |
+
switch ($mode) {
|
649 |
+
case PEAR_ERROR_EXCEPTION:
|
650 |
+
case PEAR_ERROR_RETURN:
|
651 |
+
case PEAR_ERROR_PRINT:
|
652 |
+
case PEAR_ERROR_TRIGGER:
|
653 |
+
case PEAR_ERROR_DIE:
|
654 |
+
case null:
|
655 |
+
$setmode = $mode;
|
656 |
+
$setoptions = $options;
|
657 |
+
break;
|
658 |
+
|
659 |
+
case PEAR_ERROR_CALLBACK:
|
660 |
+
$setmode = $mode;
|
661 |
+
// class/object method callback
|
662 |
+
if (is_callable($options)) {
|
663 |
+
$setoptions = $options;
|
664 |
+
} else {
|
665 |
+
trigger_error("invalid error callback", E_USER_WARNING);
|
666 |
+
}
|
667 |
+
break;
|
668 |
+
|
669 |
+
default:
|
670 |
+
trigger_error("invalid error mode", E_USER_WARNING);
|
671 |
+
break;
|
672 |
+
}
|
673 |
+
return true;
|
674 |
+
}
|
675 |
+
|
676 |
+
// {{{ pushErrorHandling()
|
677 |
+
|
678 |
+
/**
|
679 |
+
* Push a new error handler on top of the error handler options stack. With this
|
680 |
+
* you can easily override the actual error handler for some code and restore
|
681 |
+
* it later with popErrorHandling.
|
682 |
+
*
|
683 |
+
* @param mixed $mode (same as setErrorHandling)
|
684 |
+
* @param mixed $options (same as setErrorHandling)
|
685 |
+
*
|
686 |
+
* @return bool Always true
|
687 |
+
*
|
688 |
+
* @see PEAR::setErrorHandling
|
689 |
+
*/
|
690 |
+
function pushErrorHandling($mode, $options = null)
|
691 |
+
{
|
692 |
+
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
|
693 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
694 |
+
$def_mode = &$this->_default_error_mode;
|
695 |
+
$def_options = &$this->_default_error_options;
|
696 |
+
} else {
|
697 |
+
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
|
698 |
+
$def_options = &$GLOBALS['_PEAR_default_error_options'];
|
699 |
+
}
|
700 |
+
$stack[] = array($def_mode, $def_options);
|
701 |
+
|
702 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
703 |
+
$this->setErrorHandling($mode, $options);
|
704 |
+
} else {
|
705 |
+
PEAR::setErrorHandling($mode, $options);
|
706 |
+
}
|
707 |
+
$stack[] = array($mode, $options);
|
708 |
+
return true;
|
709 |
+
}
|
710 |
+
|
711 |
+
// }}}
|
712 |
+
// {{{ popErrorHandling()
|
713 |
+
|
714 |
+
/**
|
715 |
+
* Pop the last error handler used
|
716 |
+
*
|
717 |
+
* @return bool Always true
|
718 |
+
*
|
719 |
+
* @see PEAR::pushErrorHandling
|
720 |
+
*/
|
721 |
+
function popErrorHandling()
|
722 |
+
{
|
723 |
+
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
|
724 |
+
array_pop($stack);
|
725 |
+
list($mode, $options) = $stack[sizeof($stack) - 1];
|
726 |
+
array_pop($stack);
|
727 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
728 |
+
$this->setErrorHandling($mode, $options);
|
729 |
+
} else {
|
730 |
+
PEAR::setErrorHandling($mode, $options);
|
731 |
+
}
|
732 |
+
return true;
|
733 |
+
}
|
734 |
+
|
735 |
+
// }}}
|
736 |
+
// {{{ loadExtension()
|
737 |
+
|
738 |
+
/**
|
739 |
+
* OS independant PHP extension load. Remember to take care
|
740 |
+
* on the correct extension name for case sensitive OSes.
|
741 |
+
*
|
742 |
+
* @param string $ext The extension name
|
743 |
+
* @return bool Success or not on the dl() call
|
744 |
+
*/
|
745 |
+
function loadExtension($ext)
|
746 |
+
{
|
747 |
+
if (!extension_loaded($ext)) {
|
748 |
+
// if either returns true dl() will produce a FATAL error, stop that
|
749 |
+
if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
|
750 |
+
return false;
|
751 |
+
}
|
752 |
+
if (OS_WINDOWS) {
|
753 |
+
$suffix = '.dll';
|
754 |
+
} elseif (PHP_OS == 'HP-UX') {
|
755 |
+
$suffix = '.sl';
|
756 |
+
} elseif (PHP_OS == 'AIX') {
|
757 |
+
$suffix = '.a';
|
758 |
+
} elseif (PHP_OS == 'OSX') {
|
759 |
+
$suffix = '.bundle';
|
760 |
+
} else {
|
761 |
+
$suffix = '.so';
|
762 |
+
}
|
763 |
+
return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
|
764 |
+
}
|
765 |
+
return true;
|
766 |
+
}
|
767 |
+
|
768 |
+
// }}}
|
769 |
+
}
|
770 |
+
|
771 |
+
// {{{ _PEAR_call_destructors()
|
772 |
+
|
773 |
+
function _PEAR_call_destructors()
|
774 |
+
{
|
775 |
+
global $_PEAR_destructor_object_list;
|
776 |
+
if (is_array($_PEAR_destructor_object_list) &&
|
777 |
+
sizeof($_PEAR_destructor_object_list))
|
778 |
+
{
|
779 |
+
reset($_PEAR_destructor_object_list);
|
780 |
+
if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
|
781 |
+
$_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
|
782 |
+
}
|
783 |
+
while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
|
784 |
+
$classname = get_class($objref);
|
785 |
+
while ($classname) {
|
786 |
+
$destructor = "_$classname";
|
787 |
+
if (method_exists($objref, $destructor)) {
|
788 |
+
$objref->$destructor();
|
789 |
+
break;
|
790 |
+
} else {
|
791 |
+
$classname = get_parent_class($classname);
|
792 |
+
}
|
793 |
+
}
|
794 |
+
}
|
795 |
+
// Empty the object list to ensure that destructors are
|
796 |
+
// not called more than once.
|
797 |
+
$_PEAR_destructor_object_list = array();
|
798 |
+
}
|
799 |
+
|
800 |
+
// Now call the shutdown functions
|
801 |
+
if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
|
802 |
+
foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
|
803 |
+
call_user_func_array($value[0], $value[1]);
|
804 |
+
}
|
805 |
+
}
|
806 |
+
}
|
807 |
+
|
808 |
+
// }}}
|
809 |
+
/**
|
810 |
+
* Standard PEAR error class for PHP 4
|
811 |
+
*
|
812 |
+
* This class is supserseded by {@link PEAR_Exception} in PHP 5
|
813 |
+
*
|
814 |
+
* @category pear
|
815 |
+
* @package PEAR
|
816 |
+
* @author Stig Bakken <ssb@php.net>
|
817 |
+
* @author Tomas V.V. Cox <cox@idecnet.com>
|
818 |
+
* @author Gregory Beaver <cellog@php.net>
|
819 |
+
* @copyright 1997-2006 The PHP Group
|
820 |
+
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
821 |
+
* @version Release: 1.7.2
|
822 |
+
* @link http://pear.php.net/manual/en/core.pear.pear-error.php
|
823 |
+
* @see PEAR::raiseError(), PEAR::throwError()
|
824 |
+
* @since Class available since PHP 4.0.2
|
825 |
+
*/
|
826 |
+
class PEAR_Error
|
827 |
+
{
|
828 |
+
// {{{ properties
|
829 |
+
|
830 |
+
var $error_message_prefix = '';
|
831 |
+
var $mode = PEAR_ERROR_RETURN;
|
832 |
+
var $level = E_USER_NOTICE;
|
833 |
+
var $code = -1;
|
834 |
+
var $message = '';
|
835 |
+
var $userinfo = '';
|
836 |
+
var $backtrace = null;
|
837 |
+
|
838 |
+
// }}}
|
839 |
+
// {{{ constructor
|
840 |
+
|
841 |
+
/**
|
842 |
+
* PEAR_Error constructor
|
843 |
+
*
|
844 |
+
* @param string $message message
|
845 |
+
*
|
846 |
+
* @param int $code (optional) error code
|
847 |
+
*
|
848 |
+
* @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
|
849 |
+
* PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
|
850 |
+
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
|
851 |
+
*
|
852 |
+
* @param mixed $options (optional) error level, _OR_ in the case of
|
853 |
+
* PEAR_ERROR_CALLBACK, the callback function or object/method
|
854 |
+
* tuple.
|
855 |
+
*
|
856 |
+
* @param string $userinfo (optional) additional user/debug info
|
857 |
+
*
|
858 |
+
* @access public
|
859 |
+
*
|
860 |
+
*/
|
861 |
+
function PEAR_Error($message = 'unknown error', $code = null,
|
862 |
+
$mode = null, $options = null, $userinfo = null)
|
863 |
+
{
|
864 |
+
if ($mode === null) {
|
865 |
+
$mode = PEAR_ERROR_RETURN;
|
866 |
+
}
|
867 |
+
$this->message = $message;
|
868 |
+
$this->code = $code;
|
869 |
+
$this->mode = $mode;
|
870 |
+
$this->userinfo = $userinfo;
|
871 |
+
if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
|
872 |
+
$this->backtrace = debug_backtrace();
|
873 |
+
if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
|
874 |
+
unset($this->backtrace[0]['object']);
|
875 |
+
}
|
876 |
+
}
|
877 |
+
if ($mode & PEAR_ERROR_CALLBACK) {
|
878 |
+
$this->level = E_USER_NOTICE;
|
879 |
+
$this->callback = $options;
|
880 |
+
} else {
|
881 |
+
if ($options === null) {
|
882 |
+
$options = E_USER_NOTICE;
|
883 |
+
}
|
884 |
+
$this->level = $options;
|
885 |
+
$this->callback = null;
|
886 |
+
}
|
887 |
+
if ($this->mode & PEAR_ERROR_PRINT) {
|
888 |
+
if (is_null($options) || is_int($options)) {
|
889 |
+
$format = "%s";
|
890 |
+
} else {
|
891 |
+
$format = $options;
|
892 |
+
}
|
893 |
+
printf($format, $this->getMessage());
|
894 |
+
}
|
895 |
+
if ($this->mode & PEAR_ERROR_TRIGGER) {
|
896 |
+
trigger_error($this->getMessage(), $this->level);
|
897 |
+
}
|
898 |
+
if ($this->mode & PEAR_ERROR_DIE) {
|
899 |
+
$msg = $this->getMessage();
|
900 |
+
if (is_null($options) || is_int($options)) {
|
901 |
+
$format = "%s";
|
902 |
+
if (substr($msg, -1) != "\n") {
|
903 |
+
$msg .= "\n";
|
904 |
+
}
|
905 |
+
} else {
|
906 |
+
$format = $options;
|
907 |
+
}
|
908 |
+
die(sprintf($format, $msg));
|
909 |
+
}
|
910 |
+
if ($this->mode & PEAR_ERROR_CALLBACK) {
|
911 |
+
if (is_callable($this->callback)) {
|
912 |
+
call_user_func($this->callback, $this);
|
913 |
+
}
|
914 |
+
}
|
915 |
+
if ($this->mode & PEAR_ERROR_EXCEPTION) {
|
916 |
+
trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
|
917 |
+
eval('$e = new Exception($this->message, $this->code);throw($e);');
|
918 |
+
}
|
919 |
+
}
|
920 |
+
|
921 |
+
// }}}
|
922 |
+
// {{{ getMode()
|
923 |
+
|
924 |
+
/**
|
925 |
+
* Get the error mode from an error object.
|
926 |
+
*
|
927 |
+
* @return int error mode
|
928 |
+
* @access public
|
929 |
+
*/
|
930 |
+
function getMode() {
|
931 |
+
return $this->mode;
|
932 |
+
}
|
933 |
+
|
934 |
+
// }}}
|
935 |
+
// {{{ getCallback()
|
936 |
+
|
937 |
+
/**
|
938 |
+
* Get the callback function/method from an error object.
|
939 |
+
*
|
940 |
+
* @return mixed callback function or object/method array
|
941 |
+
* @access public
|
942 |
+
*/
|
943 |
+
function getCallback() {
|
944 |
+
return $this->callback;
|
945 |
+
}
|
946 |
+
|
947 |
+
// }}}
|
948 |
+
// {{{ getMessage()
|
949 |
+
|
950 |
+
|
951 |
+
/**
|
952 |
+
* Get the error message from an error object.
|
953 |
+
*
|
954 |
+
* @return string full error message
|
955 |
+
* @access public
|
956 |
+
*/
|
957 |
+
function getMessage()
|
958 |
+
{
|
959 |
+
return ($this->error_message_prefix . $this->message);
|
960 |
+
}
|
961 |
+
|
962 |
+
|
963 |
+
// }}}
|
964 |
+
// {{{ getCode()
|
965 |
+
|
966 |
+
/**
|
967 |
+
* Get error code from an error object
|
968 |
+
*
|
969 |
+
* @return int error code
|
970 |
+
* @access public
|
971 |
+
*/
|
972 |
+
function getCode()
|
973 |
+
{
|
974 |
+
return $this->code;
|
975 |
+
}
|
976 |
+
|
977 |
+
// }}}
|
978 |
+
// {{{ getType()
|
979 |
+
|
980 |
+
/**
|
981 |
+
* Get the name of this error/exception.
|
982 |
+
*
|
983 |
+
* @return string error/exception name (type)
|
984 |
+
* @access public
|
985 |
+
*/
|
986 |
+
function getType()
|
987 |
+
{
|
988 |
+
return get_class($this);
|
989 |
+
}
|
990 |
+
|
991 |
+
// }}}
|
992 |
+
// {{{ getUserInfo()
|
993 |
+
|
994 |
+
/**
|
995 |
+
* Get additional user-supplied information.
|
996 |
+
*
|
997 |
+
* @return string user-supplied information
|
998 |
+
* @access public
|
999 |
+
*/
|
1000 |
+
function getUserInfo()
|
1001 |
+
{
|
1002 |
+
return $this->userinfo;
|
1003 |
+
}
|
1004 |
+
|
1005 |
+
// }}}
|
1006 |
+
// {{{ getDebugInfo()
|
1007 |
+
|
1008 |
+
/**
|
1009 |
+
* Get additional debug information supplied by the application.
|
1010 |
+
*
|
1011 |
+
* @return string debug information
|
1012 |
+
* @access public
|
1013 |
+
*/
|
1014 |
+
function getDebugInfo()
|
1015 |
+
{
|
1016 |
+
return $this->getUserInfo();
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
// }}}
|
1020 |
+
// {{{ getBacktrace()
|
1021 |
+
|
1022 |
+
/**
|
1023 |
+
* Get the call backtrace from where the error was generated.
|
1024 |
+
* Supported with PHP 4.3.0 or newer.
|
1025 |
+
*
|
1026 |
+
* @param int $frame (optional) what frame to fetch
|
1027 |
+
* @return array Backtrace, or NULL if not available.
|
1028 |
+
* @access public
|
1029 |
+
*/
|
1030 |
+
function getBacktrace($frame = null)
|
1031 |
+
{
|
1032 |
+
if (defined('PEAR_IGNORE_BACKTRACE')) {
|
1033 |
+
return null;
|
1034 |
+
}
|
1035 |
+
if ($frame === null) {
|
1036 |
+
return $this->backtrace;
|
1037 |
+
}
|
1038 |
+
return $this->backtrace[$frame];
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
// }}}
|
1042 |
+
// {{{ addUserInfo()
|
1043 |
+
|
1044 |
+
function addUserInfo($info)
|
1045 |
+
{
|
1046 |
+
if (empty($this->userinfo)) {
|
1047 |
+
$this->userinfo = $info;
|
1048 |
+
} else {
|
1049 |
+
$this->userinfo .= " ** $info";
|
1050 |
+
}
|
1051 |
+
}
|
1052 |
+
|
1053 |
+
// }}}
|
1054 |
+
// {{{ toString()
|
1055 |
+
function __toString()
|
1056 |
+
{
|
1057 |
+
return $this->getMessage();
|
1058 |
+
}
|
1059 |
+
// }}}
|
1060 |
+
// {{{ toString()
|
1061 |
+
|
1062 |
+
/**
|
1063 |
+
* Make a string representation of this object.
|
1064 |
+
*
|
1065 |
+
* @return string a string with an object summary
|
1066 |
+
* @access public
|
1067 |
+
*/
|
1068 |
+
function toString() {
|
1069 |
+
$modes = array();
|
1070 |
+
$levels = array(E_USER_NOTICE => 'notice',
|
1071 |
+
E_USER_WARNING => 'warning',
|
1072 |
+
E_USER_ERROR => 'error');
|
1073 |
+
if ($this->mode & PEAR_ERROR_CALLBACK) {
|
1074 |
+
if (is_array($this->callback)) {
|
1075 |
+
$callback = (is_object($this->callback[0]) ?
|
1076 |
+
strtolower(get_class($this->callback[0])) :
|
1077 |
+
$this->callback[0]) . '::' .
|
1078 |
+
$this->callback[1];
|
1079 |
+
} else {
|
1080 |
+
$callback = $this->callback;
|
1081 |
+
}
|
1082 |
+
return sprintf('[%s: message="%s" code=%d mode=callback '.
|
1083 |
+
'callback=%s prefix="%s" info="%s"]',
|
1084 |
+
strtolower(get_class($this)), $this->message, $this->code,
|
1085 |
+
$callback, $this->error_message_prefix,
|
1086 |
+
$this->userinfo);
|
1087 |
+
}
|
1088 |
+
if ($this->mode & PEAR_ERROR_PRINT) {
|
1089 |
+
$modes[] = 'print';
|
1090 |
+
}
|
1091 |
+
if ($this->mode & PEAR_ERROR_TRIGGER) {
|
1092 |
+
$modes[] = 'trigger';
|
1093 |
+
}
|
1094 |
+
if ($this->mode & PEAR_ERROR_DIE) {
|
1095 |
+
$modes[] = 'die';
|
1096 |
+
}
|
1097 |
+
if ($this->mode & PEAR_ERROR_RETURN) {
|
1098 |
+
$modes[] = 'return';
|
1099 |
+
}
|
1100 |
+
return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
|
1101 |
+
'prefix="%s" info="%s"]',
|
1102 |
+
strtolower(get_class($this)), $this->message, $this->code,
|
1103 |
+
implode("|", $modes), $levels[$this->level],
|
1104 |
+
$this->error_message_prefix,
|
1105 |
+
$this->userinfo);
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
// }}}
|
1109 |
+
}
|
1110 |
+
|
1111 |
+
/*
|
1112 |
+
* Local Variables:
|
1113 |
+
* mode: php
|
1114 |
+
* tab-width: 4
|
1115 |
+
* c-basic-offset: 4
|
1116 |
+
* End:
|
1117 |
+
*/
|
1118 |
+
?>
|
lib/PEAR/PEAR/PEAR.php
ADDED
@@ -0,0 +1,1118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* PEAR, the PHP Extension and Application Repository
|
4 |
+
*
|
5 |
+
* PEAR class and PEAR_Error class
|
6 |
+
*
|
7 |
+
* PHP versions 4 and 5
|
8 |
+
*
|
9 |
+
* LICENSE: This source file is subject to version 3.0 of the PHP license
|
10 |
+
* that is available through the world-wide-web at the following URI:
|
11 |
+
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
|
12 |
+
* the PHP License and are unable to obtain it through the web, please
|
13 |
+
* send a note to license@php.net so we can mail you a copy immediately.
|
14 |
+
*
|
15 |
+
* @category pear
|
16 |
+
* @package PEAR
|
17 |
+
* @author Sterling Hughes <sterling@php.net>
|
18 |
+
* @author Stig Bakken <ssb@php.net>
|
19 |
+
* @author Tomas V.V.Cox <cox@idecnet.com>
|
20 |
+
* @author Greg Beaver <cellog@php.net>
|
21 |
+
* @copyright 1997-2008 The PHP Group
|
22 |
+
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
23 |
+
* @version CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
|
24 |
+
* @link http://pear.php.net/package/PEAR
|
25 |
+
* @since File available since Release 0.1
|
26 |
+
*/
|
27 |
+
|
28 |
+
/**#@+
|
29 |
+
* ERROR constants
|
30 |
+
*/
|
31 |
+
define('PEAR_ERROR_RETURN', 1);
|
32 |
+
define('PEAR_ERROR_PRINT', 2);
|
33 |
+
define('PEAR_ERROR_TRIGGER', 4);
|
34 |
+
define('PEAR_ERROR_DIE', 8);
|
35 |
+
define('PEAR_ERROR_CALLBACK', 16);
|
36 |
+
/**
|
37 |
+
* WARNING: obsolete
|
38 |
+
* @deprecated
|
39 |
+
*/
|
40 |
+
define('PEAR_ERROR_EXCEPTION', 32);
|
41 |
+
/**#@-*/
|
42 |
+
define('PEAR_ZE2', (function_exists('version_compare') &&
|
43 |
+
version_compare(zend_version(), "2-dev", "ge")));
|
44 |
+
|
45 |
+
if (substr(PHP_OS, 0, 3) == 'WIN') {
|
46 |
+
define('OS_WINDOWS', true);
|
47 |
+
define('OS_UNIX', false);
|
48 |
+
define('PEAR_OS', 'Windows');
|
49 |
+
} else {
|
50 |
+
define('OS_WINDOWS', false);
|
51 |
+
define('OS_UNIX', true);
|
52 |
+
define('PEAR_OS', 'Unix'); // blatant assumption
|
53 |
+
}
|
54 |
+
|
55 |
+
// instant backwards compatibility
|
56 |
+
if (!defined('PATH_SEPARATOR')) {
|
57 |
+
if (OS_WINDOWS) {
|
58 |
+
define('PATH_SEPARATOR', ';');
|
59 |
+
} else {
|
60 |
+
define('PATH_SEPARATOR', ':');
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
|
65 |
+
$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
|
66 |
+
$GLOBALS['_PEAR_destructor_object_list'] = array();
|
67 |
+
$GLOBALS['_PEAR_shutdown_funcs'] = array();
|
68 |
+
$GLOBALS['_PEAR_error_handler_stack'] = array();
|
69 |
+
|
70 |
+
@ini_set('track_errors', true);
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Base class for other PEAR classes. Provides rudimentary
|
74 |
+
* emulation of destructors.
|
75 |
+
*
|
76 |
+
* If you want a destructor in your class, inherit PEAR and make a
|
77 |
+
* destructor method called _yourclassname (same name as the
|
78 |
+
* constructor, but with a "_" prefix). Also, in your constructor you
|
79 |
+
* have to call the PEAR constructor: $this->PEAR();.
|
80 |
+
* The destructor method will be called without parameters. Note that
|
81 |
+
* at in some SAPI implementations (such as Apache), any output during
|
82 |
+
* the request shutdown (in which destructors are called) seems to be
|
83 |
+
* discarded. If you need to get any debug information from your
|
84 |
+
* destructor, use error_log(), syslog() or something similar.
|
85 |
+
*
|
86 |
+
* IMPORTANT! To use the emulated destructors you need to create the
|
87 |
+
* objects by reference: $obj =& new PEAR_child;
|
88 |
+
*
|
89 |
+
* @category pear
|
90 |
+
* @package PEAR
|
91 |
+
* @author Stig Bakken <ssb@php.net>
|
92 |
+
* @author Tomas V.V. Cox <cox@idecnet.com>
|
93 |
+
* @author Greg Beaver <cellog@php.net>
|
94 |
+
* @copyright 1997-2006 The PHP Group
|
95 |
+
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
96 |
+
* @version Release: 1.7.2
|
97 |
+
* @link http://pear.php.net/package/PEAR
|
98 |
+
* @see PEAR_Error
|
99 |
+
* @since Class available since PHP 4.0.2
|
100 |
+
* @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
|
101 |
+
*/
|
102 |
+
class PEAR
|
103 |
+
{
|
104 |
+
// {{{ properties
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Whether to enable internal debug messages.
|
108 |
+
*
|
109 |
+
* @var bool
|
110 |
+
* @access private
|
111 |
+
*/
|
112 |
+
var $_debug = false;
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Default error mode for this object.
|
116 |
+
*
|
117 |
+
* @var int
|
118 |
+
* @access private
|
119 |
+
*/
|
120 |
+
var $_default_error_mode = null;
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Default error options used for this object when error mode
|
124 |
+
* is PEAR_ERROR_TRIGGER.
|
125 |
+
*
|
126 |
+
* @var int
|
127 |
+
* @access private
|
128 |
+
*/
|
129 |
+
var $_default_error_options = null;
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Default error handler (callback) for this object, if error mode is
|
133 |
+
* PEAR_ERROR_CALLBACK.
|
134 |
+
*
|
135 |
+
* @var string
|
136 |
+
* @access private
|
137 |
+
*/
|
138 |
+
var $_default_error_handler = '';
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Which class to use for error objects.
|
142 |
+
*
|
143 |
+
* @var string
|
144 |
+
* @access private
|
145 |
+
*/
|
146 |
+
var $_error_class = 'PEAR_Error';
|
147 |
+
|
148 |
+
/**
|
149 |
+
* An array of expected errors.
|
150 |
+
*
|
151 |
+
* @var array
|
152 |
+
* @access private
|
153 |
+
*/
|
154 |
+
var $_expected_errors = array();
|
155 |
+
|
156 |
+
// }}}
|
157 |
+
|
158 |
+
// {{{ constructor
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Constructor. Registers this object in
|
162 |
+
* $_PEAR_destructor_object_list for destructor emulation if a
|
163 |
+
* destructor object exists.
|
164 |
+
*
|
165 |
+
* @param string $error_class (optional) which class to use for
|
166 |
+
* error objects, defaults to PEAR_Error.
|
167 |
+
* @access public
|
168 |
+
* @return void
|
169 |
+
*/
|
170 |
+
function PEAR($error_class = null)
|
171 |
+
{
|
172 |
+
$classname = strtolower(get_class($this));
|
173 |
+
if ($this->_debug) {
|
174 |
+
print "PEAR constructor called, class=$classname\n";
|
175 |
+
}
|
176 |
+
if ($error_class !== null) {
|
177 |
+
$this->_error_class = $error_class;
|
178 |
+
}
|
179 |
+
while ($classname && strcasecmp($classname, "pear")) {
|
180 |
+
$destructor = "_$classname";
|
181 |
+
if (method_exists($this, $destructor)) {
|
182 |
+
global $_PEAR_destructor_object_list;
|
183 |
+
$_PEAR_destructor_object_list[] = &$this;
|
184 |
+
if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
|
185 |
+
register_shutdown_function("_PEAR_call_destructors");
|
186 |
+
$GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
|
187 |
+
}
|
188 |
+
break;
|
189 |
+
} else {
|
190 |
+
$classname = get_parent_class($classname);
|
191 |
+
}
|
192 |
+
}
|
193 |
+
}
|
194 |
+
|
195 |
+
// }}}
|
196 |
+
// {{{ destructor
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Destructor (the emulated type of...). Does nothing right now,
|
200 |
+
* but is included for forward compatibility, so subclass
|
201 |
+
* destructors should always call it.
|
202 |
+
*
|
203 |
+
* See the note in the class desciption about output from
|
204 |
+
* destructors.
|
205 |
+
*
|
206 |
+
* @access public
|
207 |
+
* @return void
|
208 |
+
*/
|
209 |
+
function _PEAR() {
|
210 |
+
if ($this->_debug) {
|
211 |
+
printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
// }}}
|
216 |
+
// {{{ getStaticProperty()
|
217 |
+
|
218 |
+
/**
|
219 |
+
* If you have a class that's mostly/entirely static, and you need static
|
220 |
+
* properties, you can use this method to simulate them. Eg. in your method(s)
|
221 |
+
* do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
|
222 |
+
* You MUST use a reference, or they will not persist!
|
223 |
+
*
|
224 |
+
* @access public
|
225 |
+
* @param string $class The calling classname, to prevent clashes
|
226 |
+
* @param string $var The variable to retrieve.
|
227 |
+
* @return mixed A reference to the variable. If not set it will be
|
228 |
+
* auto initialised to NULL.
|
229 |
+
*/
|
230 |
+
public static function &getStaticProperty($class, $var)
|
231 |
+
{
|
232 |
+
static $properties;
|
233 |
+
if (!isset($properties[$class])) {
|
234 |
+
$properties[$class] = array();
|
235 |
+
}
|
236 |
+
if (!array_key_exists($var, $properties[$class])) {
|
237 |
+
$properties[$class][$var] = null;
|
238 |
+
}
|
239 |
+
return $properties[$class][$var];
|
240 |
+
}
|
241 |
+
|
242 |
+
// }}}
|
243 |
+
// {{{ registerShutdownFunc()
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Use this function to register a shutdown method for static
|
247 |
+
* classes.
|
248 |
+
*
|
249 |
+
* @access public
|
250 |
+
* @param mixed $func The function name (or array of class/method) to call
|
251 |
+
* @param mixed $args The arguments to pass to the function
|
252 |
+
* @return void
|
253 |
+
*/
|
254 |
+
function registerShutdownFunc($func, $args = array())
|
255 |
+
{
|
256 |
+
// if we are called statically, there is a potential
|
257 |
+
// that no shutdown func is registered. Bug #6445
|
258 |
+
if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
|
259 |
+
register_shutdown_function("_PEAR_call_destructors");
|
260 |
+
$GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
|
261 |
+
}
|
262 |
+
$GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
|
263 |
+
}
|
264 |
+
|
265 |
+
// }}}
|
266 |
+
// {{{ isError()
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Tell whether a value is a PEAR error.
|
270 |
+
*
|
271 |
+
* @param mixed $data the value to test
|
272 |
+
* @param int $code if $data is an error object, return true
|
273 |
+
* only if $code is a string and
|
274 |
+
* $obj->getMessage() == $code or
|
275 |
+
* $code is an integer and $obj->getCode() == $code
|
276 |
+
* @access public
|
277 |
+
* @return bool true if parameter is an error
|
278 |
+
*/
|
279 |
+
public static function isError($data, $code = null)
|
280 |
+
{
|
281 |
+
if ($data instanceof PEAR_Error) {
|
282 |
+
if (is_null($code)) {
|
283 |
+
return true;
|
284 |
+
} elseif (is_string($code)) {
|
285 |
+
return $data->getMessage() == $code;
|
286 |
+
} else {
|
287 |
+
return $data->getCode() == $code;
|
288 |
+
}
|
289 |
+
}
|
290 |
+
return false;
|
291 |
+
}
|
292 |
+
|
293 |
+
// }}}
|
294 |
+
// {{{ setErrorHandling()
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Sets how errors generated by this object should be handled.
|
298 |
+
* Can be invoked both in objects and statically. If called
|
299 |
+
* statically, setErrorHandling sets the default behaviour for all
|
300 |
+
* PEAR objects. If called in an object, setErrorHandling sets
|
301 |
+
* the default behaviour for that object.
|
302 |
+
*
|
303 |
+
* @param int $mode
|
304 |
+
* One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
|
305 |
+
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
|
306 |
+
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
|
307 |
+
*
|
308 |
+
* @param mixed $options
|
309 |
+
* When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
|
310 |
+
* of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
|
311 |
+
*
|
312 |
+
* When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
|
313 |
+
* to be the callback function or method. A callback
|
314 |
+
* function is a string with the name of the function, a
|
315 |
+
* callback method is an array of two elements: the element
|
316 |
+
* at index 0 is the object, and the element at index 1 is
|
317 |
+
* the name of the method to call in the object.
|
318 |
+
*
|
319 |
+
* When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
|
320 |
+
* a printf format string used when printing the error
|
321 |
+
* message.
|
322 |
+
*
|
323 |
+
* @access public
|
324 |
+
* @return void
|
325 |
+
* @see PEAR_ERROR_RETURN
|
326 |
+
* @see PEAR_ERROR_PRINT
|
327 |
+
* @see PEAR_ERROR_TRIGGER
|
328 |
+
* @see PEAR_ERROR_DIE
|
329 |
+
* @see PEAR_ERROR_CALLBACK
|
330 |
+
* @see PEAR_ERROR_EXCEPTION
|
331 |
+
*
|
332 |
+
* @since PHP 4.0.5
|
333 |
+
*/
|
334 |
+
|
335 |
+
function setErrorHandling($mode = null, $options = null)
|
336 |
+
{
|
337 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
338 |
+
$setmode = &$this->_default_error_mode;
|
339 |
+
$setoptions = &$this->_default_error_options;
|
340 |
+
} else {
|
341 |
+
$setmode = &$GLOBALS['_PEAR_default_error_mode'];
|
342 |
+
$setoptions = &$GLOBALS['_PEAR_default_error_options'];
|
343 |
+
}
|
344 |
+
|
345 |
+
switch ($mode) {
|
346 |
+
case PEAR_ERROR_EXCEPTION:
|
347 |
+
case PEAR_ERROR_RETURN:
|
348 |
+
case PEAR_ERROR_PRINT:
|
349 |
+
case PEAR_ERROR_TRIGGER:
|
350 |
+
case PEAR_ERROR_DIE:
|
351 |
+
case null:
|
352 |
+
$setmode = $mode;
|
353 |
+
$setoptions = $options;
|
354 |
+
break;
|
355 |
+
|
356 |
+
case PEAR_ERROR_CALLBACK:
|
357 |
+
$setmode = $mode;
|
358 |
+
// class/object method callback
|
359 |
+
if (is_callable($options)) {
|
360 |
+
$setoptions = $options;
|
361 |
+
} else {
|
362 |
+
trigger_error("invalid error callback", E_USER_WARNING);
|
363 |
+
}
|
364 |
+
break;
|
365 |
+
|
366 |
+
default:
|
367 |
+
trigger_error("invalid error mode", E_USER_WARNING);
|
368 |
+
break;
|
369 |
+
}
|
370 |
+
}
|
371 |
+
|
372 |
+
// }}}
|
373 |
+
// {{{ expectError()
|
374 |
+
|
375 |
+
/**
|
376 |
+
* This method is used to tell which errors you expect to get.
|
377 |
+
* Expected errors are always returned with error mode
|
378 |
+
* PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
|
379 |
+
* and this method pushes a new element onto it. The list of
|
380 |
+
* expected errors are in effect until they are popped off the
|
381 |
+
* stack with the popExpect() method.
|
382 |
+
*
|
383 |
+
* Note that this method can not be called statically
|
384 |
+
*
|
385 |
+
* @param mixed $code a single error code or an array of error codes to expect
|
386 |
+
*
|
387 |
+
* @return int the new depth of the "expected errors" stack
|
388 |
+
* @access public
|
389 |
+
*/
|
390 |
+
function expectError($code = '*')
|
391 |
+
{
|
392 |
+
if (is_array($code)) {
|
393 |
+
array_push($this->_expected_errors, $code);
|
394 |
+
} else {
|
395 |
+
array_push($this->_expected_errors, array($code));
|
396 |
+
}
|
397 |
+
return sizeof($this->_expected_errors);
|
398 |
+
}
|
399 |
+
|
400 |
+
// }}}
|
401 |
+
// {{{ popExpect()
|
402 |
+
|
403 |
+
/**
|
404 |
+
* This method pops one element off the expected error codes
|
405 |
+
* stack.
|
406 |
+
*
|
407 |
+
* @return array the list of error codes that were popped
|
408 |
+
*/
|
409 |
+
function popExpect()
|
410 |
+
{
|
411 |
+
return array_pop($this->_expected_errors);
|
412 |
+
}
|
413 |
+
|
414 |
+
// }}}
|
415 |
+
// {{{ _checkDelExpect()
|
416 |
+
|
417 |
+
/**
|
418 |
+
* This method checks unsets an error code if available
|
419 |
+
*
|
420 |
+
* @param mixed error code
|
421 |
+
* @return bool true if the error code was unset, false otherwise
|
422 |
+
* @access private
|
423 |
+
* @since PHP 4.3.0
|
424 |
+
*/
|
425 |
+
function _checkDelExpect($error_code)
|
426 |
+
{
|
427 |
+
$deleted = false;
|
428 |
+
|
429 |
+
foreach ($this->_expected_errors AS $key => $error_array) {
|
430 |
+
if (in_array($error_code, $error_array)) {
|
431 |
+
unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
|
432 |
+
$deleted = true;
|
433 |
+
}
|
434 |
+
|
435 |
+
// clean up empty arrays
|
436 |
+
if (0 == count($this->_expected_errors[$key])) {
|
437 |
+
unset($this->_expected_errors[$key]);
|
438 |
+
}
|
439 |
+
}
|
440 |
+
return $deleted;
|
441 |
+
}
|
442 |
+
|
443 |
+
// }}}
|
444 |
+
// {{{ delExpect()
|
445 |
+
|
446 |
+
/**
|
447 |
+
* This method deletes all occurences of the specified element from
|
448 |
+
* the expected error codes stack.
|
449 |
+
*
|
450 |
+
* @param mixed $error_code error code that should be deleted
|
451 |
+
* @return mixed list of error codes that were deleted or error
|
452 |
+
* @access public
|
453 |
+
* @since PHP 4.3.0
|
454 |
+
*/
|
455 |
+
function delExpect($error_code)
|
456 |
+
{
|
457 |
+
$deleted = false;
|
458 |
+
|
459 |
+
if ((is_array($error_code) && (0 != count($error_code)))) {
|
460 |
+
// $error_code is a non-empty array here;
|
461 |
+
// we walk through it trying to unset all
|
462 |
+
// values
|
463 |
+
foreach($error_code as $key => $error) {
|
464 |
+
if ($this->_checkDelExpect($error)) {
|
465 |
+
$deleted = true;
|
466 |
+
} else {
|
467 |
+
$deleted = false;
|
468 |
+
}
|
469 |
+
}
|
470 |
+
return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
|
471 |
+
} elseif (!empty($error_code)) {
|
472 |
+
// $error_code comes alone, trying to unset it
|
473 |
+
if ($this->_checkDelExpect($error_code)) {
|
474 |
+
return true;
|
475 |
+
} else {
|
476 |
+
return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
|
477 |
+
}
|
478 |
+
} else {
|
479 |
+
// $error_code is empty
|
480 |
+
return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
|
481 |
+
}
|
482 |
+
}
|
483 |
+
|
484 |
+
// }}}
|
485 |
+
// {{{ raiseError()
|
486 |
+
|
487 |
+
/**
|
488 |
+
* This method is a wrapper that returns an instance of the
|
489 |
+
* configured error class with this object's default error
|
490 |
+
* handling applied. If the $mode and $options parameters are not
|
491 |
+
* specified, the object's defaults are used.
|
492 |
+
*
|
493 |
+
* @param mixed $message a text error message or a PEAR error object
|
494 |
+
*
|
495 |
+
* @param int $code a numeric error code (it is up to your class
|
496 |
+
* to define these if you want to use codes)
|
497 |
+
*
|
498 |
+
* @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
|
499 |
+
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
|
500 |
+
* PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
|
501 |
+
*
|
502 |
+
* @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
|
503 |
+
* specifies the PHP-internal error level (one of
|
504 |
+
* E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
|
505 |
+
* If $mode is PEAR_ERROR_CALLBACK, this
|
506 |
+
* parameter specifies the callback function or
|
507 |
+
* method. In other error modes this parameter
|
508 |
+
* is ignored.
|
509 |
+
*
|
510 |
+
* @param string $userinfo If you need to pass along for example debug
|
511 |
+
* information, this parameter is meant for that.
|
512 |
+
*
|
513 |
+
* @param string $error_class The returned error object will be
|
514 |
+
* instantiated from this class, if specified.
|
515 |
+
*
|
516 |
+
* @param bool $skipmsg If true, raiseError will only pass error codes,
|
517 |
+
* the error message parameter will be dropped.
|
518 |
+
*
|
519 |
+
* @access public
|
520 |
+
* @return object a PEAR error object
|
521 |
+
* @see PEAR::setErrorHandling
|
522 |
+
* @since PHP 4.0.5
|
523 |
+
*/
|
524 |
+
public static function raiseError($message = null,
|
525 |
+
$code = null,
|
526 |
+
$mode = null,
|
527 |
+
$options = null,
|
528 |
+
$userinfo = null,
|
529 |
+
$error_class = null,
|
530 |
+
$skipmsg = false)
|
531 |
+
{
|
532 |
+
// The error is yet a PEAR error object
|
533 |
+
if (is_object($message)) {
|
534 |
+
$code = $message->getCode();
|
535 |
+
$userinfo = $message->getUserInfo();
|
536 |
+
$error_class = $message->getType();
|
537 |
+
$message->error_message_prefix = '';
|
538 |
+
$message = $message->getMessage();
|
539 |
+
}
|
540 |
+
|
541 |
+
if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
|
542 |
+
if ($exp[0] == "*" ||
|
543 |
+
(is_int(reset($exp)) && in_array($code, $exp)) ||
|
544 |
+
(is_string(reset($exp)) && in_array($message, $exp))) {
|
545 |
+
$mode = PEAR_ERROR_RETURN;
|
546 |
+
}
|
547 |
+
}
|
548 |
+
// No mode given, try global ones
|
549 |
+
if ($mode === null) {
|
550 |
+
// Class error handler
|
551 |
+
if (isset($this) && isset($this->_default_error_mode)) {
|
552 |
+
$mode = $this->_default_error_mode;
|
553 |
+
$options = $this->_default_error_options;
|
554 |
+
// Global error handler
|
555 |
+
} elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
|
556 |
+
$mode = $GLOBALS['_PEAR_default_error_mode'];
|
557 |
+
$options = $GLOBALS['_PEAR_default_error_options'];
|
558 |
+
}
|
559 |
+
}
|
560 |
+
|
561 |
+
if ($error_class !== null) {
|
562 |
+
$ec = $error_class;
|
563 |
+
} elseif (isset($this) && isset($this->_error_class)) {
|
564 |
+
$ec = $this->_error_class;
|
565 |
+
} else {
|
566 |
+
$ec = 'PEAR_Error';
|
567 |
+
}
|
568 |
+
if (intval(PHP_VERSION) < 5) {
|
569 |
+
// little non-eval hack to fix bug #12147
|
570 |
+
include 'PEAR/FixPHP5PEARWarnings.php';
|
571 |
+
return $a;
|
572 |
+
}
|
573 |
+
if ($skipmsg) {
|
574 |
+
$a = new $ec($code, $mode, $options, $userinfo);
|
575 |
+
} else {
|
576 |
+
$a = new $ec($message, $code, $mode, $options, $userinfo);
|
577 |
+
}
|
578 |
+
return $a;
|
579 |
+
}
|
580 |
+
|
581 |
+
// }}}
|
582 |
+
// {{{ throwError()
|
583 |
+
|
584 |
+
/**
|
585 |
+
* Simpler form of raiseError with fewer options. In most cases
|
586 |
+
* message, code and userinfo are enough.
|
587 |
+
*
|
588 |
+
* @param string $message
|
589 |
+
*
|
590 |
+
*/
|
591 |
+
function &throwError($message = null,
|
592 |
+
$code = null,
|
593 |
+
$userinfo = null)
|
594 |
+
{
|
595 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
596 |
+
$a = &$this->raiseError($message, $code, null, null, $userinfo);
|
597 |
+
return $a;
|
598 |
+
} else {
|
599 |
+
$a = &PEAR::raiseError($message, $code, null, null, $userinfo);
|
600 |
+
return $a;
|
601 |
+
}
|
602 |
+
}
|
603 |
+
|
604 |
+
// }}}
|
605 |
+
function staticPushErrorHandling($mode, $options = null)
|
606 |
+
{
|
607 |
+
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
|
608 |
+
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
|
609 |
+
$def_options = &$GLOBALS['_PEAR_default_error_options'];
|
610 |
+
$stack[] = array($def_mode, $def_options);
|
611 |
+
switch ($mode) {
|
612 |
+
case PEAR_ERROR_EXCEPTION:
|
613 |
+
case PEAR_ERROR_RETURN:
|
614 |
+
case PEAR_ERROR_PRINT:
|
615 |
+
case PEAR_ERROR_TRIGGER:
|
616 |
+
case PEAR_ERROR_DIE:
|
617 |
+
case null:
|
618 |
+
$def_mode = $mode;
|
619 |
+
$def_options = $options;
|
620 |
+
break;
|
621 |
+
|
622 |
+
case PEAR_ERROR_CALLBACK:
|
623 |
+
$def_mode = $mode;
|
624 |
+
// class/object method callback
|
625 |
+
if (is_callable($options)) {
|
626 |
+
$def_options = $options;
|
627 |
+
} else {
|
628 |
+
trigger_error("invalid error callback", E_USER_WARNING);
|
629 |
+
}
|
630 |
+
break;
|
631 |
+
|
632 |
+
default:
|
633 |
+
trigger_error("invalid error mode", E_USER_WARNING);
|
634 |
+
break;
|
635 |
+
}
|
636 |
+
$stack[] = array($mode, $options);
|
637 |
+
return true;
|
638 |
+
}
|
639 |
+
|
640 |
+
function staticPopErrorHandling()
|
641 |
+
{
|
642 |
+
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
|
643 |
+
$setmode = &$GLOBALS['_PEAR_default_error_mode'];
|
644 |
+
$setoptions = &$GLOBALS['_PEAR_default_error_options'];
|
645 |
+
array_pop($stack);
|
646 |
+
list($mode, $options) = $stack[sizeof($stack) - 1];
|
647 |
+
array_pop($stack);
|
648 |
+
switch ($mode) {
|
649 |
+
case PEAR_ERROR_EXCEPTION:
|
650 |
+
case PEAR_ERROR_RETURN:
|
651 |
+
case PEAR_ERROR_PRINT:
|
652 |
+
case PEAR_ERROR_TRIGGER:
|
653 |
+
case PEAR_ERROR_DIE:
|
654 |
+
case null:
|
655 |
+
$setmode = $mode;
|
656 |
+
$setoptions = $options;
|
657 |
+
break;
|
658 |
+
|
659 |
+
case PEAR_ERROR_CALLBACK:
|
660 |
+
$setmode = $mode;
|
661 |
+
// class/object method callback
|
662 |
+
if (is_callable($options)) {
|
663 |
+
$setoptions = $options;
|
664 |
+
} else {
|
665 |
+
trigger_error("invalid error callback", E_USER_WARNING);
|
666 |
+
}
|
667 |
+
break;
|
668 |
+
|
669 |
+
default:
|
670 |
+
trigger_error("invalid error mode", E_USER_WARNING);
|
671 |
+
break;
|
672 |
+
}
|
673 |
+
return true;
|
674 |
+
}
|
675 |
+
|
676 |
+
// {{{ pushErrorHandling()
|
677 |
+
|
678 |
+
/**
|
679 |
+
* Push a new error handler on top of the error handler options stack. With this
|
680 |
+
* you can easily override the actual error handler for some code and restore
|
681 |
+
* it later with popErrorHandling.
|
682 |
+
*
|
683 |
+
* @param mixed $mode (same as setErrorHandling)
|
684 |
+
* @param mixed $options (same as setErrorHandling)
|
685 |
+
*
|
686 |
+
* @return bool Always true
|
687 |
+
*
|
688 |
+
* @see PEAR::setErrorHandling
|
689 |
+
*/
|
690 |
+
function pushErrorHandling($mode, $options = null)
|
691 |
+
{
|
692 |
+
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
|
693 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
694 |
+
$def_mode = &$this->_default_error_mode;
|
695 |
+
$def_options = &$this->_default_error_options;
|
696 |
+
} else {
|
697 |
+
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
|
698 |
+
$def_options = &$GLOBALS['_PEAR_default_error_options'];
|
699 |
+
}
|
700 |
+
$stack[] = array($def_mode, $def_options);
|
701 |
+
|
702 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
703 |
+
$this->setErrorHandling($mode, $options);
|
704 |
+
} else {
|
705 |
+
PEAR::setErrorHandling($mode, $options);
|
706 |
+
}
|
707 |
+
$stack[] = array($mode, $options);
|
708 |
+
return true;
|
709 |
+
}
|
710 |
+
|
711 |
+
// }}}
|
712 |
+
// {{{ popErrorHandling()
|
713 |
+
|
714 |
+
/**
|
715 |
+
* Pop the last error handler used
|
716 |
+
*
|
717 |
+
* @return bool Always true
|
718 |
+
*
|
719 |
+
* @see PEAR::pushErrorHandling
|
720 |
+
*/
|
721 |
+
function popErrorHandling()
|
722 |
+
{
|
723 |
+
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
|
724 |
+
array_pop($stack);
|
725 |
+
list($mode, $options) = $stack[sizeof($stack) - 1];
|
726 |
+
array_pop($stack);
|
727 |
+
if (isset($this) && is_a($this, 'PEAR')) {
|
728 |
+
$this->setErrorHandling($mode, $options);
|
729 |
+
} else {
|
730 |
+
PEAR::setErrorHandling($mode, $options);
|
731 |
+
}
|
732 |
+
return true;
|
733 |
+
}
|
734 |
+
|
735 |
+
// }}}
|
736 |
+
// {{{ loadExtension()
|
737 |
+
|
738 |
+
/**
|
739 |
+
* OS independant PHP extension load. Remember to take care
|
740 |
+
* on the correct extension name for case sensitive OSes.
|
741 |
+
*
|
742 |
+
* @param string $ext The extension name
|
743 |
+
* @return bool Success or not on the dl() call
|
744 |
+
*/
|
745 |
+
function loadExtension($ext)
|
746 |
+
{
|
747 |
+
if (!extension_loaded($ext)) {
|
748 |
+
// if either returns true dl() will produce a FATAL error, stop that
|
749 |
+
if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
|
750 |
+
return false;
|
751 |
+
}
|
752 |
+
if (OS_WINDOWS) {
|
753 |
+
$suffix = '.dll';
|
754 |
+
} elseif (PHP_OS == 'HP-UX') {
|
755 |
+
$suffix = '.sl';
|
756 |
+
} elseif (PHP_OS == 'AIX') {
|
757 |
+
$suffix = '.a';
|
758 |
+
} elseif (PHP_OS == 'OSX') {
|
759 |
+
$suffix = '.bundle';
|
760 |
+
} else {
|
761 |
+
$suffix = '.so';
|
762 |
+
}
|
763 |
+
return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
|
764 |
+
}
|
765 |
+
return true;
|
766 |
+
}
|
767 |
+
|
768 |
+
// }}}
|
769 |
+
}
|
770 |
+
|
771 |
+
// {{{ _PEAR_call_destructors()
|
772 |
+
|
773 |
+
function _PEAR_call_destructors()
|
774 |
+
{
|
775 |
+
global $_PEAR_destructor_object_list;
|
776 |
+
if (is_array($_PEAR_destructor_object_list) &&
|
777 |
+
sizeof($_PEAR_destructor_object_list))
|
778 |
+
{
|
779 |
+
reset($_PEAR_destructor_object_list);
|
780 |
+
if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
|
781 |
+
$_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
|
782 |
+
}
|
783 |
+
while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
|
784 |
+
$classname = get_class($objref);
|
785 |
+
while ($classname) {
|
786 |
+
$destructor = "_$classname";
|
787 |
+
if (method_exists($objref, $destructor)) {
|
788 |
+
$objref->$destructor();
|
789 |
+
break;
|
790 |
+
} else {
|
791 |
+
$classname = get_parent_class($classname);
|
792 |
+
}
|
793 |
+
}
|
794 |
+
}
|
795 |
+
// Empty the object list to ensure that destructors are
|
796 |
+
// not called more than once.
|
797 |
+
$_PEAR_destructor_object_list = array();
|
798 |
+
}
|
799 |
+
|
800 |
+
// Now call the shutdown functions
|
801 |
+
if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
|
802 |
+
foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
|
803 |
+
call_user_func_array($value[0], $value[1]);
|
804 |
+
}
|
805 |
+
}
|
806 |
+
}
|
807 |
+
|
808 |
+
// }}}
|
809 |
+
/**
|
810 |
+
* Standard PEAR error class for PHP 4
|
811 |
+
*
|
812 |
+
* This class is supserseded by {@link PEAR_Exception} in PHP 5
|
813 |
+
*
|
814 |
+
* @category pear
|
815 |
+
* @package PEAR
|
816 |
+
* @author Stig Bakken <ssb@php.net>
|
817 |
+
* @author Tomas V.V. Cox <cox@idecnet.com>
|
818 |
+
* @author Gregory Beaver <cellog@php.net>
|
819 |
+
* @copyright 1997-2006 The PHP Group
|
820 |
+
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
821 |
+
* @version Release: 1.7.2
|
822 |
+
* @link http://pear.php.net/manual/en/core.pear.pear-error.php
|
823 |
+
* @see PEAR::raiseError(), PEAR::throwError()
|
824 |
+
* @since Class available since PHP 4.0.2
|
825 |
+
*/
|
826 |
+
class PEAR_Error
|
827 |
+
{
|
828 |
+
// {{{ properties
|
829 |
+
|
830 |
+
var $error_message_prefix = '';
|
831 |
+
var $mode = PEAR_ERROR_RETURN;
|
832 |
+
var $level = E_USER_NOTICE;
|
833 |
+
var $code = -1;
|
834 |
+
var $message = '';
|
835 |
+
var $userinfo = '';
|
836 |
+
var $backtrace = null;
|
837 |
+
|
838 |
+
// }}}
|
839 |
+
// {{{ constructor
|
840 |
+
|
841 |
+
/**
|
842 |
+
* PEAR_Error constructor
|
843 |
+
*
|
844 |
+
* @param string $message message
|
845 |
+
*
|
846 |
+
* @param int $code (optional) error code
|
847 |
+
*
|
848 |
+
* @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
|
849 |
+
* PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
|
850 |
+
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
|
851 |
+
*
|
852 |
+
* @param mixed $options (optional) error level, _OR_ in the case of
|
853 |
+
* PEAR_ERROR_CALLBACK, the callback function or object/method
|
854 |
+
* tuple.
|
855 |
+
*
|
856 |
+
* @param string $userinfo (optional) additional user/debug info
|
857 |
+
*
|
858 |
+
* @access public
|
859 |
+
*
|
860 |
+
*/
|
861 |
+
function PEAR_Error($message = 'unknown error', $code = null,
|
862 |
+
$mode = null, $options = null, $userinfo = null)
|
863 |
+
{
|
864 |
+
if ($mode === null) {
|
865 |
+
$mode = PEAR_ERROR_RETURN;
|
866 |
+
}
|
867 |
+
$this->message = $message;
|
868 |
+
$this->code = $code;
|
869 |
+
$this->mode = $mode;
|
870 |
+
$this->userinfo = $userinfo;
|
871 |
+
if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
|
872 |
+
$this->backtrace = debug_backtrace();
|
873 |
+
if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
|
874 |
+
unset($this->backtrace[0]['object']);
|
875 |
+
}
|
876 |
+
}
|
877 |
+
if ($mode & PEAR_ERROR_CALLBACK) {
|
878 |
+
$this->level = E_USER_NOTICE;
|
879 |
+
$this->callback = $options;
|
880 |
+
} else {
|
881 |
+
if ($options === null) {
|
882 |
+
$options = E_USER_NOTICE;
|
883 |
+
}
|
884 |
+
$this->level = $options;
|
885 |
+
$this->callback = null;
|
886 |
+
}
|
887 |
+
if ($this->mode & PEAR_ERROR_PRINT) {
|
888 |
+
if (is_null($options) || is_int($options)) {
|
889 |
+
$format = "%s";
|
890 |
+
} else {
|
891 |
+
$format = $options;
|
892 |
+
}
|
893 |
+
printf($format, $this->getMessage());
|
894 |
+
}
|
895 |
+
if ($this->mode & PEAR_ERROR_TRIGGER) {
|
896 |
+
trigger_error($this->getMessage(), $this->level);
|
897 |
+
}
|
898 |
+
if ($this->mode & PEAR_ERROR_DIE) {
|
899 |
+
$msg = $this->getMessage();
|
900 |
+
if (is_null($options) || is_int($options)) {
|
901 |
+
$format = "%s";
|
902 |
+
if (substr($msg, -1) != "\n") {
|
903 |
+
$msg .= "\n";
|
904 |
+
}
|
905 |
+
} else {
|
906 |
+
$format = $options;
|
907 |
+
}
|
908 |
+
die(sprintf($format, $msg));
|
909 |
+
}
|
910 |
+
if ($this->mode & PEAR_ERROR_CALLBACK) {
|
911 |
+
if (is_callable($this->callback)) {
|
912 |
+
call_user_func($this->callback, $this);
|
913 |
+
}
|
914 |
+
}
|
915 |
+
if ($this->mode & PEAR_ERROR_EXCEPTION) {
|
916 |
+
trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
|
917 |
+
eval('$e = new Exception($this->message, $this->code);throw($e);');
|
918 |
+
}
|
919 |
+
}
|
920 |
+
|
921 |
+
// }}}
|
922 |
+
// {{{ getMode()
|
923 |
+
|
924 |
+
/**
|
925 |
+
* Get the error mode from an error object.
|
926 |
+
*
|
927 |
+
* @return int error mode
|
928 |
+
* @access public
|
929 |
+
*/
|
930 |
+
function getMode() {
|
931 |
+
return $this->mode;
|
932 |
+
}
|
933 |
+
|
934 |
+
// }}}
|
935 |
+
// {{{ getCallback()
|
936 |
+
|
937 |
+
/**
|
938 |
+
* Get the callback function/method from an error object.
|
939 |
+
*
|
940 |
+
* @return mixed callback function or object/method array
|
941 |
+
* @access public
|
942 |
+
*/
|
943 |
+
function getCallback() {
|
944 |
+
return $this->callback;
|
945 |
+
}
|
946 |
+
|
947 |
+
// }}}
|
948 |
+
// {{{ getMessage()
|
949 |
+
|
950 |
+
|
951 |
+
/**
|
952 |
+
* Get the error message from an error object.
|
953 |
+
*
|
954 |
+
* @return string full error message
|
955 |
+
* @access public
|
956 |
+
*/
|
957 |
+
function getMessage()
|
958 |
+
{
|
959 |
+
return ($this->error_message_prefix . $this->message);
|
960 |
+
}
|
961 |
+
|
962 |
+
|
963 |
+
// }}}
|
964 |
+
// {{{ getCode()
|
965 |
+
|
966 |
+
/**
|
967 |
+
* Get error code from an error object
|
968 |
+
*
|
969 |
+
* @return int error code
|
970 |
+
* @access public
|
971 |
+
*/
|
972 |
+
function getCode()
|
973 |
+
{
|
974 |
+
return $this->code;
|
975 |
+
}
|
976 |
+
|
977 |
+
// }}}
|
978 |
+
// {{{ getType()
|
979 |
+
|
980 |
+
/**
|
981 |
+
* Get the name of this error/exception.
|
982 |
+
*
|
983 |
+
* @return string error/exception name (type)
|
984 |
+
* @access public
|
985 |
+
*/
|
986 |
+
function getType()
|
987 |
+
{
|
988 |
+
return get_class($this);
|
989 |
+
}
|
990 |
+
|
991 |
+
// }}}
|
992 |
+
// {{{ getUserInfo()
|
993 |
+
|
994 |
+
/**
|
995 |
+
* Get additional user-supplied information.
|
996 |
+
*
|
997 |
+
* @return string user-supplied information
|
998 |
+
* @access public
|
999 |
+
*/
|
1000 |
+
function getUserInfo()
|
1001 |
+
{
|
1002 |
+
return $this->userinfo;
|
1003 |
+
}
|
1004 |
+
|
1005 |
+
// }}}
|
1006 |
+
// {{{ getDebugInfo()
|
1007 |
+
|
1008 |
+
/**
|
1009 |
+
* Get additional debug information supplied by the application.
|
1010 |
+
*
|
1011 |
+
* @return string debug information
|
1012 |
+
* @access public
|
1013 |
+
*/
|
1014 |
+
function getDebugInfo()
|
1015 |
+
{
|
1016 |
+
return $this->getUserInfo();
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
// }}}
|
1020 |
+
// {{{ getBacktrace()
|
1021 |
+
|
1022 |
+
/**
|
1023 |
+
* Get the call backtrace from where the error was generated.
|
1024 |
+
* Supported with PHP 4.3.0 or newer.
|
1025 |
+
*
|
1026 |
+
* @param int $frame (optional) what frame to fetch
|
1027 |
+
* @return array Backtrace, or NULL if not available.
|
1028 |
+
* @access public
|
1029 |
+
*/
|
1030 |
+
function getBacktrace($frame = null)
|
1031 |
+
{
|
1032 |
+
if (defined('PEAR_IGNORE_BACKTRACE')) {
|
1033 |
+
return null;
|
1034 |
+
}
|
1035 |
+
if ($frame === null) {
|
1036 |
+
return $this->backtrace;
|
1037 |
+
}
|
1038 |
+
return $this->backtrace[$frame];
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
// }}}
|
1042 |
+
// {{{ addUserInfo()
|
1043 |
+
|
1044 |
+
function addUserInfo($info)
|
1045 |
+
{
|
1046 |
+
if (empty($this->userinfo)) {
|
1047 |
+
$this->userinfo = $info;
|
1048 |
+
} else {
|
1049 |
+
$this->userinfo .= " ** $info";
|
1050 |
+
}
|
1051 |
+
}
|
1052 |
+
|
1053 |
+
// }}}
|
1054 |
+
// {{{ toString()
|
1055 |
+
function __toString()
|
1056 |
+
{
|
1057 |
+
return $this->getMessage();
|
1058 |
+
}
|
1059 |
+
// }}}
|
1060 |
+
// {{{ toString()
|
1061 |
+
|
1062 |
+
/**
|
1063 |
+
* Make a string representation of this object.
|
1064 |
+
*
|
1065 |
+
* @return string a string with an object summary
|
1066 |
+
* @access public
|
1067 |
+
*/
|
1068 |
+
function toString() {
|
1069 |
+
$modes = array();
|
1070 |
+
$levels = array(E_USER_NOTICE => 'notice',
|
1071 |
+
E_USER_WARNING => 'warning',
|
1072 |
+
E_USER_ERROR => 'error');
|
1073 |
+
if ($this->mode & PEAR_ERROR_CALLBACK) {
|
1074 |
+
if (is_array($this->callback)) {
|
1075 |
+
$callback = (is_object($this->callback[0]) ?
|
1076 |
+
strtolower(get_class($this->callback[0])) :
|
1077 |
+
$this->callback[0]) . '::' .
|
1078 |
+
$this->callback[1];
|
1079 |
+
} else {
|
1080 |
+
$callback = $this->callback;
|
1081 |
+
}
|
1082 |
+
return sprintf('[%s: message="%s" code=%d mode=callback '.
|
1083 |
+
'callback=%s prefix="%s" info="%s"]',
|
1084 |
+
strtolower(get_class($this)), $this->message, $this->code,
|
1085 |
+
$callback, $this->error_message_prefix,
|
1086 |
+
$this->userinfo);
|
1087 |
+
}
|
1088 |
+
if ($this->mode & PEAR_ERROR_PRINT) {
|
1089 |
+
$modes[] = 'print';
|
1090 |
+
}
|
1091 |
+
if ($this->mode & PEAR_ERROR_TRIGGER) {
|
1092 |
+
$modes[] = 'trigger';
|
1093 |
+
}
|
1094 |
+
if ($this->mode & PEAR_ERROR_DIE) {
|
1095 |
+
$modes[] = 'die';
|
1096 |
+
}
|
1097 |
+
if ($this->mode & PEAR_ERROR_RETURN) {
|
1098 |
+
$modes[] = 'return';
|
1099 |
+
}
|
1100 |
+
return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
|
1101 |
+
'prefix="%s" info="%s"]',
|
1102 |
+
strtolower(get_class($this)), $this->message, $this->code,
|
1103 |
+
implode("|", $modes), $levels[$this->level],
|
1104 |
+
$this->error_message_prefix,
|
1105 |
+
$this->userinfo);
|
1106 |
+
}
|
1107 |
+
|
1108 |
+
// }}}
|
1109 |
+
}
|
1110 |
+
|
1111 |
+
/*
|
1112 |
+
* Local Variables:
|
1113 |
+
* mode: php
|
1114 |
+
* tab-width: 4
|
1115 |
+
* c-basic-offset: 4
|
1116 |
+
* End:
|
1117 |
+
*/
|
1118 |
+
?>
|
lib/PEAR/SOAP/Base.php
ADDED
@@ -0,0 +1,1142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file loads all required libraries, defines constants used across the
|
4 |
+
* SOAP package, and defines the base classes that most other classes of this
|
5 |
+
* package extend.
|
6 |
+
*
|
7 |
+
* PHP versions 4 and 5
|
8 |
+
*
|
9 |
+
* LICENSE: This source file is subject to version 2.02 of the PHP license,
|
10 |
+
* that is bundled with this package in the file LICENSE, and is available at
|
11 |
+
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
|
12 |
+
* did not receive a copy of the PHP license and are unable to obtain it
|
13 |
+
* through the world-wide-web, please send a note to license@php.net so we can
|
14 |
+
* mail you a copy immediately.
|
15 |
+
*
|
16 |
+
* @category Web Services
|
17 |
+
* @package SOAP
|
18 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
19 |
+
* @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
|
20 |
+
* @author Chuck Hagenbuch <chuck@horde.org> Maintenance
|
21 |
+
* @author Jan Schneider <jan@horde.org> Maintenance
|
22 |
+
* @copyright 2003-2007 The PHP Group
|
23 |
+
* @license http://www.php.net/license/2_02.txt PHP License 2.02
|
24 |
+
* @link http://pear.php.net/package/SOAP
|
25 |
+
*/
|
26 |
+
|
27 |
+
/** Define linebreak sequence for the Mail_Mime package. */
|
28 |
+
define('MAIL_MIMEPART_CRLF', "\r\n");
|
29 |
+
|
30 |
+
require_once 'PEAR.php';
|
31 |
+
|
32 |
+
if (!defined('INF')) {
|
33 |
+
define('INF', 1.8e307);
|
34 |
+
}
|
35 |
+
if (!defined('NAN')) {
|
36 |
+
define('NAN', 0.0);
|
37 |
+
}
|
38 |
+
|
39 |
+
define('SOAP_LIBRARY_VERSION', '0.12.0');
|
40 |
+
define('SOAP_LIBRARY_NAME', 'PEAR-SOAP 0.12.0-beta');
|
41 |
+
|
42 |
+
// Set schema version.
|
43 |
+
define('SOAP_XML_SCHEMA_VERSION', 'http://www.w3.org/2001/XMLSchema');
|
44 |
+
define('SOAP_XML_SCHEMA_INSTANCE', 'http://www.w3.org/2001/XMLSchema-instance');
|
45 |
+
define('SOAP_XML_SCHEMA_1999', 'http://www.w3.org/1999/XMLSchema');
|
46 |
+
define('SOAP_SCHEMA', 'http://schemas.xmlsoap.org/wsdl/soap/');
|
47 |
+
define('SOAP_SCHEMA_ENCODING', 'http://schemas.xmlsoap.org/soap/encoding/');
|
48 |
+
define('SOAP_ENVELOP', 'http://schemas.xmlsoap.org/soap/envelope/');
|
49 |
+
|
50 |
+
define('SCHEMA_DISCO', 'http://schemas.xmlsoap.org/disco/');
|
51 |
+
define('SCHEMA_DISCO_SCL', 'http://schemas.xmlsoap.org/disco/scl/');
|
52 |
+
|
53 |
+
define('SCHEMA_SOAP', 'http://schemas.xmlsoap.org/wsdl/soap/');
|
54 |
+
define('SCHEMA_SOAP12', 'http://schemas.xmlsoap.org/wsdl/soap12/');
|
55 |
+
define('SCHEMA_SOAP_HTTP', 'http://schemas.xmlsoap.org/soap/http');
|
56 |
+
define('SCHEMA_WSDL_HTTP', 'http://schemas.xmlsoap.org/wsdl/http/');
|
57 |
+
define('SCHEMA_MIME', 'http://schemas.xmlsoap.org/wsdl/mime/');
|
58 |
+
define('SCHEMA_WSDL', 'http://schemas.xmlsoap.org/wsdl/');
|
59 |
+
define('SCHEMA_DIME', 'http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/');
|
60 |
+
define('SCHEMA_CONTENT', 'http://schemas.xmlsoap.org/ws/2002/04/content-type/');
|
61 |
+
define('SCHEMA_REF', 'http://schemas.xmlsoap.org/ws/2002/04/reference/');
|
62 |
+
|
63 |
+
define('SOAP_DEFAULT_ENCODING', 'UTF-8');
|
64 |
+
|
65 |
+
/**
|
66 |
+
* @package SOAP
|
67 |
+
*/
|
68 |
+
class SOAP_Base_Object extends PEAR
|
69 |
+
{
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Supported encodings, limited by XML extension.
|
73 |
+
*
|
74 |
+
* @var array $_encodings
|
75 |
+
*/
|
76 |
+
var $_encodings = array('ISO-8859-1', 'US-ASCII', 'UTF-8');
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Fault code.
|
80 |
+
*
|
81 |
+
* @var string $_myfaultcode
|
82 |
+
*/
|
83 |
+
var $_myfaultcode = '';
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Recent PEAR_Error object.
|
87 |
+
*
|
88 |
+
* @var PEAR_Error $fault
|
89 |
+
*/
|
90 |
+
var $fault = null;
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Constructor.
|
94 |
+
*
|
95 |
+
* @param string $faultcode Error code.
|
96 |
+
*/
|
97 |
+
function SOAP_Base_Object($faultcode = 'Client')
|
98 |
+
{
|
99 |
+
$this->_myfaultcode = $faultcode;
|
100 |
+
parent::PEAR('SOAP_Fault');
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Raises a SOAP error.
|
105 |
+
*
|
106 |
+
* Please refer to the SOAP definition for an impression of what a certain
|
107 |
+
* parameter stands for.
|
108 |
+
*
|
109 |
+
* @param string|object $str Error message or object.
|
110 |
+
* @param string $detail Detailed error message.
|
111 |
+
* @param string $actorURI
|
112 |
+
* @param mixed $code
|
113 |
+
* @param mixed $mode
|
114 |
+
* @param mixed $options
|
115 |
+
* @param boolean $skipmsg
|
116 |
+
*/
|
117 |
+
function &_raiseSoapFault($str, $detail = '', $actorURI = '', $code = null,
|
118 |
+
$mode = null, $options = null, $skipmsg = false)
|
119 |
+
{
|
120 |
+
// Pass through previous faults.
|
121 |
+
$is_instance = isset($this) && $this instanceof SOAP_Base_Object;
|
122 |
+
if (is_object($str)) {
|
123 |
+
$fault = $str;
|
124 |
+
} else {
|
125 |
+
if (!$code) {
|
126 |
+
$code = $is_instance ? $this->_myfaultcode : 'Client';
|
127 |
+
}
|
128 |
+
require_once 'SOAP/Fault.php';
|
129 |
+
$fault = new SOAP_Fault($str, $code, $actorURI, $detail, $mode,
|
130 |
+
$options);
|
131 |
+
}
|
132 |
+
if ($is_instance) {
|
133 |
+
$this->fault = $fault;
|
134 |
+
}
|
135 |
+
|
136 |
+
return $fault;
|
137 |
+
}
|
138 |
+
|
139 |
+
function _isfault()
|
140 |
+
{
|
141 |
+
return $this->fault != null;
|
142 |
+
}
|
143 |
+
|
144 |
+
function &_getfault()
|
145 |
+
{
|
146 |
+
return $this->fault;
|
147 |
+
}
|
148 |
+
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Common base class of all SOAP classes.
|
153 |
+
*
|
154 |
+
* @access public
|
155 |
+
* @package SOAP
|
156 |
+
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
157 |
+
*/
|
158 |
+
class SOAP_Base extends SOAP_Base_Object
|
159 |
+
{
|
160 |
+
var $_XMLSchema = array('http://www.w3.org/2001/XMLSchema',
|
161 |
+
'http://www.w3.org/1999/XMLSchema');
|
162 |
+
var $_XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
|
163 |
+
|
164 |
+
// load types into typemap array
|
165 |
+
var $_typemap = array(
|
166 |
+
'http://www.w3.org/2001/XMLSchema' => array(
|
167 |
+
'string' => 'string',
|
168 |
+
'boolean' => 'boolean',
|
169 |
+
'float' => 'float',
|
170 |
+
'double' => 'float',
|
171 |
+
'decimal' => 'float',
|
172 |
+
'duration' => 'integer',
|
173 |
+
'dateTime' => 'string',
|
174 |
+
'time' => 'string',
|
175 |
+
'date' => 'string',
|
176 |
+
'gYearMonth' => 'integer',
|
177 |
+
'gYear' => 'integer',
|
178 |
+
'gMonthDay' => 'integer',
|
179 |
+
'gDay' => 'integer',
|
180 |
+
'gMonth' => 'integer',
|
181 |
+
'hexBinary' => 'string',
|
182 |
+
'base64Binary' => 'string',
|
183 |
+
// derived datatypes
|
184 |
+
'normalizedString' => 'string',
|
185 |
+
'token' => 'string',
|
186 |
+
'language' => 'string',
|
187 |
+
'NMTOKEN' => 'string',
|
188 |
+
'NMTOKENS' => 'string',
|
189 |
+
'Name' => 'string',
|
190 |
+
'NCName' => 'string',
|
191 |
+
'ID' => 'string',
|
192 |
+
'IDREF' => 'string',
|
193 |
+
'IDREFS' => 'string',
|
194 |
+
'ENTITY' => 'string',
|
195 |
+
'ENTITIES' => 'string',
|
196 |
+
'integer' => 'integer',
|
197 |
+
'nonPositiveInteger' => 'integer',
|
198 |
+
'negativeInteger' => 'integer',
|
199 |
+
// longs (64bit ints) are not supported cross-platform.
|
200 |
+
'long' => 'string',
|
201 |
+
'int' => 'integer',
|
202 |
+
'short' => 'integer',
|
203 |
+
'byte' => 'string',
|
204 |
+
'nonNegativeInteger' => 'integer',
|
205 |
+
'unsignedLong' => 'integer',
|
206 |
+
'unsignedInt' => 'integer',
|
207 |
+
'unsignedShort' => 'integer',
|
208 |
+
'unsignedByte' => 'integer',
|
209 |
+
'positiveInteger' => 'integer',
|
210 |
+
'anyType' => 'string',
|
211 |
+
'anyURI' => 'string',
|
212 |
+
'QName' => 'string'
|
213 |
+
),
|
214 |
+
'http://www.w3.org/1999/XMLSchema' => array(
|
215 |
+
'i4' => 'integer',
|
216 |
+
'int' => 'integer',
|
217 |
+
'boolean' => 'boolean',
|
218 |
+
'string' => 'string',
|
219 |
+
'double' => 'float',
|
220 |
+
'float' => 'float',
|
221 |
+
'dateTime' => 'string',
|
222 |
+
'timeInstant' => 'string',
|
223 |
+
'base64Binary' => 'string',
|
224 |
+
'base64' => 'string',
|
225 |
+
'ur-type' => 'string'
|
226 |
+
),
|
227 |
+
'http://schemas.xmlsoap.org/soap/encoding/' => array(
|
228 |
+
'base64' => 'string',
|
229 |
+
'array' => 'array',
|
230 |
+
'Array' => 'array',
|
231 |
+
'Struct' => 'array')
|
232 |
+
);
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Default class name to use for decoded response objects.
|
236 |
+
*
|
237 |
+
* @var string $_defaultObjectClassname
|
238 |
+
*/
|
239 |
+
var $_defaultObjectClassname = 'stdClass';
|
240 |
+
|
241 |
+
/**
|
242 |
+
* Hash with used namespaces.
|
243 |
+
*
|
244 |
+
* @var array
|
245 |
+
*/
|
246 |
+
var $_namespaces;
|
247 |
+
|
248 |
+
/**
|
249 |
+
* The default namespace.
|
250 |
+
*
|
251 |
+
* @var string
|
252 |
+
*/
|
253 |
+
var $_namespace;
|
254 |
+
|
255 |
+
var $_xmlEntities = array('&' => '&',
|
256 |
+
'<' => '<',
|
257 |
+
'>' => '>',
|
258 |
+
"'" => ''',
|
259 |
+
'"' => '"');
|
260 |
+
|
261 |
+
var $_doconversion = false;
|
262 |
+
|
263 |
+
var $_attachments = array();
|
264 |
+
|
265 |
+
var $_wsdl = null;
|
266 |
+
|
267 |
+
/**
|
268 |
+
* True if we use section 5 encoding, or false if this is literal.
|
269 |
+
*
|
270 |
+
* @var boolean $_section5
|
271 |
+
*/
|
272 |
+
var $_section5 = true;
|
273 |
+
|
274 |
+
// Handle type to class mapping.
|
275 |
+
var $_auto_translation = false;
|
276 |
+
var $_type_translation = array();
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Constructor.
|
280 |
+
*
|
281 |
+
* @param string $faultcode Error code.
|
282 |
+
*/
|
283 |
+
function SOAP_Base($faultcode = 'Client')
|
284 |
+
{
|
285 |
+
parent::SOAP_Base_Object($faultcode);
|
286 |
+
$this->_resetNamespaces();
|
287 |
+
}
|
288 |
+
|
289 |
+
/**
|
290 |
+
* Sets the SOAP-ENV prefix and returns the current value.
|
291 |
+
*
|
292 |
+
* @access public
|
293 |
+
*
|
294 |
+
* @param string SOAP-ENV prefix
|
295 |
+
*
|
296 |
+
* @return string current SOAP-ENV prefix.
|
297 |
+
*/
|
298 |
+
function SOAPENVPrefix($prefix = null)
|
299 |
+
{
|
300 |
+
static $_soapenv_prefix = 'SOAP-ENV';
|
301 |
+
if (!is_null($prefix)) {
|
302 |
+
$_soapenv_prefix = $prefix;
|
303 |
+
}
|
304 |
+
return $_soapenv_prefix;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Sets the SOAP-ENC prefix and returns the current value.
|
309 |
+
*
|
310 |
+
* @access public
|
311 |
+
*
|
312 |
+
* @param string SOAP-ENC prefix
|
313 |
+
*
|
314 |
+
* @return string current SOAP-ENC prefix.
|
315 |
+
*/
|
316 |
+
function SOAPENCPrefix($prefix = null)
|
317 |
+
{
|
318 |
+
static $_soapenv_prefix = 'SOAP-ENC';
|
319 |
+
if (!is_null($prefix)) {
|
320 |
+
$_soapenv_prefix = $prefix;
|
321 |
+
}
|
322 |
+
return $_soapenv_prefix;
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* Sets the default namespace.
|
327 |
+
*
|
328 |
+
* @param string $namespace The default namespace.
|
329 |
+
*/
|
330 |
+
function setDefaultNamespace($namespace)
|
331 |
+
{
|
332 |
+
$this->_namespace = $namespace;
|
333 |
+
}
|
334 |
+
|
335 |
+
function _resetNamespaces()
|
336 |
+
{
|
337 |
+
$this->_namespaces = array(
|
338 |
+
'http://schemas.xmlsoap.org/soap/envelope/' => SOAP_BASE::SOAPENVPrefix(),
|
339 |
+
'http://www.w3.org/2001/XMLSchema' => 'xsd',
|
340 |
+
'http://www.w3.org/2001/XMLSchema-instance' => 'xsi',
|
341 |
+
'http://schemas.xmlsoap.org/soap/encoding/' => SOAP_BASE::SOAPENCPrefix());
|
342 |
+
}
|
343 |
+
|
344 |
+
/**
|
345 |
+
* Sets the schema version used in the SOAP message.
|
346 |
+
*
|
347 |
+
* @access private
|
348 |
+
* @see $_XMLSchema
|
349 |
+
*
|
350 |
+
* @param string $schemaVersion The schema version.
|
351 |
+
*/
|
352 |
+
function _setSchemaVersion($schemaVersion)
|
353 |
+
{
|
354 |
+
if (!in_array($schemaVersion, $this->_XMLSchema)) {
|
355 |
+
return $this->_raiseSoapFault("unsuported XMLSchema $schemaVersion");
|
356 |
+
}
|
357 |
+
$this->_XMLSchemaVersion = $schemaVersion;
|
358 |
+
$tmpNS = array_flip($this->_namespaces);
|
359 |
+
$tmpNS['xsd'] = $this->_XMLSchemaVersion;
|
360 |
+
$tmpNS['xsi'] = $this->_XMLSchemaVersion . '-instance';
|
361 |
+
$this->_namespaces = array_flip($tmpNS);
|
362 |
+
}
|
363 |
+
|
364 |
+
function _getNamespacePrefix($ns)
|
365 |
+
{
|
366 |
+
if ($this->_namespace && $ns == $this->_namespace) {
|
367 |
+
return '';
|
368 |
+
}
|
369 |
+
if (isset($this->_namespaces[$ns])) {
|
370 |
+
return $this->_namespaces[$ns];
|
371 |
+
}
|
372 |
+
$prefix = 'ns' . count($this->_namespaces);
|
373 |
+
$this->_namespaces[$ns] = $prefix;
|
374 |
+
return $prefix;
|
375 |
+
}
|
376 |
+
|
377 |
+
function _getNamespaceForPrefix($prefix)
|
378 |
+
{
|
379 |
+
$flipped = array_flip($this->_namespaces);
|
380 |
+
if (isset($flipped[$prefix])) {
|
381 |
+
return $flipped[$prefix];
|
382 |
+
}
|
383 |
+
return null;
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Serializes a value, array or object according to the rules set by this
|
388 |
+
* object.
|
389 |
+
*
|
390 |
+
* @see SOAP_Value
|
391 |
+
*
|
392 |
+
* @param mixed $value The actual value.
|
393 |
+
* @param QName $name The value name.
|
394 |
+
* @param QName $type The value type.
|
395 |
+
* @param array $options A list of encoding and serialization options.
|
396 |
+
* @param array $attributes A hash of additional attributes.
|
397 |
+
* @param string $artype The type of any array elements.
|
398 |
+
*/
|
399 |
+
function _serializeValue($value, $name = null, $type = null,
|
400 |
+
$options = array(), $attributes = array(),
|
401 |
+
$artype = '')
|
402 |
+
{
|
403 |
+
$namespaces = array();
|
404 |
+
$arrayType = $array_depth = $xmlout_value = null;
|
405 |
+
$typePrefix = $elPrefix = $xmlout_arrayType = '';
|
406 |
+
$xmlout_type = $xmlns = $ptype = $array_type_ns = '';
|
407 |
+
|
408 |
+
if (!$name->name || is_numeric($name->name)) {
|
409 |
+
$name->name = 'item';
|
410 |
+
}
|
411 |
+
|
412 |
+
if ($this->_wsdl) {
|
413 |
+
list($ptype, $arrayType, $array_type_ns, $array_depth)
|
414 |
+
= $this->_wsdl->getSchemaType($type, $name);
|
415 |
+
}
|
416 |
+
|
417 |
+
if (!$arrayType) {
|
418 |
+
$arrayType = $artype;
|
419 |
+
}
|
420 |
+
if (!$ptype) {
|
421 |
+
$ptype = $this->_getType($value);
|
422 |
+
}
|
423 |
+
if (!$type) {
|
424 |
+
$type = new QName($ptype);
|
425 |
+
}
|
426 |
+
|
427 |
+
if (strcasecmp($ptype, 'Struct') == 0 ||
|
428 |
+
strcasecmp($type->name, 'Struct') == 0) {
|
429 |
+
// Struct
|
430 |
+
$vars = is_object($value) ? get_object_vars($value) : $value;
|
431 |
+
if (is_array($vars)) {
|
432 |
+
foreach (array_keys($vars) as $k) {
|
433 |
+
// Hide private vars.
|
434 |
+
if ($k[0] == '_') {
|
435 |
+
continue;
|
436 |
+
}
|
437 |
+
|
438 |
+
if (is_object($vars[$k])) {
|
439 |
+
if (is_a($vars[$k], 'SOAP_Value')) {
|
440 |
+
$xmlout_value .= $vars[$k]->serialize($this);
|
441 |
+
} else {
|
442 |
+
// XXX get the members and serialize them instead
|
443 |
+
// converting to an array is more overhead than we
|
444 |
+
// should really do.
|
445 |
+
$xmlout_value .= $this->_serializeValue(get_object_vars($vars[$k]), new QName($k, $this->_section5 ? null : $name->namepace), null, $options);
|
446 |
+
}
|
447 |
+
} else {
|
448 |
+
$xmlout_value .= $this->_serializeValue($vars[$k], new QName($k, $this->_section5 ? null : $name->namespace), false, $options);
|
449 |
+
}
|
450 |
+
}
|
451 |
+
}
|
452 |
+
} elseif (strcasecmp($ptype, 'Array') == 0 ||
|
453 |
+
strcasecmp($type->name, 'Array') == 0) {
|
454 |
+
// Array.
|
455 |
+
$type = new QName('Array', SOAP_SCHEMA_ENCODING);
|
456 |
+
$numtypes = 0;
|
457 |
+
$value = (array)$value;
|
458 |
+
// XXX this will be slow on larger arrays. Basically, it flattens
|
459 |
+
// arrays to allow us to serialize multi-dimensional arrays. We
|
460 |
+
// only do this if arrayType is set, which will typically only
|
461 |
+
// happen if we are using WSDL
|
462 |
+
if (isset($options['flatten']) ||
|
463 |
+
($arrayType &&
|
464 |
+
(strchr($arrayType, ',') || strstr($arrayType, '][')))) {
|
465 |
+
$numtypes = $this->_multiArrayType($value, $arrayType,
|
466 |
+
$ar_size, $xmlout_value);
|
467 |
+
}
|
468 |
+
|
469 |
+
$array_type = $array_type_prefix = '';
|
470 |
+
if ($numtypes != 1) {
|
471 |
+
$arrayTypeQName = new QName($arrayType);
|
472 |
+
$arrayType = $arrayTypeQName->name;
|
473 |
+
$array_types = array();
|
474 |
+
$array_val = null;
|
475 |
+
|
476 |
+
// Serialize each array element.
|
477 |
+
$ar_size = count($value);
|
478 |
+
foreach ($value as $array_val) {
|
479 |
+
if (is_a($array_val, 'SOAP_Value')) {
|
480 |
+
$array_type = $array_val->type;
|
481 |
+
$array_types[$array_type] = 1;
|
482 |
+
$array_type_ns = $array_val->type_namespace;
|
483 |
+
$xmlout_value .= $array_val->serialize($this);
|
484 |
+
} else {
|
485 |
+
$array_type = $this->_getType($array_val);
|
486 |
+
$array_types[$array_type] = 1;
|
487 |
+
if (empty($options['keep_arrays_flat'])) {
|
488 |
+
$xmlout_value .= $this->_serializeValue($array_val, new QName('item', $this->_section5 ? null : $name->namespace), new QName($array_type), $options);
|
489 |
+
} else {
|
490 |
+
$xmlout_value .= $this->_serializeValue($array_val, $name, new QName($array_type), $options, $attributes);
|
491 |
+
}
|
492 |
+
}
|
493 |
+
}
|
494 |
+
|
495 |
+
if (!$arrayType) {
|
496 |
+
$numtypes = count($array_types);
|
497 |
+
if ($numtypes == 1) {
|
498 |
+
$arrayType = $array_type;
|
499 |
+
}
|
500 |
+
// Using anyType is more interoperable.
|
501 |
+
if ($array_type == 'Struct') {
|
502 |
+
$array_type = '';
|
503 |
+
} elseif ($array_type == 'Array') {
|
504 |
+
$arrayType = 'anyType';
|
505 |
+
$array_type_prefix = 'xsd';
|
506 |
+
} else {
|
507 |
+
if (!$arrayType) {
|
508 |
+
$arrayType = $array_type;
|
509 |
+
}
|
510 |
+
}
|
511 |
+
}
|
512 |
+
}
|
513 |
+
if (!$arrayType || $numtypes > 1) {
|
514 |
+
// Should reference what schema we're using.
|
515 |
+
$arrayType = 'xsd:anyType';
|
516 |
+
} else {
|
517 |
+
if ($array_type_ns) {
|
518 |
+
$array_type_prefix = $this->_getNamespacePrefix($array_type_ns);
|
519 |
+
} elseif (isset($this->_typemap[$this->_XMLSchemaVersion][$arrayType])) {
|
520 |
+
$array_type_prefix = $this->_namespaces[$this->_XMLSchemaVersion];
|
521 |
+
} elseif (isset($this->_typemap[SOAP_SCHEMA_ENCODING][$arrayType])) {
|
522 |
+
$array_type_prefix = SOAP_BASE::SOAPENCPrefix();
|
523 |
+
}
|
524 |
+
if ($array_type_prefix) {
|
525 |
+
$arrayType = $array_type_prefix . ':' . $arrayType;
|
526 |
+
}
|
527 |
+
}
|
528 |
+
|
529 |
+
$xmlout_arrayType = ' ' . SOAP_BASE::SOAPENCPrefix()
|
530 |
+
. ':arrayType="' . $arrayType;
|
531 |
+
if ($array_depth != null) {
|
532 |
+
for ($i = 0; $i < $array_depth; $i++) {
|
533 |
+
$xmlout_arrayType .= '[]';
|
534 |
+
}
|
535 |
+
}
|
536 |
+
$xmlout_arrayType .= "[$ar_size]\"";
|
537 |
+
} elseif ($value instanceof SOAP_Value) {
|
538 |
+
$xmlout_value = $value->serialize($this);
|
539 |
+
} elseif ($type->name == 'string') {
|
540 |
+
$xmlout_value = htmlspecialchars($value);
|
541 |
+
} elseif ($type->name == 'rawstring') {
|
542 |
+
$xmlout_value = $value;
|
543 |
+
} elseif ($type->name == 'boolean') {
|
544 |
+
$xmlout_value = $value ? 'true' : 'false';
|
545 |
+
} else {
|
546 |
+
$xmlout_value = $value;
|
547 |
+
}
|
548 |
+
|
549 |
+
// Add namespaces.
|
550 |
+
if ($name->namespace) {
|
551 |
+
$elPrefix = $this->_getNamespacePrefix($name->namespace);
|
552 |
+
if ($elPrefix) {
|
553 |
+
$xmlout_name = $elPrefix . ':' . $name->name;
|
554 |
+
} else {
|
555 |
+
$xmlout_name = $name->name;
|
556 |
+
}
|
557 |
+
} else {
|
558 |
+
$xmlout_name = $name->name;
|
559 |
+
}
|
560 |
+
|
561 |
+
if ($type->namespace) {
|
562 |
+
$typePrefix = false;
|
563 |
+
if (empty($options['no_type_prefix'])) {
|
564 |
+
$typePrefix = $this->_getNamespacePrefix($type->namespace);
|
565 |
+
}
|
566 |
+
if ($typePrefix) {
|
567 |
+
$xmlout_type = $typePrefix . ':' . $type->name;
|
568 |
+
} else {
|
569 |
+
$xmlout_type = $type->name;
|
570 |
+
}
|
571 |
+
} elseif ($type->name &&
|
572 |
+
isset($this->_typemap[$this->_XMLSchemaVersion][$type->name])) {
|
573 |
+
$typePrefix = $this->_namespaces[$this->_XMLSchemaVersion];
|
574 |
+
if ($typePrefix) {
|
575 |
+
$xmlout_type = $typePrefix . ':' . $type->name;
|
576 |
+
} else {
|
577 |
+
$xmlout_type = $type->name;
|
578 |
+
}
|
579 |
+
}
|
580 |
+
|
581 |
+
// Handle additional attributes.
|
582 |
+
$xml_attr = '';
|
583 |
+
if (count($attributes)) {
|
584 |
+
foreach ($attributes as $k => $v) {
|
585 |
+
$kqn = new QName($k);
|
586 |
+
$vqn = new QName($v);
|
587 |
+
$xml_attr .= ' ' . $kqn->fqn() . '="' . $vqn->fqn() . '"';
|
588 |
+
}
|
589 |
+
}
|
590 |
+
|
591 |
+
// Store the attachment for mime encoding.
|
592 |
+
if (isset($options['attachment']) &&
|
593 |
+
!PEAR::isError($options['attachment'])) {
|
594 |
+
$this->_attachments[] = $options['attachment'];
|
595 |
+
}
|
596 |
+
|
597 |
+
if ($this->_section5) {
|
598 |
+
if ($xmlout_type) {
|
599 |
+
$xmlout_type = " xsi:type=\"$xmlout_type\"";
|
600 |
+
}
|
601 |
+
if (is_null($xmlout_value)) {
|
602 |
+
$xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
|
603 |
+
"$xml_attr xsi:nil=\"true\"/>";
|
604 |
+
} else {
|
605 |
+
$xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
|
606 |
+
"$xml_attr>$xmlout_value</$xmlout_name>";
|
607 |
+
}
|
608 |
+
} elseif ($type->name == 'Array' && !empty($options['keep_arrays_flat'])) {
|
609 |
+
$xml = $xmlout_value;
|
610 |
+
} else {
|
611 |
+
if (is_null($xmlout_value)) {
|
612 |
+
$xml = "\r\n<$xmlout_name$xmlns$xml_attr/>";
|
613 |
+
} else {
|
614 |
+
$xml = "\r\n<$xmlout_name$xmlns$xml_attr>" .
|
615 |
+
$xmlout_value . "</$xmlout_name>";
|
616 |
+
}
|
617 |
+
}
|
618 |
+
|
619 |
+
return $xml;
|
620 |
+
}
|
621 |
+
|
622 |
+
/**
|
623 |
+
* Converts a PHP type to a SOAP type.
|
624 |
+
*
|
625 |
+
* @param mixed $value The value to inspect.
|
626 |
+
*
|
627 |
+
* @return string The value's SOAP type.
|
628 |
+
*/
|
629 |
+
function _getType($value)
|
630 |
+
{
|
631 |
+
$type = gettype($value);
|
632 |
+
switch ($type) {
|
633 |
+
case 'object':
|
634 |
+
if (is_a($value, 'soap_value')) {
|
635 |
+
$type = $value->type;
|
636 |
+
} else {
|
637 |
+
$type = 'Struct';
|
638 |
+
}
|
639 |
+
break;
|
640 |
+
|
641 |
+
case 'array':
|
642 |
+
// Hashes are always handled as structs.
|
643 |
+
if ($this->_isHash($value)) {
|
644 |
+
$type = 'Struct';
|
645 |
+
break;
|
646 |
+
}
|
647 |
+
if (count($value) > 1) {
|
648 |
+
// For non-wsdl structs that are all the same type
|
649 |
+
reset($value);
|
650 |
+
$value1 = next($value);
|
651 |
+
$value2 = next($value);
|
652 |
+
if (is_a($value1, 'SOAP_Value') &&
|
653 |
+
is_a($value2, 'SOAP_Value') &&
|
654 |
+
$value1->name != $value2->name) {
|
655 |
+
// This is a struct, not an array.
|
656 |
+
$type = 'Struct';
|
657 |
+
break;
|
658 |
+
}
|
659 |
+
}
|
660 |
+
$type = 'Array';
|
661 |
+
break;
|
662 |
+
|
663 |
+
case 'integer':
|
664 |
+
case 'long':
|
665 |
+
$type = 'int';
|
666 |
+
break;
|
667 |
+
|
668 |
+
case 'boolean':
|
669 |
+
break;
|
670 |
+
|
671 |
+
case 'double':
|
672 |
+
// double is deprecated in PHP 4.2 and later.
|
673 |
+
$type = 'float';
|
674 |
+
break;
|
675 |
+
|
676 |
+
case 'null':
|
677 |
+
$type = '';
|
678 |
+
break;
|
679 |
+
|
680 |
+
case 'string':
|
681 |
+
default:
|
682 |
+
break;
|
683 |
+
}
|
684 |
+
|
685 |
+
return $type;
|
686 |
+
}
|
687 |
+
|
688 |
+
function _multiArrayType($value, &$type, &$size, &$xml)
|
689 |
+
{
|
690 |
+
if (is_array($value)) {
|
691 |
+
// Seems we have a multi dimensional array, figure it out if we
|
692 |
+
// do.
|
693 |
+
for ($i = 0, $c = count($value); $i < $c; ++$i) {
|
694 |
+
$this->_multiArrayType($value[$i], $type, $size, $xml);
|
695 |
+
}
|
696 |
+
|
697 |
+
$sz = count($value);
|
698 |
+
if ($size) {
|
699 |
+
$size = $sz . ',' . $size;
|
700 |
+
} else {
|
701 |
+
$size = $sz;
|
702 |
+
}
|
703 |
+
return 1;
|
704 |
+
} elseif (is_object($value)) {
|
705 |
+
$type = $value->type;
|
706 |
+
$xml .= $value->serialize($this);
|
707 |
+
} else {
|
708 |
+
$type = $this->_getType($value);
|
709 |
+
$xml .= $this->_serializeValue($value, new QName('item'), new QName($type));
|
710 |
+
}
|
711 |
+
$size = null;
|
712 |
+
|
713 |
+
return 1;
|
714 |
+
}
|
715 |
+
|
716 |
+
/**
|
717 |
+
* Returns whether a type is a base64 type.
|
718 |
+
*
|
719 |
+
* @param string $type A type name.
|
720 |
+
*
|
721 |
+
* @return boolean True if the type name is a base64 type.
|
722 |
+
*/
|
723 |
+
function _isBase64Type($type)
|
724 |
+
{
|
725 |
+
return $type == 'base64' || $type == 'base64Binary';
|
726 |
+
}
|
727 |
+
|
728 |
+
/**
|
729 |
+
* Returns whether an array is a hash.
|
730 |
+
*
|
731 |
+
* @param array $a An array to check.
|
732 |
+
*
|
733 |
+
* @return boolean True if the specified array is a hash.
|
734 |
+
*/
|
735 |
+
function _isHash($a)
|
736 |
+
{
|
737 |
+
foreach (array_keys($a) as $k) {
|
738 |
+
// Checking the type is faster than regexp.
|
739 |
+
if (!is_int($k)) {
|
740 |
+
return true;
|
741 |
+
}
|
742 |
+
}
|
743 |
+
return false;
|
744 |
+
}
|
745 |
+
|
746 |
+
function _un_htmlentities($string)
|
747 |
+
{
|
748 |
+
$trans_tbl = get_html_translation_table(HTML_ENTITIES);
|
749 |
+
$trans_tbl = array_flip($trans_tbl);
|
750 |
+
return strtr($string, $trans_tbl);
|
751 |
+
}
|
752 |
+
|
753 |
+
/**
|
754 |
+
* Converts a SOAP_Value object into a PHP value.
|
755 |
+
*/
|
756 |
+
function _decode($soapval)
|
757 |
+
{
|
758 |
+
if (!$soapval instanceof SOAP_Value) {
|
759 |
+
return $soapval;
|
760 |
+
}
|
761 |
+
|
762 |
+
if (is_array($soapval->value)) {
|
763 |
+
$isstruct = $soapval->type != 'Array';
|
764 |
+
if ($isstruct) {
|
765 |
+
$classname = $this->_defaultObjectClassname;
|
766 |
+
if (isset($this->_type_translation[$soapval->tqn->fqn()])) {
|
767 |
+
// This will force an error in PHP if the class does not
|
768 |
+
// exist.
|
769 |
+
$classname = $this->_type_translation[$soapval->tqn->fqn()];
|
770 |
+
} elseif (isset($this->_type_translation[$soapval->type])) {
|
771 |
+
// This will force an error in PHP if the class does not
|
772 |
+
// exist.
|
773 |
+
$classname = $this->_type_translation[$soapval->type];
|
774 |
+
} elseif ($this->_auto_translation) {
|
775 |
+
if (class_exists($soapval->type)) {
|
776 |
+
$classname = $soapval->type;
|
777 |
+
} elseif ($this->_wsdl) {
|
778 |
+
$t = $this->_wsdl->getComplexTypeNameForElement($soapval->name, $soapval->namespace);
|
779 |
+
if ($t && class_exists($t)) {
|
780 |
+
$classname = $t;
|
781 |
+
}
|
782 |
+
}
|
783 |
+
}
|
784 |
+
$return = new $classname;
|
785 |
+
} else {
|
786 |
+
$return = array();
|
787 |
+
}
|
788 |
+
|
789 |
+
foreach ($soapval->value as $item) {
|
790 |
+
if ($isstruct) {
|
791 |
+
if ($this->_wsdl) {
|
792 |
+
// Get this child's WSDL information.
|
793 |
+
// /$soapval->ns/$soapval->type/$item->ns/$item->name
|
794 |
+
$child_type = $this->_wsdl->getComplexTypeChildType(
|
795 |
+
$soapval->namespace,
|
796 |
+
$soapval->name,
|
797 |
+
$item->namespace,
|
798 |
+
$item->name);
|
799 |
+
if ($child_type) {
|
800 |
+
$item->type = $child_type;
|
801 |
+
}
|
802 |
+
}
|
803 |
+
if ($item->type == 'Array') {
|
804 |
+
if (isset($return->{$item->name}) &&
|
805 |
+
is_object($return->{$item->name})) {
|
806 |
+
$return->{$item->name} = $this->_decode($item);
|
807 |
+
} elseif (isset($return->{$item->name}) &&
|
808 |
+
is_array($return->{$item->name})) {
|
809 |
+
$return->{$item->name}[] = $this->_decode($item);
|
810 |
+
} elseif (isset($return->{$item->name})) {
|
811 |
+
$return->{$item->name} = array(
|
812 |
+
$return->{$item->name},
|
813 |
+
$this->_decode($item)
|
814 |
+
);
|
815 |
+
} elseif (is_array($return)) {
|
816 |
+
$return[] = $this->_decode($item);
|
817 |
+
} else {
|
818 |
+
$return->{$item->name} = $this->_decode($item);
|
819 |
+
}
|
820 |
+
} elseif (isset($return->{$item->name})) {
|
821 |
+
$d = $this->_decode($item);
|
822 |
+
if (count(get_object_vars($return)) == 1) {
|
823 |
+
$isstruct = false;
|
824 |
+
$return = array($return->{$item->name}, $d);
|
825 |
+
} else {
|
826 |
+
$return->{$item->name} = array($return->{$item->name}, $d);
|
827 |
+
}
|
828 |
+
} else {
|
829 |
+
$return->{$item->name} = $this->_decode($item);
|
830 |
+
}
|
831 |
+
// Set the attributes as members in the class.
|
832 |
+
if (method_exists($return, '__set_attribute')) {
|
833 |
+
foreach ($soapval->attributes as $key => $value) {
|
834 |
+
call_user_func_array(array(&$return,
|
835 |
+
'__set_attribute'),
|
836 |
+
array($key, $value));
|
837 |
+
}
|
838 |
+
}
|
839 |
+
} else {
|
840 |
+
if ($soapval->arrayType && is_a($item, 'SOAP_Value')) {
|
841 |
+
if ($this->_isBase64Type($item->type) &&
|
842 |
+
!$this->_isBase64Type($soapval->arrayType)) {
|
843 |
+
// Decode the value if we're losing the base64
|
844 |
+
// type information.
|
845 |
+
$item->value = base64_decode($item->value);
|
846 |
+
}
|
847 |
+
$item->type = $soapval->arrayType;
|
848 |
+
}
|
849 |
+
$return[] = $this->_decode($item);
|
850 |
+
}
|
851 |
+
}
|
852 |
+
|
853 |
+
return $return;
|
854 |
+
}
|
855 |
+
|
856 |
+
if ($soapval->type == 'boolean') {
|
857 |
+
if ($soapval->value != '0' &&
|
858 |
+
strcasecmp($soapval->value, 'false') != 0) {
|
859 |
+
$soapval->value = true;
|
860 |
+
} else {
|
861 |
+
$soapval->value = false;
|
862 |
+
}
|
863 |
+
} elseif ($soapval->type &&
|
864 |
+
isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type])) {
|
865 |
+
// If we can, set variable type.
|
866 |
+
settype($soapval->value,
|
867 |
+
$this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type]);
|
868 |
+
} elseif ($soapval->type == 'Struct') {
|
869 |
+
$soapval->value = null;
|
870 |
+
}
|
871 |
+
|
872 |
+
return $soapval->value;
|
873 |
+
}
|
874 |
+
|
875 |
+
/**
|
876 |
+
* Creates the SOAP envelope with the SOAP envelop data.
|
877 |
+
*
|
878 |
+
* @param SOAP_Value $method SOAP_Value instance with the method name as
|
879 |
+
* the name, and the method arguments as the
|
880 |
+
* value.
|
881 |
+
* @param array $headers A list of additional SOAP_Header objects.
|
882 |
+
* @param string $encoding The charset of the SOAP message.
|
883 |
+
* @param array $options A list of encoding/serialization options.
|
884 |
+
*
|
885 |
+
* @return string The complete SOAP message.
|
886 |
+
*/
|
887 |
+
function makeEnvelope($method, $headers, $encoding = SOAP_DEFAULT_ENCODING,
|
888 |
+
$options = array())
|
889 |
+
{
|
890 |
+
$smsg = $header_xml = $ns_string = '';
|
891 |
+
|
892 |
+
if ($headers) {
|
893 |
+
for ($i = 0, $c = count($headers); $i < $c; $i++) {
|
894 |
+
$header_xml .= $headers[$i]->serialize($this);
|
895 |
+
}
|
896 |
+
$header_xml = sprintf("<%s:Header>\r\n%s\r\n</%s:Header>\r\n",
|
897 |
+
SOAP_BASE::SOAPENVPrefix(), $header_xml,
|
898 |
+
SOAP_BASE::SOAPENVPrefix());
|
899 |
+
}
|
900 |
+
|
901 |
+
if (!isset($options['input']) || $options['input'] == 'parse') {
|
902 |
+
if (is_array($method)) {
|
903 |
+
for ($i = 0, $c = count($method); $i < $c; $i++) {
|
904 |
+
$smsg .= $method[$i]->serialize($this);
|
905 |
+
}
|
906 |
+
} else {
|
907 |
+
$smsg = $method->serialize($this);
|
908 |
+
}
|
909 |
+
} else {
|
910 |
+
$smsg = $method;
|
911 |
+
}
|
912 |
+
$body = sprintf("<%s:Body>%s\r\n</%s:Body>\r\n",
|
913 |
+
SOAP_BASE::SOAPENVPrefix(), $smsg,
|
914 |
+
SOAP_BASE::SOAPENVPrefix());
|
915 |
+
|
916 |
+
foreach ($this->_namespaces as $k => $v) {
|
917 |
+
$ns_string .= "\r\n " . sprintf('xmlns:%s="%s"', $v, $k);
|
918 |
+
}
|
919 |
+
if ($this->_namespace) {
|
920 |
+
$ns_string .= "\r\n " . sprintf('xmlns="%s"', $this->_namespace);
|
921 |
+
}
|
922 |
+
|
923 |
+
/* If 'use' == 'literal', do not put in the encodingStyle. This is
|
924 |
+
* denoted by $this->_section5 being false. 'use' can be defined at a
|
925 |
+
* more granular level than we are dealing with here, so this does not
|
926 |
+
* work for all services. */
|
927 |
+
$xml = sprintf('<?xml version="1.0" encoding="%s"?>%s<%s:Envelope%s',
|
928 |
+
$encoding, "\r\n", SOAP_BASE::SOAPENVPrefix(),
|
929 |
+
$ns_string);
|
930 |
+
if ($this->_section5) {
|
931 |
+
$xml .= "\r\n " . sprintf('%s:encodingStyle="%s"',
|
932 |
+
SOAP_BASE::SOAPENVPrefix(),
|
933 |
+
SOAP_SCHEMA_ENCODING);
|
934 |
+
}
|
935 |
+
$xml .= sprintf('>%s%s%s</%s:Envelope>' . "\r\n",
|
936 |
+
"\r\n", $header_xml, $body, SOAP_BASE::SOAPENVPrefix());
|
937 |
+
|
938 |
+
return $xml;
|
939 |
+
}
|
940 |
+
|
941 |
+
function _makeMimeMessage($xml, $encoding = SOAP_DEFAULT_ENCODING)
|
942 |
+
{
|
943 |
+
if (!@include_once 'Mail/mimePart.php') {
|
944 |
+
return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
|
945 |
+
}
|
946 |
+
|
947 |
+
// Encode any attachments. See http://www.w3.org/TR/SOAP-attachments
|
948 |
+
// Now we have to mime encode the message.
|
949 |
+
$params = array('content_type' => 'multipart/related; type="text/xml"');
|
950 |
+
$msg = new Mail_mimePart('', $params);
|
951 |
+
|
952 |
+
// Add the xml part.
|
953 |
+
$params['content_type'] = 'text/xml';
|
954 |
+
$params['charset'] = $encoding;
|
955 |
+
$msg->addSubPart($xml, $params);
|
956 |
+
|
957 |
+
// Add the attachements
|
958 |
+
for ($i = 0, $c = count($this->_attachments); $i < $c; ++$i) {
|
959 |
+
$msg->addSubPart($this->_attachments[$i]['body'],
|
960 |
+
$this->_attachments[$i]);
|
961 |
+
}
|
962 |
+
|
963 |
+
return $msg->encode();
|
964 |
+
}
|
965 |
+
|
966 |
+
// TODO: this needs to be used from the Transport system.
|
967 |
+
function _makeDIMEMessage($xml)
|
968 |
+
{
|
969 |
+
if (!@include_once 'Net/DIME.php') {
|
970 |
+
return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
|
971 |
+
}
|
972 |
+
|
973 |
+
// Encode any attachments. See
|
974 |
+
// http://search.ietf.org/internet-drafts/draft-nielsen-dime-soap-00.txt
|
975 |
+
// Now we have to DIME encode the message
|
976 |
+
$dime = new Net_DIME_Message();
|
977 |
+
$msg = $dime->encodeData($xml, SOAP_ENVELOP, null, NET_DIME_TYPE_URI);
|
978 |
+
|
979 |
+
// Add the attachments.
|
980 |
+
$c = count($this->_attachments);
|
981 |
+
for ($i = 0; $i < $c; $i++) {
|
982 |
+
$msg .= $dime->encodeData($this->_attachments[$i]['body'],
|
983 |
+
$this->_attachments[$i]['content_type'],
|
984 |
+
$this->_attachments[$i]['cid'],
|
985 |
+
NET_DIME_TYPE_MEDIA);
|
986 |
+
}
|
987 |
+
$msg .= $dime->endMessage();
|
988 |
+
|
989 |
+
return $msg;
|
990 |
+
}
|
991 |
+
|
992 |
+
function _decodeMimeMessage(&$data, &$headers, &$attachments)
|
993 |
+
{
|
994 |
+
if (!@include_once 'Mail/mimeDecode.php') {
|
995 |
+
return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
|
996 |
+
}
|
997 |
+
|
998 |
+
$params['include_bodies'] = true;
|
999 |
+
$params['decode_bodies'] = true;
|
1000 |
+
$params['decode_headers'] = true;
|
1001 |
+
|
1002 |
+
// Lame thing to have to do for decoding.
|
1003 |
+
$decoder = new Mail_mimeDecode($data);
|
1004 |
+
$structure = $decoder->decode($params);
|
1005 |
+
|
1006 |
+
if (isset($structure->body)) {
|
1007 |
+
$data = $structure->body;
|
1008 |
+
$headers = $structure->headers;
|
1009 |
+
|
1010 |
+
return;
|
1011 |
+
} elseif (isset($structure->parts)) {
|
1012 |
+
$data = $structure->parts[0]->body;
|
1013 |
+
$headers = array_merge($structure->headers,
|
1014 |
+
$structure->parts[0]->headers);
|
1015 |
+
if (count($structure->parts) <= 1) {
|
1016 |
+
return;
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
$mime_parts = array_splice($structure->parts, 1);
|
1020 |
+
// Prepare the parts for the SOAP parser.
|
1021 |
+
for ($i = 0, $c = count($mime_parts); $i < $c; $i++) {
|
1022 |
+
$p = $mime_parts[$i];
|
1023 |
+
if (isset($p->headers['content-location'])) {
|
1024 |
+
// TODO: modify location per SwA note section 3
|
1025 |
+
// http://www.w3.org/TR/SOAP-attachments
|
1026 |
+
$attachments[$p->headers['content-location']] = $p->body;
|
1027 |
+
} else {
|
1028 |
+
$cid = 'cid:' . substr($p->headers['content-id'], 1, -1);
|
1029 |
+
$attachments[$cid] = $p->body;
|
1030 |
+
}
|
1031 |
+
}
|
1032 |
+
|
1033 |
+
return;
|
1034 |
+
}
|
1035 |
+
|
1036 |
+
$this->_raiseSoapFault('Mime parsing error', '', '', 'Server');
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
function _decodeDIMEMessage(&$data, &$headers, &$attachments)
|
1040 |
+
{
|
1041 |
+
if (!@include_once 'Net/DIME.php') {
|
1042 |
+
return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
|
1043 |
+
}
|
1044 |
+
|
1045 |
+
// This SHOULD be moved to the transport layer, e.g. PHP itself should
|
1046 |
+
// handle parsing DIME ;)
|
1047 |
+
$dime = new Net_DIME_Message();
|
1048 |
+
$err = $dime->decodeData($data);
|
1049 |
+
if (PEAR::isError($err)) {
|
1050 |
+
$this->_raiseSoapFault('Failed to decode the DIME message!', '', '', 'Server');
|
1051 |
+
return;
|
1052 |
+
}
|
1053 |
+
if (strcasecmp($dime->parts[0]['type'], SOAP_ENVELOP) != 0) {
|
1054 |
+
$this->_raiseSoapFault('DIME record 1 is not a SOAP envelop!', '', '', 'Server');
|
1055 |
+
return;
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
$data = $dime->parts[0]['data'];
|
1059 |
+
// Fake it for now.
|
1060 |
+
$headers['content-type'] = 'text/xml';
|
1061 |
+
$c = count($dime->parts);
|
1062 |
+
for ($i = 0; $i < $c; $i++) {
|
1063 |
+
$part =& $dime->parts[$i];
|
1064 |
+
// We need to handle URI's better.
|
1065 |
+
$id = strncmp($part['id'], 'cid:', 4)
|
1066 |
+
? 'cid:' . $part['id']
|
1067 |
+
: $part['id'];
|
1068 |
+
$attachments[$id] = $part['data'];
|
1069 |
+
}
|
1070 |
+
}
|
1071 |
+
|
1072 |
+
/**
|
1073 |
+
* Explicitly sets the translation for a specific class.
|
1074 |
+
*
|
1075 |
+
* Auto translation works for all cases, but opens ANY class in the script
|
1076 |
+
* to be used as a data type, and may not be desireable.
|
1077 |
+
*
|
1078 |
+
* @param string $type A SOAP type.
|
1079 |
+
* @param string $class A PHP class name.
|
1080 |
+
*/
|
1081 |
+
function setTypeTranslation($type, $class = null)
|
1082 |
+
{
|
1083 |
+
$tq = new QName($type);
|
1084 |
+
if (!$class) {
|
1085 |
+
$class = $tq->name;
|
1086 |
+
}
|
1087 |
+
$this->_type_translation[$type]=$class;
|
1088 |
+
}
|
1089 |
+
|
1090 |
+
}
|
1091 |
+
|
1092 |
+
/**
|
1093 |
+
* Class used to handle QNAME values in XML.
|
1094 |
+
*
|
1095 |
+
* @package SOAP
|
1096 |
+
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
1097 |
+
*/
|
1098 |
+
class QName
|
1099 |
+
{
|
1100 |
+
var $name = '';
|
1101 |
+
var $ns = '';
|
1102 |
+
var $namespace = '';
|
1103 |
+
|
1104 |
+
function QName($name, $namespace = '')
|
1105 |
+
{
|
1106 |
+
if ($name && $name[0] == '{') {
|
1107 |
+
preg_match('/\{(.*?)\}(.*)/', $name, $m);
|
1108 |
+
$this->name = $m[2];
|
1109 |
+
$this->namespace = $m[1];
|
1110 |
+
} elseif (substr_count($name, ':') == 1) {
|
1111 |
+
$s = explode(':', $name);
|
1112 |
+
$s = array_reverse($s);
|
1113 |
+
$this->name = $s[0];
|
1114 |
+
$this->ns = $s[1];
|
1115 |
+
$this->namespace = $namespace;
|
1116 |
+
} else {
|
1117 |
+
$this->name = $name;
|
1118 |
+
$this->namespace = $namespace;
|
1119 |
+
}
|
1120 |
+
|
1121 |
+
// A little more magic than should be in a qname.
|
1122 |
+
$p = strpos($this->name, '[');
|
1123 |
+
if ($p) {
|
1124 |
+
// TODO: Need to re-examine this logic later.
|
1125 |
+
// Chop off [].
|
1126 |
+
$this->arraySize = explode(',', substr($this->name, $p + 1, -$p - 2));
|
1127 |
+
$this->arrayInfo = substr($this->name, $p);
|
1128 |
+
$this->name = substr($this->name, 0, $p);
|
1129 |
+
}
|
1130 |
+
}
|
1131 |
+
|
1132 |
+
function fqn()
|
1133 |
+
{
|
1134 |
+
if ($this->namespace) {
|
1135 |
+
return '{' . $this->namespace . '}' . $this->name;
|
1136 |
+
} elseif ($this->ns) {
|
1137 |
+
return $this->ns . ':' . $this->name;
|
1138 |
+
}
|
1139 |
+
return $this->name;
|
1140 |
+
}
|
1141 |
+
|
1142 |
+
}
|
lib/PEAR/SOAP/Client.php
ADDED
@@ -0,0 +1,837 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file contains the code for the SOAP client.
|
4 |
+
*
|
5 |
+
* PHP versions 4 and 5
|
6 |
+
*
|
7 |
+
* LICENSE: This source file is subject to version 2.02 of the PHP license,
|
8 |
+
* that is bundled with this package in the file LICENSE, and is available at
|
9 |
+
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
|
10 |
+
* did not receive a copy of the PHP license and are unable to obtain it
|
11 |
+
* through the world-wide-web, please send a note to license@php.net so we can
|
12 |
+
* mail you a copy immediately.
|
13 |
+
*
|
14 |
+
* @category Web Services
|
15 |
+
* @package SOAP
|
16 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
17 |
+
* @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
|
18 |
+
* @author Chuck Hagenbuch <chuck@horde.org> Maintenance
|
19 |
+
* @author Jan Schneider <jan@horde.org> Maintenance
|
20 |
+
* @copyright 2003-2005 The PHP Group
|
21 |
+
* @license http://www.php.net/license/2_02.txt PHP License 2.02
|
22 |
+
* @link http://pear.php.net/package/SOAP
|
23 |
+
*/
|
24 |
+
|
25 |
+
/** SOAP_Value */
|
26 |
+
require_once 'SOAP/Value.php';
|
27 |
+
require_once 'SOAP/Base.php';
|
28 |
+
require_once 'SOAP/Transport.php';
|
29 |
+
require_once 'SOAP/WSDL.php';
|
30 |
+
require_once 'SOAP/Fault.php';
|
31 |
+
require_once 'SOAP/Parser.php';
|
32 |
+
|
33 |
+
// Arnaud: the following code was taken from DataObject and adapted to suit
|
34 |
+
|
35 |
+
// this will be horrifically slow!!!!
|
36 |
+
// NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer
|
37 |
+
// these two are BC/FC handlers for call in PHP4/5
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @package SOAP
|
41 |
+
*/
|
42 |
+
if (!class_exists('SOAP_Client_Overload', false)) {
|
43 |
+
if (substr(zend_version(), 0, 1) > 1) {
|
44 |
+
class SOAP_Client_Overload extends SOAP_Base {
|
45 |
+
function __call($method, $args)
|
46 |
+
{
|
47 |
+
$return = null;
|
48 |
+
$this->_call($method, $args, $return);
|
49 |
+
return $return;
|
50 |
+
}
|
51 |
+
}
|
52 |
+
} else {
|
53 |
+
if (!function_exists('clone')) {
|
54 |
+
eval('function clone($t) { return $t; }');
|
55 |
+
}
|
56 |
+
eval('
|
57 |
+
class SOAP_Client_Overload extends SOAP_Base {
|
58 |
+
function __call($method, $args, &$return)
|
59 |
+
{
|
60 |
+
return $this->_call($method, $args, $return);
|
61 |
+
}
|
62 |
+
}');
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* SOAP Client Class
|
68 |
+
*
|
69 |
+
* This class is the main interface for making soap requests.
|
70 |
+
*
|
71 |
+
* basic usage:<code>
|
72 |
+
* $soapclient = new SOAP_Client( string path [ , boolean wsdl] );
|
73 |
+
* echo $soapclient->call( string methodname [ , array parameters] );
|
74 |
+
* </code>
|
75 |
+
* or, if using PHP 5+ or the overload extension:<code>
|
76 |
+
* $soapclient = new SOAP_Client( string path [ , boolean wsdl] );
|
77 |
+
* echo $soapclient->methodname( [ array parameters] );
|
78 |
+
* </code>
|
79 |
+
*
|
80 |
+
* Originally based on SOAPx4 by Dietrich Ayala
|
81 |
+
* http://dietrich.ganx4.com/soapx4
|
82 |
+
*
|
83 |
+
* @access public
|
84 |
+
* @package SOAP
|
85 |
+
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
86 |
+
* @author Stig Bakken <ssb@fast.no> Conversion to PEAR
|
87 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
88 |
+
*/
|
89 |
+
class SOAP_Client extends SOAP_Client_Overload
|
90 |
+
{
|
91 |
+
/**
|
92 |
+
* Communication endpoint.
|
93 |
+
*
|
94 |
+
* Currently the following transport formats are supported:
|
95 |
+
* - HTTP
|
96 |
+
* - SMTP
|
97 |
+
*
|
98 |
+
* Example endpoints:
|
99 |
+
* http://www.example.com/soap/server.php
|
100 |
+
* https://www.example.com/soap/server.php
|
101 |
+
* mailto:soap@example.com
|
102 |
+
*
|
103 |
+
* @see SOAP_Client()
|
104 |
+
* @var string
|
105 |
+
*/
|
106 |
+
var $_endpoint = '';
|
107 |
+
|
108 |
+
/**
|
109 |
+
* The SOAP PORT name that is used by the client.
|
110 |
+
*
|
111 |
+
* @var string
|
112 |
+
*/
|
113 |
+
var $_portName = '';
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Endpoint type e.g. 'wdsl'.
|
117 |
+
*
|
118 |
+
* @var string
|
119 |
+
*/
|
120 |
+
var $_endpointType = '';
|
121 |
+
|
122 |
+
/**
|
123 |
+
* The received xml.
|
124 |
+
*
|
125 |
+
* @var string
|
126 |
+
*/
|
127 |
+
var $xml;
|
128 |
+
|
129 |
+
/**
|
130 |
+
* The outgoing and incoming data stream for debugging.
|
131 |
+
*
|
132 |
+
* @var string
|
133 |
+
*/
|
134 |
+
var $wire;
|
135 |
+
|
136 |
+
/**
|
137 |
+
* The outgoing data stream for debugging.
|
138 |
+
*
|
139 |
+
* @var string
|
140 |
+
*/
|
141 |
+
var $_last_request = null;
|
142 |
+
|
143 |
+
/**
|
144 |
+
* The incoming data stream for debugging.
|
145 |
+
*
|
146 |
+
* @var string
|
147 |
+
*/
|
148 |
+
var $_last_response = null;
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Options.
|
152 |
+
*
|
153 |
+
* @var array
|
154 |
+
*/
|
155 |
+
var $_options = array('trace' => false);
|
156 |
+
|
157 |
+
/**
|
158 |
+
* The character encoding used for XML parser, etc.
|
159 |
+
*
|
160 |
+
* @var string
|
161 |
+
*/
|
162 |
+
var $_encoding = SOAP_DEFAULT_ENCODING;
|
163 |
+
|
164 |
+
/**
|
165 |
+
* The array of SOAP_Headers that we are sending.
|
166 |
+
*
|
167 |
+
* @var array
|
168 |
+
*/
|
169 |
+
var $headersOut = null;
|
170 |
+
|
171 |
+
/**
|
172 |
+
* The headers we recieved back in the response.
|
173 |
+
*
|
174 |
+
* @var array
|
175 |
+
*/
|
176 |
+
var $headersIn = null;
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Options for the HTTP_Request class (see HTTP/Request.php).
|
180 |
+
*
|
181 |
+
* @var array
|
182 |
+
*/
|
183 |
+
var $_proxy_params = array();
|
184 |
+
|
185 |
+
/**
|
186 |
+
* The SOAP_Transport instance.
|
187 |
+
*
|
188 |
+
* @var SOAP_Transport
|
189 |
+
*/
|
190 |
+
var $_soap_transport = null;
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Constructor.
|
194 |
+
*
|
195 |
+
* @access public
|
196 |
+
*
|
197 |
+
* @param string $endpoint An URL.
|
198 |
+
* @param boolean $wsdl Whether the endpoint is a WSDL file.
|
199 |
+
* @param string $portName The service's port name to use.
|
200 |
+
* @param array $proxy_params Options for the HTTP_Request class
|
201 |
+
* @see HTTP_Request
|
202 |
+
* @param boolean|string $cache Use WSDL caching? The cache directory if
|
203 |
+
* a string.
|
204 |
+
*/
|
205 |
+
function SOAP_Client($endpoint, $wsdl = false, $portName = false,
|
206 |
+
$proxy_params = array(), $cache = false)
|
207 |
+
{
|
208 |
+
parent::SOAP_Base('Client');
|
209 |
+
|
210 |
+
$this->_endpoint = $endpoint;
|
211 |
+
$this->_portName = $portName;
|
212 |
+
$this->_proxy_params = $proxy_params;
|
213 |
+
|
214 |
+
// This hack should perhaps be removed as it might cause unexpected
|
215 |
+
// behaviour.
|
216 |
+
$wsdl = $wsdl
|
217 |
+
? $wsdl
|
218 |
+
: strtolower(substr($endpoint, -4)) == 'wsdl';
|
219 |
+
|
220 |
+
// make values
|
221 |
+
if ($wsdl) {
|
222 |
+
$this->_endpointType = 'wsdl';
|
223 |
+
// instantiate wsdl class
|
224 |
+
$this->_wsdl = new SOAP_WSDL($this->_endpoint,
|
225 |
+
$this->_proxy_params,
|
226 |
+
$cache);
|
227 |
+
if ($this->_wsdl->fault) {
|
228 |
+
$this->_raiseSoapFault($this->_wsdl->fault);
|
229 |
+
}
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
function _reset()
|
234 |
+
{
|
235 |
+
$this->xml = null;
|
236 |
+
$this->wire = null;
|
237 |
+
$this->_last_request = null;
|
238 |
+
$this->_last_response = null;
|
239 |
+
$this->headersIn = null;
|
240 |
+
$this->headersOut = null;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Sets the character encoding.
|
245 |
+
*
|
246 |
+
* Limited to 'UTF-8', 'US_ASCII' and 'ISO-8859-1'.
|
247 |
+
*
|
248 |
+
* @access public
|
249 |
+
*
|
250 |
+
* @param string encoding
|
251 |
+
*
|
252 |
+
* @return mixed SOAP_Fault on error.
|
253 |
+
*/
|
254 |
+
function setEncoding($encoding)
|
255 |
+
{
|
256 |
+
if (in_array($encoding, $this->_encodings)) {
|
257 |
+
$this->_encoding = $encoding;
|
258 |
+
return;
|
259 |
+
}
|
260 |
+
return $this->_raiseSoapFault('Invalid Encoding');
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Adds a header to the envelope.
|
265 |
+
*
|
266 |
+
* @access public
|
267 |
+
*
|
268 |
+
* @param SOAP_Header $soap_value A SOAP_Header or an array with the
|
269 |
+
* elements 'name', 'namespace',
|
270 |
+
* 'mustunderstand', and 'actor' to send
|
271 |
+
* as a header.
|
272 |
+
*/
|
273 |
+
function addHeader($soap_value)
|
274 |
+
{
|
275 |
+
// Add a new header to the message.
|
276 |
+
if (is_a($soap_value, 'SOAP_Header')) {
|
277 |
+
$this->headersOut[] = $soap_value;
|
278 |
+
} elseif (is_array($soap_value)) {
|
279 |
+
// name, value, namespace, mustunderstand, actor
|
280 |
+
$this->headersOut[] = new SOAP_Header($soap_value[0],
|
281 |
+
null,
|
282 |
+
$soap_value[1],
|
283 |
+
$soap_value[2],
|
284 |
+
$soap_value[3]);
|
285 |
+
} else {
|
286 |
+
$this->_raiseSoapFault('Invalid parameter provided to addHeader(). Must be an array or a SOAP_Header.');
|
287 |
+
}
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Calls a method on the SOAP endpoint.
|
292 |
+
*
|
293 |
+
* The namespace parameter is overloaded to accept an array of options
|
294 |
+
* that can contain data necessary for various transports if it is used as
|
295 |
+
* an array, it MAY contain a namespace value and a soapaction value. If
|
296 |
+
* it is overloaded, the soapaction parameter is ignored and MUST be
|
297 |
+
* placed in the options array. This is done to provide backwards
|
298 |
+
* compatibility with current clients, but may be removed in the future.
|
299 |
+
* The currently supported values are:
|
300 |
+
* - 'namespace'
|
301 |
+
* - 'soapaction'
|
302 |
+
* - 'timeout': HTTP socket timeout
|
303 |
+
* - 'transfer-encoding': SMTP transport, Content-Transfer-Encoding: header
|
304 |
+
* - 'from': SMTP transport, From: header
|
305 |
+
* - 'subject': SMTP transport, Subject: header
|
306 |
+
* - 'headers': SMTP transport, hash of extra SMTP headers
|
307 |
+
* - 'attachments': what encoding to use for attachments (Mime, Dime)
|
308 |
+
* - 'trace': whether to trace the SOAP communication
|
309 |
+
* - 'style': 'document' or 'rpc'; when set to 'document' the parameters
|
310 |
+
* are not wrapped inside a tag with the SOAP action name
|
311 |
+
* - 'use': 'literal' for literal encoding, anything else for section 5
|
312 |
+
* encoding; when set to 'literal' SOAP types will be omitted.
|
313 |
+
* - 'keep_arrays_flat': use the tag name multiple times for each element
|
314 |
+
* when passing in an array in literal mode
|
315 |
+
* - 'no_type_prefix': supress adding of the namespace prefix
|
316 |
+
*
|
317 |
+
* @access public
|
318 |
+
*
|
319 |
+
* @param string $method The method to call.
|
320 |
+
* @param array $params The method parameters.
|
321 |
+
* @param string|array $namespace Namespace or hash with options. Note:
|
322 |
+
* most options need to be repeated for
|
323 |
+
* SOAP_Value instances.
|
324 |
+
* @param string $soapAction
|
325 |
+
*
|
326 |
+
* @return mixed The method result or a SOAP_Fault on error.
|
327 |
+
*/
|
328 |
+
function call($method, $params, $namespace = false, $soapAction = false)
|
329 |
+
{
|
330 |
+
$this->headersIn = null;
|
331 |
+
$this->_last_request = null;
|
332 |
+
$this->_last_response = null;
|
333 |
+
$this->wire = null;
|
334 |
+
$this->xml = null;
|
335 |
+
|
336 |
+
$soap_data = $this->_generate($method, $params, $namespace, $soapAction);
|
337 |
+
if (PEAR::isError($soap_data)) {
|
338 |
+
$fault = $this->_raiseSoapFault($soap_data);
|
339 |
+
return $fault;
|
340 |
+
}
|
341 |
+
|
342 |
+
// _generate() may have changed the endpoint if the WSDL has more
|
343 |
+
// than one service, so we need to see if we need to generate a new
|
344 |
+
// transport to hook to a different URI. Since the transport protocol
|
345 |
+
// can also change, we need to get an entirely new object. This could
|
346 |
+
// probably be optimized.
|
347 |
+
if (!$this->_soap_transport ||
|
348 |
+
$this->_endpoint != $this->_soap_transport->url) {
|
349 |
+
$this->_soap_transport = SOAP_Transport::getTransport($this->_endpoint);
|
350 |
+
if (PEAR::isError($this->_soap_transport)) {
|
351 |
+
$fault = $this->_raiseSoapFault($this->_soap_transport);
|
352 |
+
$this->_soap_transport = null;
|
353 |
+
return $fault;
|
354 |
+
}
|
355 |
+
}
|
356 |
+
$this->_soap_transport->encoding = $this->_encoding;
|
357 |
+
|
358 |
+
// Send the message.
|
359 |
+
$transport_options = array_merge_recursive($this->_proxy_params,
|
360 |
+
$this->_options);
|
361 |
+
$this->xml = $this->_soap_transport->send($soap_data, $transport_options);
|
362 |
+
|
363 |
+
// Save the wire information for debugging.
|
364 |
+
if ($this->_options['trace']) {
|
365 |
+
$this->_last_request = $this->_soap_transport->outgoing_payload;
|
366 |
+
$this->_last_response = $this->_soap_transport->incoming_payload;
|
367 |
+
$this->wire = $this->getWire();
|
368 |
+
}
|
369 |
+
if ($this->_soap_transport->fault) {
|
370 |
+
$fault = $this->_raiseSoapFault($this->xml);
|
371 |
+
return $fault;
|
372 |
+
}
|
373 |
+
|
374 |
+
if (isset($this->_options['result']) &&
|
375 |
+
$this->_options['result'] != 'parse') {
|
376 |
+
return $this->xml;
|
377 |
+
}
|
378 |
+
|
379 |
+
$this->__result_encoding = $this->_soap_transport->result_encoding;
|
380 |
+
|
381 |
+
$result = $this->parseResponse($this->xml, $this->__result_encoding,
|
382 |
+
$this->_soap_transport->attachments);
|
383 |
+
return $result;
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Sets an option to use with the transport layers.
|
388 |
+
*
|
389 |
+
* For example:
|
390 |
+
* <code>
|
391 |
+
* $soapclient->setOpt('curl', CURLOPT_VERBOSE, 1)
|
392 |
+
* </code>
|
393 |
+
* to pass a specific option to curl if using an SSL connection.
|
394 |
+
*
|
395 |
+
* @access public
|
396 |
+
*
|
397 |
+
* @param string $category Category to which the option applies or option
|
398 |
+
* name.
|
399 |
+
* @param string $option An option name if $category is a category name,
|
400 |
+
* an option value if $category is an option name.
|
401 |
+
* @param string $value An option value if $category is a category
|
402 |
+
* name.
|
403 |
+
*/
|
404 |
+
function setOpt($category, $option, $value = null)
|
405 |
+
{
|
406 |
+
if (!is_null($value)) {
|
407 |
+
if (!isset($this->_options[$category])) {
|
408 |
+
$this->_options[$category] = array();
|
409 |
+
}
|
410 |
+
$this->_options[$category][$option] = $value;
|
411 |
+
} else {
|
412 |
+
$this->_options[$category] = $option;
|
413 |
+
}
|
414 |
+
}
|
415 |
+
|
416 |
+
/**
|
417 |
+
* Call method supporting the overload extension.
|
418 |
+
*
|
419 |
+
* If the overload extension is loaded, you can call the client class with
|
420 |
+
* a soap method name:
|
421 |
+
* <code>
|
422 |
+
* $soap = new SOAP_Client(....);
|
423 |
+
* $value = $soap->getStockQuote('MSFT');
|
424 |
+
* </code>
|
425 |
+
*
|
426 |
+
* @access public
|
427 |
+
*
|
428 |
+
* @param string $method The method to call.
|
429 |
+
* @param array $params The method parameters.
|
430 |
+
* @param mixed $return_value Will get the method's return value
|
431 |
+
* assigned.
|
432 |
+
*
|
433 |
+
* @return boolean Always true.
|
434 |
+
*/
|
435 |
+
function _call($method, $params, &$return_value)
|
436 |
+
{
|
437 |
+
// Overloading lowercases the method name, we need to look into the
|
438 |
+
// WSDL and try to find the correct method name to get the correct
|
439 |
+
// case for the call.
|
440 |
+
if ($this->_wsdl) {
|
441 |
+
$this->_wsdl->matchMethod($method);
|
442 |
+
}
|
443 |
+
|
444 |
+
$return_value = $this->call($method, $params);
|
445 |
+
|
446 |
+
return true;
|
447 |
+
}
|
448 |
+
|
449 |
+
/**
|
450 |
+
* Returns the XML content of the last SOAP request.
|
451 |
+
*
|
452 |
+
* @return string The last request.
|
453 |
+
*/
|
454 |
+
function getLastRequest()
|
455 |
+
{
|
456 |
+
return $this->_last_request;
|
457 |
+
}
|
458 |
+
|
459 |
+
/**
|
460 |
+
* Returns the XML content of the last SOAP response.
|
461 |
+
*
|
462 |
+
* @return string The last response.
|
463 |
+
*/
|
464 |
+
function getLastResponse()
|
465 |
+
{
|
466 |
+
return $this->_last_response;
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Sets the SOAP encoding.
|
471 |
+
*
|
472 |
+
* The default encoding is section 5 encoded.
|
473 |
+
*
|
474 |
+
* @param string $use Either 'literal' or 'encoded' (section 5).
|
475 |
+
*/
|
476 |
+
function setUse($use)
|
477 |
+
{
|
478 |
+
$this->_options['use'] = $use;
|
479 |
+
}
|
480 |
+
|
481 |
+
/**
|
482 |
+
* Sets the SOAP encoding style.
|
483 |
+
*
|
484 |
+
* The default style is rpc.
|
485 |
+
*
|
486 |
+
* @param string $style Either 'document' or 'rpc'.
|
487 |
+
*/
|
488 |
+
function setStyle($style)
|
489 |
+
{
|
490 |
+
$this->_options['style'] = $style;
|
491 |
+
}
|
492 |
+
|
493 |
+
/**
|
494 |
+
* Sets whether to trace the traffic on the transport level.
|
495 |
+
*
|
496 |
+
* @see getWire()
|
497 |
+
*
|
498 |
+
* @param boolean $trace
|
499 |
+
*/
|
500 |
+
function setTrace($trace)
|
501 |
+
{
|
502 |
+
$this->_options['trace'] = $trace;
|
503 |
+
}
|
504 |
+
|
505 |
+
/**
|
506 |
+
* Generates the complete XML SOAP message for an RPC call.
|
507 |
+
*
|
508 |
+
* @see call()
|
509 |
+
*
|
510 |
+
* @param string $method The method to call.
|
511 |
+
* @param array $params The method parameters.
|
512 |
+
* @param string|array $namespace Namespace or hash with options. Note:
|
513 |
+
* most options need to be repeated for
|
514 |
+
* SOAP_Value instances.
|
515 |
+
* @param string $soapAction
|
516 |
+
*
|
517 |
+
* @return string The SOAP message including envelope.
|
518 |
+
*/
|
519 |
+
function _generate($method, $params, $namespace = false,
|
520 |
+
$soapAction = false)
|
521 |
+
{
|
522 |
+
$this->fault = null;
|
523 |
+
$this->_options['input'] = 'parse';
|
524 |
+
$this->_options['result'] = 'parse';
|
525 |
+
$this->_options['parameters'] = false;
|
526 |
+
|
527 |
+
if ($params && !is_array($params)) {
|
528 |
+
$params = array($params);
|
529 |
+
}
|
530 |
+
|
531 |
+
if (is_array($namespace)) {
|
532 |
+
// Options passed as a hash.
|
533 |
+
foreach ($namespace as $optname => $opt) {
|
534 |
+
$this->_options[strtolower($optname)] = $opt;
|
535 |
+
}
|
536 |
+
} else {
|
537 |
+
// We'll place $soapAction into our array for usage in the
|
538 |
+
// transport.
|
539 |
+
if ($soapAction) {
|
540 |
+
$this->_options['soapaction'] = $soapAction;
|
541 |
+
}
|
542 |
+
if ($namespace) {
|
543 |
+
$this->_options['namespace'] = $namespace;
|
544 |
+
}
|
545 |
+
}
|
546 |
+
if (isset($this->_options['namespace'])) {
|
547 |
+
$namespace = $this->_options['namespace'];
|
548 |
+
} else {
|
549 |
+
$namespace = false;
|
550 |
+
}
|
551 |
+
|
552 |
+
if ($this->_endpointType == 'wsdl') {
|
553 |
+
$this->_setSchemaVersion($this->_wsdl->xsd);
|
554 |
+
|
555 |
+
// Get port name.
|
556 |
+
if (!$this->_portName) {
|
557 |
+
$this->_portName = $this->_wsdl->getPortName($method);
|
558 |
+
}
|
559 |
+
if (PEAR::isError($this->_portName)) {
|
560 |
+
return $this->_raiseSoapFault($this->_portName);
|
561 |
+
}
|
562 |
+
|
563 |
+
// Get endpoint.
|
564 |
+
$this->_endpoint = $this->_wsdl->getEndpoint($this->_portName);
|
565 |
+
if (PEAR::isError($this->_endpoint)) {
|
566 |
+
return $this->_raiseSoapFault($this->_endpoint);
|
567 |
+
}
|
568 |
+
|
569 |
+
// Get operation data.
|
570 |
+
$opData = $this->_wsdl->getOperationData($this->_portName, $method);
|
571 |
+
|
572 |
+
if (PEAR::isError($opData)) {
|
573 |
+
return $this->_raiseSoapFault($opData);
|
574 |
+
}
|
575 |
+
$namespace = isset($opData['namespace'])?$opData['namespace']:'';
|
576 |
+
$this->_options['style'] = $opData['style'];
|
577 |
+
$this->_options['use'] = $opData['input']['use'];
|
578 |
+
$this->_options['soapaction'] = $opData['soapAction'];
|
579 |
+
|
580 |
+
// Set input parameters.
|
581 |
+
if ($this->_options['input'] == 'parse') {
|
582 |
+
$this->_options['parameters'] = $opData['parameters'];
|
583 |
+
$nparams = array();
|
584 |
+
if (isset($opData['input']['parts']) &&
|
585 |
+
count($opData['input']['parts'])) {
|
586 |
+
foreach ($opData['input']['parts'] as $name => $part) {
|
587 |
+
$xmlns = '';
|
588 |
+
$attrs = array();
|
589 |
+
// Is the name a complex type?
|
590 |
+
if (isset($part['element'])) {
|
591 |
+
$xmlns = $this->_wsdl->namespaces[$part['namespace']];
|
592 |
+
$part = $this->_wsdl->elements[$part['namespace']][$part['type']];
|
593 |
+
$name = $part['name'];
|
594 |
+
}
|
595 |
+
if (isset($params[$name]) ||
|
596 |
+
$this->_wsdl->getDataHandler($name, $part['namespace'])) {
|
597 |
+
$nparams[$name] =& $params[$name];
|
598 |
+
} else {
|
599 |
+
// We now force an associative array for
|
600 |
+
// parameters if using WSDL.
|
601 |
+
return $this->_raiseSoapFault("The named parameter $name is not in the call parameters.");
|
602 |
+
}
|
603 |
+
if (gettype($nparams[$name]) != 'object' ||
|
604 |
+
!$nparams[$name] instanceof SOAP_Value) {
|
605 |
+
// Type is likely a qname, split it apart, and get
|
606 |
+
// the type namespace from WSDL.
|
607 |
+
$qname = new QName($part['type']);
|
608 |
+
if ($qname->ns) {
|
609 |
+
$type_namespace = $this->_wsdl->namespaces[$qname->ns];
|
610 |
+
} elseif (isset($part['namespace'])) {
|
611 |
+
$type_namespace = $this->_wsdl->namespaces[$part['namespace']];
|
612 |
+
} else {
|
613 |
+
$type_namespace = null;
|
614 |
+
}
|
615 |
+
$qname->namespace = $type_namespace;
|
616 |
+
$pqname = $name;
|
617 |
+
if ($xmlns) {
|
618 |
+
$pqname = '{' . $xmlns . '}' . $name;
|
619 |
+
}
|
620 |
+
$nparams[$name] = new SOAP_Value($pqname,
|
621 |
+
$qname->fqn(),
|
622 |
+
$nparams[$name],
|
623 |
+
$attrs);
|
624 |
+
} else {
|
625 |
+
// WSDL fixups to the SOAP value.
|
626 |
+
}
|
627 |
+
}
|
628 |
+
}
|
629 |
+
$params =& $nparams;
|
630 |
+
unset($nparams);
|
631 |
+
}
|
632 |
+
} else {
|
633 |
+
$this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION);
|
634 |
+
}
|
635 |
+
|
636 |
+
// Serialize the message.
|
637 |
+
$this->_section5 = (!isset($this->_options['use']) ||
|
638 |
+
$this->_options['use'] != 'literal');
|
639 |
+
|
640 |
+
if (!isset($this->_options['style']) ||
|
641 |
+
$this->_options['style'] == 'rpc') {
|
642 |
+
$this->_options['style'] = 'rpc';
|
643 |
+
$this->docparams = true;
|
644 |
+
$mqname = new QName($method, $namespace);
|
645 |
+
$methodValue = new SOAP_Value($mqname->fqn(), 'Struct', $params,
|
646 |
+
array(), $this->_options);
|
647 |
+
$soap_msg = $this->makeEnvelope($methodValue,
|
648 |
+
$this->headersOut,
|
649 |
+
$this->_encoding,
|
650 |
+
$this->_options);
|
651 |
+
} else {
|
652 |
+
if (!$params) {
|
653 |
+
$mqname = new QName($method, $namespace);
|
654 |
+
$params = new SOAP_Value($mqname->fqn(), 'Struct', null);
|
655 |
+
} elseif ($this->_options['input'] == 'parse') {
|
656 |
+
if (is_array($params)) {
|
657 |
+
$nparams = array();
|
658 |
+
$keys = array_keys($params);
|
659 |
+
foreach ($keys as $k) {
|
660 |
+
if (gettype($params[$k]) != 'object') {
|
661 |
+
$nparams[] = new SOAP_Value($k,
|
662 |
+
false,
|
663 |
+
$params[$k]);
|
664 |
+
} else {
|
665 |
+
$nparams[] =& $params[$k];
|
666 |
+
}
|
667 |
+
}
|
668 |
+
$params =& $nparams;
|
669 |
+
}
|
670 |
+
if ($this->_options['parameters']) {
|
671 |
+
$mqname = new QName($method, $namespace);
|
672 |
+
$params = new SOAP_Value($mqname->fqn(),
|
673 |
+
'Struct',
|
674 |
+
$params);
|
675 |
+
}
|
676 |
+
}
|
677 |
+
$soap_msg = $this->makeEnvelope($params,
|
678 |
+
$this->headersOut,
|
679 |
+
$this->_encoding,
|
680 |
+
$this->_options);
|
681 |
+
}
|
682 |
+
$this->headersOut = null;
|
683 |
+
|
684 |
+
if (PEAR::isError($soap_msg)) {
|
685 |
+
return $this->_raiseSoapFault($soap_msg);
|
686 |
+
}
|
687 |
+
|
688 |
+
// Handle MIME or DIME encoding.
|
689 |
+
// TODO: DIME encoding should move to the transport, do it here for
|
690 |
+
// now and for ease of getting it done.
|
691 |
+
if (count($this->_attachments)) {
|
692 |
+
if ((isset($this->_options['attachments']) &&
|
693 |
+
$this->_options['attachments'] == 'Mime') ||
|
694 |
+
isset($this->_options['Mime'])) {
|
695 |
+
$soap_msg = $this->_makeMimeMessage($soap_msg, $this->_encoding);
|
696 |
+
} else {
|
697 |
+
// default is dime
|
698 |
+
$soap_msg = $this->_makeDIMEMessage($soap_msg, $this->_encoding);
|
699 |
+
$this->_options['headers']['Content-Type'] = 'application/dime';
|
700 |
+
}
|
701 |
+
if (PEAR::isError($soap_msg)) {
|
702 |
+
return $this->_raiseSoapFault($soap_msg);
|
703 |
+
}
|
704 |
+
}
|
705 |
+
|
706 |
+
// Instantiate client.
|
707 |
+
if (is_array($soap_msg)) {
|
708 |
+
$soap_data = $soap_msg['body'];
|
709 |
+
if (count($soap_msg['headers'])) {
|
710 |
+
if (isset($this->_options['headers'])) {
|
711 |
+
$this->_options['headers'] = array_merge($this->_options['headers'], $soap_msg['headers']);
|
712 |
+
} else {
|
713 |
+
$this->_options['headers'] = $soap_msg['headers'];
|
714 |
+
}
|
715 |
+
}
|
716 |
+
} else {
|
717 |
+
$soap_data = $soap_msg;
|
718 |
+
}
|
719 |
+
|
720 |
+
return $soap_data;
|
721 |
+
}
|
722 |
+
|
723 |
+
/**
|
724 |
+
* Parses a SOAP response.
|
725 |
+
*
|
726 |
+
* @see SOAP_Parser::
|
727 |
+
*
|
728 |
+
* @param string $response XML content of SOAP response.
|
729 |
+
* @param string $encoding Character set encoding, defaults to 'UTF-8'.
|
730 |
+
* @param array $attachments List of attachments.
|
731 |
+
*/
|
732 |
+
function parseResponse($response, $encoding, $attachments)
|
733 |
+
{
|
734 |
+
// Parse the response.
|
735 |
+
$response = new SOAP_Parser($response, $encoding, $attachments);
|
736 |
+
if ($response->fault) {
|
737 |
+
$fault = $this->_raiseSoapFault($response->fault);
|
738 |
+
return $fault;
|
739 |
+
}
|
740 |
+
|
741 |
+
// Return array of parameters.
|
742 |
+
$return = $response->getResponse();
|
743 |
+
$headers = $response->getHeaders();
|
744 |
+
if ($headers) {
|
745 |
+
$this->headersIn = $this->_decodeResponse($headers, false);
|
746 |
+
}
|
747 |
+
|
748 |
+
$decoded = $this->_decodeResponse($return);
|
749 |
+
return $decoded;
|
750 |
+
}
|
751 |
+
|
752 |
+
/**
|
753 |
+
* Converts a complex SOAP_Value into a PHP Array
|
754 |
+
*
|
755 |
+
* @param SOAP_Value $response Value object.
|
756 |
+
* @param boolean $shift
|
757 |
+
*
|
758 |
+
* @return array
|
759 |
+
*/
|
760 |
+
function _decodeResponse($response, $shift = true)
|
761 |
+
{
|
762 |
+
if (!$response) {
|
763 |
+
$decoded = null;
|
764 |
+
return $decoded;
|
765 |
+
}
|
766 |
+
|
767 |
+
// Check for valid response.
|
768 |
+
if (PEAR::isError($response)) {
|
769 |
+
$fault = $this->_raiseSoapFault($response);
|
770 |
+
return $fault;
|
771 |
+
} elseif (!$response instanceof SOAP_Value) {
|
772 |
+
$fault = $this->_raiseSoapFault("Didn't get SOAP_Value object back from client");
|
773 |
+
return $fault;
|
774 |
+
}
|
775 |
+
|
776 |
+
// Decode to native php datatype.
|
777 |
+
$returnArray = $this->_decode($response);
|
778 |
+
|
779 |
+
// Fault?
|
780 |
+
if (PEAR::isError($returnArray)) {
|
781 |
+
$fault = $this->_raiseSoapFault($returnArray);
|
782 |
+
return $fault;
|
783 |
+
}
|
784 |
+
|
785 |
+
if (is_object($returnArray) &&
|
786 |
+
strcasecmp(get_class($returnArray), 'stdClass') == 0) {
|
787 |
+
$returnArray = get_object_vars($returnArray);
|
788 |
+
}
|
789 |
+
|
790 |
+
if (is_array($returnArray)) {
|
791 |
+
if (isset($returnArray['faultcode']) ||
|
792 |
+
isset($returnArray[SOAP_BASE::SOAPENVPrefix().':faultcode'])) {
|
793 |
+
$faultcode = $faultstring = $faultdetail = $faultactor = '';
|
794 |
+
foreach ($returnArray as $k => $v) {
|
795 |
+
if (stristr($k, 'faultcode')) $faultcode = $v;
|
796 |
+
if (stristr($k, 'faultstring')) $faultstring = $v;
|
797 |
+
if (stristr($k, 'detail')) $faultdetail = $v;
|
798 |
+
if (stristr($k, 'faultactor')) $faultactor = $v;
|
799 |
+
}
|
800 |
+
$fault = $this->_raiseSoapFault($faultstring, $faultdetail,
|
801 |
+
$faultactor, $faultcode);
|
802 |
+
return $fault;
|
803 |
+
}
|
804 |
+
// Return array of return values.
|
805 |
+
if ($shift && count($returnArray) == 1) {
|
806 |
+
$decoded = array_shift($returnArray);
|
807 |
+
return $decoded;
|
808 |
+
}
|
809 |
+
return $returnArray;
|
810 |
+
}
|
811 |
+
|
812 |
+
return $returnArray;
|
813 |
+
}
|
814 |
+
|
815 |
+
/**
|
816 |
+
* Returns the outgoing and incoming traffic on the transport level.
|
817 |
+
*
|
818 |
+
* Tracing has to be enabled.
|
819 |
+
*
|
820 |
+
* @see setTrace()
|
821 |
+
*
|
822 |
+
* @return string The complete traffic between the client and the server.
|
823 |
+
*/
|
824 |
+
function getWire()
|
825 |
+
{
|
826 |
+
if ($this->_options['trace'] &&
|
827 |
+
($this->_last_request || $this->_last_response)) {
|
828 |
+
return "OUTGOING:\n\n" .
|
829 |
+
$this->_last_request .
|
830 |
+
"\n\nINCOMING\n\n" .
|
831 |
+
preg_replace("/></",">\r\n<", $this->_last_response);
|
832 |
+
}
|
833 |
+
|
834 |
+
return null;
|
835 |
+
}
|
836 |
+
|
837 |
+
}
|
lib/PEAR/SOAP/Fault.php
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file contains the SOAP_Fault class, used for all error objects in this
|
4 |
+
* package.
|
5 |
+
*
|
6 |
+
* PHP versions 4 and 5
|
7 |
+
*
|
8 |
+
* LICENSE: This source file is subject to version 2.02 of the PHP license,
|
9 |
+
* that is bundled with this package in the file LICENSE, and is available at
|
10 |
+
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
|
11 |
+
* did not receive a copy of the PHP license and are unable to obtain it
|
12 |
+
* through the world-wide-web, please send a note to license@php.net so we can
|
13 |
+
* mail you a copy immediately.
|
14 |
+
*
|
15 |
+
* @category Web Services
|
16 |
+
* @package SOAP
|
17 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
18 |
+
* @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
|
19 |
+
* @author Chuck Hagenbuch <chuck@horde.org> Maintenance
|
20 |
+
* @author Jan Schneider <jan@horde.org> Maintenance
|
21 |
+
* @copyright 2003-2006 The PHP Group
|
22 |
+
* @license http://www.php.net/license/2_02.txt PHP License 2.02
|
23 |
+
* @link http://pear.php.net/package/SOAP
|
24 |
+
*/
|
25 |
+
|
26 |
+
/** PEAR_Error */
|
27 |
+
require_once 'PEAR.php';
|
28 |
+
|
29 |
+
/**
|
30 |
+
* PEAR::Error wrapper used to match SOAP Faults to PEAR Errors
|
31 |
+
*
|
32 |
+
* SOAP_Fault can provide a complete backtrace of the error. Revealing these
|
33 |
+
* details in a public web services is a bad idea because it can be used by
|
34 |
+
* attackers. Thus you have to enable backtrace information in SOAP_Fault
|
35 |
+
* responses by putting the following code in your script after your
|
36 |
+
* "require_once 'SOAP/Server.php';" line:
|
37 |
+
*
|
38 |
+
* <code>
|
39 |
+
* $backtrace =& PEAR::getStaticProperty('SOAP_Fault', 'backtrace');
|
40 |
+
* $backtrace = true;
|
41 |
+
* </code>
|
42 |
+
*
|
43 |
+
* @package SOAP
|
44 |
+
* @access public
|
45 |
+
* @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
|
46 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
47 |
+
*/
|
48 |
+
class SOAP_Fault extends PEAR_Error
|
49 |
+
{
|
50 |
+
/**
|
51 |
+
* Constructor.
|
52 |
+
*
|
53 |
+
* @param string $faultstring Message string for fault.
|
54 |
+
* @param mixed $faultcode The faultcode.
|
55 |
+
* @param mixed $faultactor
|
56 |
+
* @param mixed $detail @see PEAR_Error
|
57 |
+
* @param array $mode @see PEAR_Error
|
58 |
+
* @param array $options @see PEAR_Error
|
59 |
+
*/
|
60 |
+
function SOAP_Fault($faultstring = 'unknown error', $faultcode = 'Client',
|
61 |
+
$faultactor = null, $detail = null, $mode = null,
|
62 |
+
$options = null)
|
63 |
+
{
|
64 |
+
parent::PEAR_Error($faultstring, $faultcode, $mode, $options, $detail);
|
65 |
+
if ($faultactor) {
|
66 |
+
$this->error_message_prefix = $faultactor;
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Returns a SOAP XML message that can be sent as a server response.
|
72 |
+
*
|
73 |
+
* @return string
|
74 |
+
*/
|
75 |
+
function message($encoding = SOAP_DEFAULT_ENCODING)
|
76 |
+
{
|
77 |
+
$msg = new SOAP_Base();
|
78 |
+
$params = array();
|
79 |
+
$params[] = new SOAP_Value('faultcode', 'QName', SOAP_BASE::SOAPENVPrefix().':' . $this->code);
|
80 |
+
$params[] = new SOAP_Value('faultstring', 'string', $this->message);
|
81 |
+
$params[] = new SOAP_Value('faultactor', 'anyURI', $this->error_message_prefix);
|
82 |
+
if (PEAR::getStaticProperty('SOAP_Fault', 'backtrace') &&
|
83 |
+
isset($this->backtrace)) {
|
84 |
+
$params[] = new SOAP_Value('detail', 'string', $this->backtrace);
|
85 |
+
} else {
|
86 |
+
$params[] = new SOAP_Value('detail', 'string', $this->userinfo);
|
87 |
+
}
|
88 |
+
|
89 |
+
$methodValue = new SOAP_Value('{' . SOAP_ENVELOP . '}Fault', 'Struct', $params);
|
90 |
+
$headers = null;
|
91 |
+
return $msg->makeEnvelope($methodValue, $headers, $encoding);
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Returns a simple native PHP array containing the fault data.
|
96 |
+
*
|
97 |
+
* @return array
|
98 |
+
*/
|
99 |
+
function getFault()
|
100 |
+
{
|
101 |
+
$fault = new stdClass();
|
102 |
+
$fault->faultcode = $this->code;
|
103 |
+
$fault->faultstring = $this->message;
|
104 |
+
$fault->faultactor = $this->error_message_prefix;
|
105 |
+
$fault->detail = $this->userinfo;
|
106 |
+
return $fault;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Returns the SOAP actor for the fault.
|
111 |
+
*
|
112 |
+
* @return string
|
113 |
+
*/
|
114 |
+
function getActor()
|
115 |
+
{
|
116 |
+
return $this->error_message_prefix;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Returns the fault detail.
|
121 |
+
*
|
122 |
+
* @return string
|
123 |
+
*/
|
124 |
+
function getDetail()
|
125 |
+
{
|
126 |
+
return $this->userinfo;
|
127 |
+
}
|
128 |
+
|
129 |
+
}
|
lib/PEAR/SOAP/Parser.php
ADDED
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file contains the code for the SOAP message parser.
|
4 |
+
*
|
5 |
+
* PHP versions 4 and 5
|
6 |
+
*
|
7 |
+
* LICENSE: This source file is subject to version 2.02 of the PHP license,
|
8 |
+
* that is bundled with this package in the file LICENSE, and is available at
|
9 |
+
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
|
10 |
+
* did not receive a copy of the PHP license and are unable to obtain it
|
11 |
+
* through the world-wide-web, please send a note to license@php.net so we can
|
12 |
+
* mail you a copy immediately.
|
13 |
+
*
|
14 |
+
* @category Web Services
|
15 |
+
* @package SOAP
|
16 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
17 |
+
* @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
|
18 |
+
* @author Chuck Hagenbuch <chuck@horde.org> Maintenance
|
19 |
+
* @author Jan Schneider <jan@horde.org> Maintenance
|
20 |
+
* @copyright 2003-2005 The PHP Group
|
21 |
+
* @license http://www.php.net/license/2_02.txt PHP License 2.02
|
22 |
+
* @link http://pear.php.net/package/SOAP
|
23 |
+
*/
|
24 |
+
|
25 |
+
require_once 'SOAP/Base.php';
|
26 |
+
require_once 'SOAP/Value.php';
|
27 |
+
|
28 |
+
/**
|
29 |
+
* SOAP Parser
|
30 |
+
*
|
31 |
+
* This class is used by SOAP::Message and SOAP::Server to parse soap
|
32 |
+
* packets. Originally based on SOAPx4 by Dietrich Ayala
|
33 |
+
* http://dietrich.ganx4.com/soapx4
|
34 |
+
*
|
35 |
+
* @access public
|
36 |
+
* @package SOAP
|
37 |
+
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
38 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
39 |
+
*/
|
40 |
+
class SOAP_Parser extends SOAP_Base
|
41 |
+
{
|
42 |
+
var $status = '';
|
43 |
+
var $position = 0;
|
44 |
+
var $depth = 0;
|
45 |
+
var $default_namespace = '';
|
46 |
+
var $message = array();
|
47 |
+
var $depth_array = array();
|
48 |
+
var $parent = 0;
|
49 |
+
var $root_struct_name = array();
|
50 |
+
var $header_struct_name = array();
|
51 |
+
var $curent_root_struct_name = '';
|
52 |
+
var $root_struct = array();
|
53 |
+
var $header_struct = array();
|
54 |
+
var $curent_root_struct = 0;
|
55 |
+
var $references = array();
|
56 |
+
var $need_references = array();
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Used to handle non-root elements before root body element.
|
60 |
+
*
|
61 |
+
* @var integer
|
62 |
+
*/
|
63 |
+
var $bodyDepth;
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Constructor.
|
67 |
+
*
|
68 |
+
* @param string $xml XML content.
|
69 |
+
* @param string $encoding Character set encoding, defaults to 'UTF-8'.
|
70 |
+
* @param array $attachments List of attachments.
|
71 |
+
*/
|
72 |
+
function SOAP_Parser($xml, $encoding = SOAP_DEFAULT_ENCODING,
|
73 |
+
$attachments = null)
|
74 |
+
{
|
75 |
+
parent::SOAP_Base('Parser');
|
76 |
+
$this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION);
|
77 |
+
|
78 |
+
$this->attachments = $attachments;
|
79 |
+
|
80 |
+
// Check the XML tag for encoding.
|
81 |
+
if (preg_match('/<\?xml[^>]+encoding\s*?=\s*?(\'([^\']*)\'|"([^"]*)")[^>]*?[\?]>/', $xml, $m)) {
|
82 |
+
$encoding = strtoupper($m[2] ? $m[2] : $m[3]);
|
83 |
+
}
|
84 |
+
|
85 |
+
// Determine where in the message we are (envelope, header, body,
|
86 |
+
// method). Check whether content has been read.
|
87 |
+
if (!empty($xml)) {
|
88 |
+
// Prepare the XML parser.
|
89 |
+
$parser = xml_parser_create($encoding);
|
90 |
+
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
|
91 |
+
xml_set_object($parser, $this);
|
92 |
+
xml_set_element_handler($parser, '_startElement', '_endElement');
|
93 |
+
xml_set_character_data_handler($parser, '_characterData');
|
94 |
+
|
95 |
+
// Some lame SOAP implementations add nul bytes at the end of the
|
96 |
+
// SOAP stream, and expat chokes on that.
|
97 |
+
if ($xml[strlen($xml) - 1] == 0) {
|
98 |
+
$xml = trim($xml);
|
99 |
+
}
|
100 |
+
|
101 |
+
// Parse the XML file.
|
102 |
+
if (!xml_parse($parser, $xml, true)) {
|
103 |
+
$err = sprintf('XML error on line %d col %d byte %d %s',
|
104 |
+
xml_get_current_line_number($parser),
|
105 |
+
xml_get_current_column_number($parser),
|
106 |
+
xml_get_current_byte_index($parser),
|
107 |
+
xml_error_string(xml_get_error_code($parser)));
|
108 |
+
$this->_raiseSoapFault($err, htmlspecialchars($xml));
|
109 |
+
}
|
110 |
+
xml_parser_free($parser);
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Returns an array of responses.
|
116 |
+
*
|
117 |
+
* After parsing a SOAP message, use this to get the response.
|
118 |
+
*
|
119 |
+
* @return array
|
120 |
+
*/
|
121 |
+
function getResponse()
|
122 |
+
{
|
123 |
+
if (!empty($this->root_struct[0])) {
|
124 |
+
return $this->_buildResponse($this->root_struct[0]);
|
125 |
+
} else {
|
126 |
+
return $this->_raiseSoapFault('Cannot build response');
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Returns an array of header responses.
|
132 |
+
*
|
133 |
+
* After parsing a SOAP message, use this to get the response.
|
134 |
+
*
|
135 |
+
* @return array
|
136 |
+
*/
|
137 |
+
function getHeaders()
|
138 |
+
{
|
139 |
+
if (!empty($this->header_struct[0])) {
|
140 |
+
return $this->_buildResponse($this->header_struct[0]);
|
141 |
+
} else {
|
142 |
+
// We don't fault if there are no headers; that can be handled by
|
143 |
+
// the application if necessary.
|
144 |
+
return null;
|
145 |
+
}
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Recurses to build a multi dimensional array.
|
150 |
+
*
|
151 |
+
* @see _buildResponse()
|
152 |
+
*/
|
153 |
+
function _domulti($d, &$ar, &$r, &$v, $ad = 0)
|
154 |
+
{
|
155 |
+
if ($d) {
|
156 |
+
$this->_domulti($d - 1, $ar, $r[$ar[$ad]], $v, $ad + 1);
|
157 |
+
} else {
|
158 |
+
$r = $v;
|
159 |
+
}
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Loops through the message, building response structures.
|
164 |
+
*
|
165 |
+
* @param integer $pos Position.
|
166 |
+
*
|
167 |
+
* @return SOAP_Value
|
168 |
+
*/
|
169 |
+
function _buildResponse($pos)
|
170 |
+
{
|
171 |
+
$response = null;
|
172 |
+
|
173 |
+
if (isset($this->message[$pos]['children'])) {
|
174 |
+
$children = explode('|', $this->message[$pos]['children']);
|
175 |
+
foreach ($children as $c => $child_pos) {
|
176 |
+
if ($this->message[$child_pos]['type'] != null) {
|
177 |
+
$response[] = $this->_buildResponse($child_pos);
|
178 |
+
}
|
179 |
+
}
|
180 |
+
if (isset($this->message[$pos]['arraySize'])) {
|
181 |
+
$ardepth = count($this->message[$pos]['arraySize']);
|
182 |
+
if ($ardepth > 1) {
|
183 |
+
$ar = array_pad(array(), $ardepth, 0);
|
184 |
+
if (isset($this->message[$pos]['arrayOffset'])) {
|
185 |
+
for ($i = 0; $i < $ardepth; $i++) {
|
186 |
+
$ar[$i] += $this->message[$pos]['arrayOffset'][$i];
|
187 |
+
}
|
188 |
+
}
|
189 |
+
$elc = count($response);
|
190 |
+
for ($i = 0; $i < $elc; $i++) {
|
191 |
+
// Recurse to build a multi dimensional array.
|
192 |
+
$this->_domulti($ardepth, $ar, $newresp, $response[$i]);
|
193 |
+
|
194 |
+
// Increment our array pointers.
|
195 |
+
$ad = $ardepth - 1;
|
196 |
+
$ar[$ad]++;
|
197 |
+
while ($ad > 0 &&
|
198 |
+
$ar[$ad] >= $this->message[$pos]['arraySize'][$ad]) {
|
199 |
+
$ar[$ad] = 0;
|
200 |
+
$ad--;
|
201 |
+
$ar[$ad]++;
|
202 |
+
}
|
203 |
+
}
|
204 |
+
$response = $newresp;
|
205 |
+
} elseif (isset($this->message[$pos]['arrayOffset']) &&
|
206 |
+
$this->message[$pos]['arrayOffset'][0] > 0) {
|
207 |
+
// Check for padding.
|
208 |
+
$pad = $this->message[$pos]['arrayOffset'][0] + count($response) * -1;
|
209 |
+
$response = array_pad($response, $pad, null);
|
210 |
+
}
|
211 |
+
}
|
212 |
+
}
|
213 |
+
|
214 |
+
// Build attributes.
|
215 |
+
$attrs = array();
|
216 |
+
foreach ($this->message[$pos]['attrs'] as $atn => $atv) {
|
217 |
+
if (!strstr($atn, 'xmlns') && !strpos($atn, ':')) {
|
218 |
+
$attrs[$atn] = $atv;
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
// Add current node's value.
|
223 |
+
$nqn = new QName($this->message[$pos]['name'],
|
224 |
+
$this->message[$pos]['namespace']);
|
225 |
+
$tqn = new QName($this->message[$pos]['type'],
|
226 |
+
$this->message[$pos]['type_namespace']);
|
227 |
+
if ($response) {
|
228 |
+
$response = new SOAP_Value($nqn->fqn(), $tqn->fqn(), $response,
|
229 |
+
$attrs);
|
230 |
+
if (isset($this->message[$pos]['arrayType'])) {
|
231 |
+
$response->arrayType = $this->message[$pos]['arrayType'];
|
232 |
+
}
|
233 |
+
} else {
|
234 |
+
// Check if value is an empty array
|
235 |
+
if ($tqn->name == 'Array') {
|
236 |
+
$response = new SOAP_Value($nqn->fqn(), $tqn->fqn(), array(),
|
237 |
+
$attrs);
|
238 |
+
//if ($pos == 4) var_dump($this->message[$pos], $response);
|
239 |
+
} else {
|
240 |
+
$response = new SOAP_Value($nqn->fqn(), $tqn->fqn(),
|
241 |
+
$this->message[$pos]['cdata'],
|
242 |
+
$attrs);
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
// Handle header attribute that we need.
|
247 |
+
if (array_key_exists('actor', $this->message[$pos])) {
|
248 |
+
$response->actor = $this->message[$pos]['actor'];
|
249 |
+
}
|
250 |
+
if (array_key_exists('mustUnderstand', $this->message[$pos])) {
|
251 |
+
$response->mustunderstand = $this->message[$pos]['mustUnderstand'];
|
252 |
+
}
|
253 |
+
|
254 |
+
return $response;
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Start element handler used with the XML parser.
|
259 |
+
*/
|
260 |
+
function _startElement($parser, $name, $attrs)
|
261 |
+
{
|
262 |
+
// Position in a total number of elements, starting from 0.
|
263 |
+
// Update class level position.
|
264 |
+
$pos = $this->position++;
|
265 |
+
|
266 |
+
// And set mine.
|
267 |
+
$this->message[$pos] = array(
|
268 |
+
'type' => '',
|
269 |
+
'type_namespace' => '',
|
270 |
+
'cdata' => '',
|
271 |
+
'pos' => $pos,
|
272 |
+
'id' => '');
|
273 |
+
|
274 |
+
// Parent/child/depth determinations.
|
275 |
+
|
276 |
+
// depth = How many levels removed from root?
|
277 |
+
// Set mine as current global depth and increment global depth value.
|
278 |
+
$this->message[$pos]['depth'] = $this->depth++;
|
279 |
+
|
280 |
+
// Else add self as child to whoever the current parent is.
|
281 |
+
if ($pos != 0) {
|
282 |
+
if (isset($this->message[$this->parent]['children'])) {
|
283 |
+
$this->message[$this->parent]['children'] .= '|' . $pos;
|
284 |
+
} else {
|
285 |
+
$this->message[$this->parent]['children'] = $pos;
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
// Set my parent.
|
290 |
+
$this->message[$pos]['parent'] = $this->parent;
|
291 |
+
|
292 |
+
// Set self as current value for this depth.
|
293 |
+
$this->depth_array[$this->depth] = $pos;
|
294 |
+
// Set self as current parent.
|
295 |
+
$this->parent = $pos;
|
296 |
+
$qname = new QName($name);
|
297 |
+
// Set status.
|
298 |
+
if (strcasecmp('envelope', $qname->name) == 0) {
|
299 |
+
$this->status = 'envelope';
|
300 |
+
} elseif (strcasecmp('header', $qname->name) == 0) {
|
301 |
+
$this->status = 'header';
|
302 |
+
$this->header_struct_name[] = $this->curent_root_struct_name = $qname->name;
|
303 |
+
$this->header_struct[] = $this->curent_root_struct = $pos;
|
304 |
+
$this->message[$pos]['type'] = 'Struct';
|
305 |
+
} elseif (strcasecmp('body', $qname->name) == 0) {
|
306 |
+
$this->status = 'body';
|
307 |
+
$this->bodyDepth = $this->depth;
|
308 |
+
|
309 |
+
// Set method
|
310 |
+
} elseif ($this->status == 'body') {
|
311 |
+
// Is this element allowed to be a root?
|
312 |
+
// TODO: this needs to be optimized, we loop through $attrs twice
|
313 |
+
// now.
|
314 |
+
$can_root = $this->depth == $this->bodyDepth + 1;
|
315 |
+
if ($can_root) {
|
316 |
+
foreach ($attrs as $key => $value) {
|
317 |
+
if (stristr($key, ':root') && !$value) {
|
318 |
+
$can_root = false;
|
319 |
+
}
|
320 |
+
}
|
321 |
+
}
|
322 |
+
|
323 |
+
if ($can_root) {
|
324 |
+
$this->status = 'method';
|
325 |
+
$this->root_struct_name[] = $this->curent_root_struct_name = $qname->name;
|
326 |
+
$this->root_struct[] = $this->curent_root_struct = $pos;
|
327 |
+
$this->message[$pos]['type'] = 'Struct';
|
328 |
+
}
|
329 |
+
}
|
330 |
+
|
331 |
+
// Set my status.
|
332 |
+
$this->message[$pos]['status'] = $this->status;
|
333 |
+
|
334 |
+
// Set name.
|
335 |
+
$this->message[$pos]['name'] = htmlspecialchars($qname->name);
|
336 |
+
|
337 |
+
// Set attributes.
|
338 |
+
$this->message[$pos]['attrs'] = $attrs;
|
339 |
+
|
340 |
+
// Loop through attributes, logging ns and type declarations.
|
341 |
+
foreach ($attrs as $key => $value) {
|
342 |
+
// If ns declarations, add to class level array of valid
|
343 |
+
// namespaces.
|
344 |
+
$kqn = new QName($key);
|
345 |
+
if ($kqn->ns == 'xmlns') {
|
346 |
+
$prefix = $kqn->name;
|
347 |
+
|
348 |
+
if (in_array($value, $this->_XMLSchema)) {
|
349 |
+
$this->_setSchemaVersion($value);
|
350 |
+
}
|
351 |
+
|
352 |
+
$this->_namespaces[$value] = $prefix;
|
353 |
+
|
354 |
+
// Set method namespace.
|
355 |
+
} elseif ($key == 'xmlns') {
|
356 |
+
$qname->ns = $this->_getNamespacePrefix($value);
|
357 |
+
$qname->namespace = $value;
|
358 |
+
} elseif ($kqn->name == 'actor') {
|
359 |
+
$this->message[$pos]['actor'] = $value;
|
360 |
+
} elseif ($kqn->name == 'mustUnderstand') {
|
361 |
+
$this->message[$pos]['mustUnderstand'] = $value;
|
362 |
+
|
363 |
+
// If it's a type declaration, set type.
|
364 |
+
} elseif ($kqn->name == 'type') {
|
365 |
+
$vqn = new QName($value);
|
366 |
+
$this->message[$pos]['type'] = $vqn->name;
|
367 |
+
$this->message[$pos]['type_namespace'] = $this->_getNamespaceForPrefix($vqn->ns);
|
368 |
+
|
369 |
+
// Should do something here with the namespace of specified
|
370 |
+
// type?
|
371 |
+
|
372 |
+
} elseif ($kqn->name == 'arrayType') {
|
373 |
+
$vqn = new QName($value);
|
374 |
+
$this->message[$pos]['type'] = 'Array';
|
375 |
+
if (isset($vqn->arraySize)) {
|
376 |
+
$this->message[$pos]['arraySize'] = $vqn->arraySize;
|
377 |
+
}
|
378 |
+
$this->message[$pos]['arrayType'] = $vqn->name;
|
379 |
+
|
380 |
+
} elseif ($kqn->name == 'offset') {
|
381 |
+
$this->message[$pos]['arrayOffset'] = split(',', substr($value, 1, strlen($value) - 2));
|
382 |
+
|
383 |
+
} elseif ($kqn->name == 'id') {
|
384 |
+
// Save id to reference array.
|
385 |
+
$this->references[$value] = $pos;
|
386 |
+
$this->message[$pos]['id'] = $value;
|
387 |
+
|
388 |
+
} elseif ($kqn->name == 'href') {
|
389 |
+
if ($value[0] == '#') {
|
390 |
+
$ref = substr($value, 1);
|
391 |
+
if (isset($this->references[$ref])) {
|
392 |
+
// cdata, type, inval.
|
393 |
+
$ref_pos = $this->references[$ref];
|
394 |
+
$this->message[$pos]['children'] = &$this->message[$ref_pos]['children'];
|
395 |
+
$this->message[$pos]['cdata'] = &$this->message[$ref_pos]['cdata'];
|
396 |
+
$this->message[$pos]['type'] = &$this->message[$ref_pos]['type'];
|
397 |
+
$this->message[$pos]['arraySize'] = &$this->message[$ref_pos]['arraySize'];
|
398 |
+
$this->message[$pos]['arrayType'] = &$this->message[$ref_pos]['arrayType'];
|
399 |
+
} else {
|
400 |
+
// Reverse reference, store in 'need reference'.
|
401 |
+
if (!isset($this->need_references[$ref])) {
|
402 |
+
$this->need_references[$ref] = array();
|
403 |
+
}
|
404 |
+
$this->need_references[$ref][] = $pos;
|
405 |
+
}
|
406 |
+
} elseif (isset($this->attachments[$value])) {
|
407 |
+
$this->message[$pos]['cdata'] = $this->attachments[$value];
|
408 |
+
}
|
409 |
+
}
|
410 |
+
}
|
411 |
+
// See if namespace is defined in tag.
|
412 |
+
if (isset($attrs['xmlns:' . $qname->ns])) {
|
413 |
+
$namespace = $attrs['xmlns:' . $qname->ns];
|
414 |
+
} elseif ($qname->ns && !$qname->namespace) {
|
415 |
+
$namespace = $this->_getNamespaceForPrefix($qname->ns);
|
416 |
+
} else {
|
417 |
+
// Get namespace.
|
418 |
+
$namespace = $qname->namespace ? $qname->namespace : $this->default_namespace;
|
419 |
+
}
|
420 |
+
$this->message[$pos]['namespace'] = $namespace;
|
421 |
+
$this->default_namespace = $namespace;
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* End element handler used with the XML parser.
|
426 |
+
*/
|
427 |
+
function _endElement($parser, $name)
|
428 |
+
{
|
429 |
+
// Position of current element is equal to the last value left in
|
430 |
+
// depth_array for my depth.
|
431 |
+
$pos = $this->depth_array[$this->depth];
|
432 |
+
|
433 |
+
// Bring depth down a notch.
|
434 |
+
$this->depth--;
|
435 |
+
$qname = new QName($name);
|
436 |
+
|
437 |
+
// Get type if not explicitly declared in an xsi:type attribute.
|
438 |
+
// TODO: check on integrating WSDL validation here.
|
439 |
+
if ($this->message[$pos]['type'] == '') {
|
440 |
+
if (isset($this->message[$pos]['children'])) {
|
441 |
+
/* this is slow, need to look at some faster method
|
442 |
+
$children = explode('|', $this->message[$pos]['children']);
|
443 |
+
if (count($children) > 2 &&
|
444 |
+
$this->message[$children[1]]['name'] == $this->message[$children[2]]['name']) {
|
445 |
+
$this->message[$pos]['type'] = 'Array';
|
446 |
+
} else {
|
447 |
+
$this->message[$pos]['type'] = 'Struct';
|
448 |
+
}*/
|
449 |
+
$this->message[$pos]['type'] = 'Struct';
|
450 |
+
} else {
|
451 |
+
$parent = $this->message[$pos]['parent'];
|
452 |
+
if ($this->message[$parent]['type'] == 'Array' &&
|
453 |
+
isset($this->message[$parent]['arrayType'])) {
|
454 |
+
$this->message[$pos]['type'] = $this->message[$parent]['arrayType'];
|
455 |
+
} else {
|
456 |
+
$this->message[$pos]['type'] = 'string';
|
457 |
+
}
|
458 |
+
}
|
459 |
+
}
|
460 |
+
|
461 |
+
// If tag we are currently closing is the method wrapper.
|
462 |
+
if ($pos == $this->curent_root_struct) {
|
463 |
+
$this->status = 'body';
|
464 |
+
} elseif ($qname->name == 'Body' || $qname->name == 'Header') {
|
465 |
+
$this->status = 'envelope';
|
466 |
+
}
|
467 |
+
|
468 |
+
// Set parent back to my parent.
|
469 |
+
$this->parent = $this->message[$pos]['parent'];
|
470 |
+
|
471 |
+
// Handle any reverse references now.
|
472 |
+
$idref = $this->message[$pos]['id'];
|
473 |
+
|
474 |
+
if ($idref != '' && isset($this->need_references[$idref])) {
|
475 |
+
foreach ($this->need_references[$idref] as $ref_pos) {
|
476 |
+
// XXX is this stuff there already?
|
477 |
+
$this->message[$ref_pos]['children'] = &$this->message[$pos]['children'];
|
478 |
+
$this->message[$ref_pos]['cdata'] = &$this->message[$pos]['cdata'];
|
479 |
+
$this->message[$ref_pos]['type'] = &$this->message[$pos]['type'];
|
480 |
+
$this->message[$ref_pos]['arraySize'] = &$this->message[$pos]['arraySize'];
|
481 |
+
$this->message[$ref_pos]['arrayType'] = &$this->message[$pos]['arrayType'];
|
482 |
+
}
|
483 |
+
}
|
484 |
+
}
|
485 |
+
|
486 |
+
/**
|
487 |
+
* Element content handler used with the XML parser.
|
488 |
+
*/
|
489 |
+
function _characterData($parser, $data)
|
490 |
+
{
|
491 |
+
$pos = $this->depth_array[$this->depth];
|
492 |
+
if (isset($this->message[$pos]['cdata'])) {
|
493 |
+
$this->message[$pos]['cdata'] .= $data;
|
494 |
+
} else {
|
495 |
+
$this->message[$pos]['cdata'] = $data;
|
496 |
+
}
|
497 |
+
}
|
498 |
+
|
499 |
+
}
|
lib/PEAR/SOAP/Transport.php
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file contains the code for an abstract transport layer.
|
4 |
+
*
|
5 |
+
* PHP versions 4 and 5
|
6 |
+
*
|
7 |
+
* LICENSE: This source file is subject to version 2.02 of the PHP license,
|
8 |
+
* that is bundled with this package in the file LICENSE, and is available at
|
9 |
+
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
|
10 |
+
* did not receive a copy of the PHP license and are unable to obtain it
|
11 |
+
* through the world-wide-web, please send a note to license@php.net so we can
|
12 |
+
* mail you a copy immediately.
|
13 |
+
*
|
14 |
+
* @category Web Services
|
15 |
+
* @package SOAP
|
16 |
+
* @author Dietrich Ayala <dietrich@ganx4.com>
|
17 |
+
* @author Shane Caraveo <Shane@Caraveo.com>
|
18 |
+
* @author Jan Schneider <jan@horde.org>
|
19 |
+
* @copyright 2003-2006 The PHP Group
|
20 |
+
* @license http://www.php.net/license/2_02.txt PHP License 2.02
|
21 |
+
* @link http://pear.php.net/package/SOAP
|
22 |
+
*/
|
23 |
+
|
24 |
+
require_once 'SOAP/Base.php';
|
25 |
+
|
26 |
+
/**
|
27 |
+
* SOAP Transport Layer
|
28 |
+
*
|
29 |
+
* This layer can use different protocols dependant on the endpoint url
|
30 |
+
* provided.
|
31 |
+
*
|
32 |
+
* No knowlege of the SOAP protocol is available at this level.
|
33 |
+
* No knowlege of the transport protocols is available at this level.
|
34 |
+
*
|
35 |
+
* @access public
|
36 |
+
* @package SOAP
|
37 |
+
* @author Shane Caraveo <shane@php.net>
|
38 |
+
* @author Jan Schneider <jan@horde.org>
|
39 |
+
*/
|
40 |
+
class SOAP_Transport extends SOAP_Base
|
41 |
+
{
|
42 |
+
/**
|
43 |
+
* Connection endpoint URL.
|
44 |
+
*
|
45 |
+
* @var string
|
46 |
+
*/
|
47 |
+
var $url = '';
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Array containing urlparts.
|
51 |
+
*
|
52 |
+
* @see parse_url()
|
53 |
+
*
|
54 |
+
* @var mixed
|
55 |
+
*/
|
56 |
+
var $urlparts = null;
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Incoming payload.
|
60 |
+
*
|
61 |
+
* @var string
|
62 |
+
*/
|
63 |
+
var $incoming_payload = '';
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Outgoing payload.
|
67 |
+
*
|
68 |
+
* @var string
|
69 |
+
*/
|
70 |
+
var $outgoing_payload = '';
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Request encoding.
|
74 |
+
*
|
75 |
+
* @var string
|
76 |
+
*/
|
77 |
+
var $encoding = SOAP_DEFAULT_ENCODING;
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Response encoding.
|
81 |
+
*
|
82 |
+
* We assume UTF-8 if no encoding is set.
|
83 |
+
*
|
84 |
+
* @var string
|
85 |
+
*/
|
86 |
+
var $result_encoding = 'UTF-8';
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Decoded attachments from the reponse.
|
90 |
+
*
|
91 |
+
* @var array
|
92 |
+
*/
|
93 |
+
var $attachments;
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Request User-Agent.
|
97 |
+
*
|
98 |
+
* @var string
|
99 |
+
*/
|
100 |
+
var $_userAgent = SOAP_LIBRARY_NAME;
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Sends and receives SOAP data.
|
104 |
+
*
|
105 |
+
* @access public
|
106 |
+
* @abstract
|
107 |
+
*
|
108 |
+
* @param string Outgoing SOAP data.
|
109 |
+
* @param array Options.
|
110 |
+
*
|
111 |
+
* @return string|SOAP_Fault
|
112 |
+
*/
|
113 |
+
function send($msg, $options = null)
|
114 |
+
{
|
115 |
+
return $this->_raiseSoapFault('SOAP_Transport::send() not implemented.');
|
116 |
+
}
|
117 |
+
|
118 |
+
public static function getTransport($url, $encoding = SOAP_DEFAULT_ENCODING)
|
119 |
+
{
|
120 |
+
$urlparts = @parse_url($url);
|
121 |
+
|
122 |
+
if (!$urlparts['scheme']) {
|
123 |
+
return SOAP_Base_Object::_raiseSoapFault("Invalid transport URI: $url");
|
124 |
+
}
|
125 |
+
|
126 |
+
if (strcasecmp($urlparts['scheme'], 'mailto') == 0) {
|
127 |
+
$transport_type = 'SMTP';
|
128 |
+
} elseif (strcasecmp($urlparts['scheme'], 'https') == 0) {
|
129 |
+
$transport_type = 'HTTP';
|
130 |
+
} else {
|
131 |
+
/* Handle other transport types */
|
132 |
+
$transport_type = strtoupper($urlparts['scheme']);
|
133 |
+
}
|
134 |
+
$transport_class = "SOAP_Transport_$transport_type";
|
135 |
+
if (!class_exists($transport_class)) {
|
136 |
+
if (!(@include_once('SOAP/Transport/' . basename($transport_type) . '.php'))) {
|
137 |
+
return SOAP_Base_Object::_raiseSoapFault("No Transport for {$urlparts['scheme']}");
|
138 |
+
}
|
139 |
+
}
|
140 |
+
if (!class_exists($transport_class)) {
|
141 |
+
return SOAP_Base_Object::_raiseSoapFault("No Transport class $transport_class");
|
142 |
+
}
|
143 |
+
|
144 |
+
return new $transport_class($url, $encoding);
|
145 |
+
}
|
146 |
+
|
147 |
+
}
|
lib/PEAR/SOAP/Transport/HTTP.php
ADDED
@@ -0,0 +1,624 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file contains the code for a HTTP transport layer.
|
4 |
+
*
|
5 |
+
* PHP versions 4 and 5
|
6 |
+
*
|
7 |
+
* LICENSE: This source file is subject to version 2.02 of the PHP license,
|
8 |
+
* that is bundled with this package in the file LICENSE, and is available at
|
9 |
+
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
|
10 |
+
* did not receive a copy of the PHP license and are unable to obtain it
|
11 |
+
* through the world-wide-web, please send a note to license@php.net so we can
|
12 |
+
* mail you a copy immediately.
|
13 |
+
*
|
14 |
+
* @category Web Services
|
15 |
+
* @package SOAP
|
16 |
+
* @author Shane Caraveo <Shane@Caraveo.com>
|
17 |
+
* @author Jan Schneider <jan@horde.org>
|
18 |
+
* @copyright 2003-2006 The PHP Group
|
19 |
+
* @license http://www.php.net/license/2_02.txt PHP License 2.02
|
20 |
+
* @link http://pear.php.net/package/SOAP
|
21 |
+
*/
|
22 |
+
|
23 |
+
/**
|
24 |
+
* HTTP Transport class
|
25 |
+
*
|
26 |
+
* @package SOAP
|
27 |
+
* @category Web Services
|
28 |
+
*/
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Needed Classes
|
32 |
+
*/
|
33 |
+
require_once 'SOAP/Transport.php';
|
34 |
+
|
35 |
+
/**
|
36 |
+
* HTTP Transport for SOAP
|
37 |
+
*
|
38 |
+
* @access public
|
39 |
+
* @package SOAP
|
40 |
+
* @author Shane Caraveo <shane@php.net>
|
41 |
+
* @author Jan Schneider <jan@horde.org>
|
42 |
+
*/
|
43 |
+
class SOAP_Transport_HTTP extends SOAP_Transport
|
44 |
+
{
|
45 |
+
/**
|
46 |
+
* Basic Auth string.
|
47 |
+
*
|
48 |
+
* @var array
|
49 |
+
*/
|
50 |
+
var $headers = array();
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Cookies.
|
54 |
+
*
|
55 |
+
* @var array
|
56 |
+
*/
|
57 |
+
var $cookies;
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Connection timeout in seconds. 0 = none.
|
61 |
+
*
|
62 |
+
* @var integer
|
63 |
+
*/
|
64 |
+
var $timeout = 4;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* HTTP-Response Content-Type.
|
68 |
+
*/
|
69 |
+
var $result_content_type;
|
70 |
+
|
71 |
+
var $result_headers = array();
|
72 |
+
|
73 |
+
var $result_cookies = array();
|
74 |
+
|
75 |
+
/**
|
76 |
+
* SOAP_Transport_HTTP Constructor
|
77 |
+
*
|
78 |
+
* @access public
|
79 |
+
*
|
80 |
+
* @param string $url HTTP url to SOAP endpoint.
|
81 |
+
* @param string $encoding Encoding to use.
|
82 |
+
*/
|
83 |
+
function SOAP_Transport_HTTP($url, $encoding = SOAP_DEFAULT_ENCODING)
|
84 |
+
{
|
85 |
+
parent::SOAP_Base('HTTP');
|
86 |
+
$this->urlparts = @parse_url($url);
|
87 |
+
$this->url = $url;
|
88 |
+
$this->encoding = $encoding;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Sends and receives SOAP data.
|
93 |
+
*
|
94 |
+
* @access public
|
95 |
+
*
|
96 |
+
* @param string Outgoing SOAP data.
|
97 |
+
* @param array Options.
|
98 |
+
*
|
99 |
+
* @return string|SOAP_Fault
|
100 |
+
*/
|
101 |
+
function send($msg, $options = array())
|
102 |
+
{
|
103 |
+
$this->fault = null;
|
104 |
+
|
105 |
+
if (!$this->_validateUrl()) {
|
106 |
+
return $this->fault;
|
107 |
+
}
|
108 |
+
|
109 |
+
if (isset($options['timeout'])) {
|
110 |
+
$this->timeout = (int)$options['timeout'];
|
111 |
+
}
|
112 |
+
|
113 |
+
if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
|
114 |
+
return $this->_sendHTTP($msg, $options);
|
115 |
+
} elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
|
116 |
+
return $this->_sendHTTPS($msg, $options);
|
117 |
+
}
|
118 |
+
|
119 |
+
return $this->_raiseSoapFault('Invalid url scheme ' . $this->url);
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Sets data for HTTP authentication, creates authorization header.
|
124 |
+
*
|
125 |
+
* @param string $username Username.
|
126 |
+
* @param string $password Response data, minus HTTP headers.
|
127 |
+
*
|
128 |
+
* @access public
|
129 |
+
*/
|
130 |
+
function setCredentials($username, $password)
|
131 |
+
{
|
132 |
+
$this->headers['Authorization'] = 'Basic ' . base64_encode($username . ':' . $password);
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Adds a cookie.
|
137 |
+
*
|
138 |
+
* @access public
|
139 |
+
* @param string $name Cookie name.
|
140 |
+
* @param mixed $value Cookie value.
|
141 |
+
*/
|
142 |
+
function addCookie($name, $value)
|
143 |
+
{
|
144 |
+
$this->cookies[$name] = $value;
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Generates the correct headers for the cookies.
|
149 |
+
*
|
150 |
+
* @access private
|
151 |
+
*
|
152 |
+
* @param array $options Cookie options. If 'nocookies' is set and true
|
153 |
+
* the cookies from the last response are added
|
154 |
+
* automatically. 'cookies' is name-value-hash with
|
155 |
+
* a list of cookies to add.
|
156 |
+
*
|
157 |
+
* @return string The cookie header value.
|
158 |
+
*/
|
159 |
+
function _generateCookieHeader($options)
|
160 |
+
{
|
161 |
+
$this->cookies = array();
|
162 |
+
|
163 |
+
if (empty($options['nocookies']) &&
|
164 |
+
isset($this->result_cookies)) {
|
165 |
+
// Add the cookies we got from the last request.
|
166 |
+
foreach ($this->result_cookies as $cookie) {
|
167 |
+
if ($cookie['domain'] == $this->urlparts['host']) {
|
168 |
+
$this->cookies[$cookie['name']] = $cookie['value'];
|
169 |
+
}
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
// Add cookies the user wants to set.
|
174 |
+
if (isset($options['cookies'])) {
|
175 |
+
foreach ($options['cookies'] as $cookie) {
|
176 |
+
if ($cookie['domain'] == $this->urlparts['host']) {
|
177 |
+
$this->cookies[$cookie['name']] = $cookie['value'];
|
178 |
+
}
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
$cookies = '';
|
183 |
+
foreach ($this->cookies as $name => $value) {
|
184 |
+
if (!empty($cookies)) {
|
185 |
+
$cookies .= '; ';
|
186 |
+
}
|
187 |
+
$cookies .= urlencode($name) . '=' . urlencode($value);
|
188 |
+
}
|
189 |
+
|
190 |
+
return $cookies;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Validate url data passed to constructor.
|
195 |
+
*
|
196 |
+
* @access private
|
197 |
+
* @return boolean
|
198 |
+
*/
|
199 |
+
function _validateUrl()
|
200 |
+
{
|
201 |
+
if (!is_array($this->urlparts) ) {
|
202 |
+
$this->_raiseSoapFault('Unable to parse URL ' . $this->url);
|
203 |
+
return false;
|
204 |
+
}
|
205 |
+
if (!isset($this->urlparts['host'])) {
|
206 |
+
$this->_raiseSoapFault('No host in URL ' . $this->url);
|
207 |
+
return false;
|
208 |
+
}
|
209 |
+
if (!isset($this->urlparts['port'])) {
|
210 |
+
if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
|
211 |
+
$this->urlparts['port'] = 80;
|
212 |
+
} elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
|
213 |
+
$this->urlparts['port'] = 443;
|
214 |
+
}
|
215 |
+
|
216 |
+
}
|
217 |
+
if (isset($this->urlparts['user'])) {
|
218 |
+
$this->setCredentials(urldecode($this->urlparts['user']),
|
219 |
+
urldecode($this->urlparts['pass']));
|
220 |
+
}
|
221 |
+
if (!isset($this->urlparts['path']) || !$this->urlparts['path']) {
|
222 |
+
$this->urlparts['path'] = '/';
|
223 |
+
}
|
224 |
+
|
225 |
+
return true;
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Finds out what the encoding is.
|
230 |
+
* Sets the object property accordingly.
|
231 |
+
*
|
232 |
+
* @access private
|
233 |
+
* @param array $headers Headers.
|
234 |
+
*/
|
235 |
+
function _parseEncoding($headers)
|
236 |
+
{
|
237 |
+
$h = stristr($headers, 'Content-Type');
|
238 |
+
preg_match_all('/^Content-Type:\s*(.*)$/im', $h, $ct, PREG_SET_ORDER);
|
239 |
+
$n = count($ct);
|
240 |
+
$ct = $ct[$n - 1];
|
241 |
+
|
242 |
+
// Strip the string of \r.
|
243 |
+
$this->result_content_type = str_replace("\r", '', $ct[1]);
|
244 |
+
|
245 |
+
if (preg_match('/(.*?)(?:;\s?charset=)(.*)/i',
|
246 |
+
$this->result_content_type,
|
247 |
+
$m)) {
|
248 |
+
$this->result_content_type = $m[1];
|
249 |
+
if (count($m) > 2) {
|
250 |
+
$enc = strtoupper(str_replace('"', '', $m[2]));
|
251 |
+
if (in_array($enc, $this->_encodings)) {
|
252 |
+
$this->result_encoding = $enc;
|
253 |
+
}
|
254 |
+
}
|
255 |
+
}
|
256 |
+
|
257 |
+
// Deal with broken servers that don't set content type on faults.
|
258 |
+
if (!$this->result_content_type) {
|
259 |
+
$this->result_content_type = 'text/xml';
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Parses the headers.
|
265 |
+
*
|
266 |
+
* @param array $headers The headers.
|
267 |
+
*/
|
268 |
+
function _parseHeaders($headers)
|
269 |
+
{
|
270 |
+
/* Largely borrowed from HTTP_Request. */
|
271 |
+
$this->result_headers = array();
|
272 |
+
$headers = split("\r?\n", $headers);
|
273 |
+
foreach ($headers as $value) {
|
274 |
+
if (strpos($value,':') === false) {
|
275 |
+
$this->result_headers[0] = $value;
|
276 |
+
continue;
|
277 |
+
}
|
278 |
+
list($name, $value) = split(':', $value);
|
279 |
+
$headername = strtolower($name);
|
280 |
+
$headervalue = trim($value);
|
281 |
+
$this->result_headers[$headername] = $headervalue;
|
282 |
+
|
283 |
+
if ($headername == 'set-cookie') {
|
284 |
+
// Parse a SetCookie header to fill _cookies array.
|
285 |
+
$cookie = array('expires' => null,
|
286 |
+
'domain' => $this->urlparts['host'],
|
287 |
+
'path' => null,
|
288 |
+
'secure' => false);
|
289 |
+
|
290 |
+
if (!strpos($headervalue, ';')) {
|
291 |
+
// Only a name=value pair.
|
292 |
+
list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $headervalue));
|
293 |
+
$cookie['name'] = urldecode($cookie['name']);
|
294 |
+
$cookie['value'] = urldecode($cookie['value']);
|
295 |
+
|
296 |
+
} else {
|
297 |
+
// Some optional parameters are supplied.
|
298 |
+
$elements = explode(';', $headervalue);
|
299 |
+
list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $elements[0]));
|
300 |
+
$cookie['name'] = urldecode($cookie['name']);
|
301 |
+
$cookie['value'] = urldecode($cookie['value']);
|
302 |
+
|
303 |
+
for ($i = 1; $i < count($elements);$i++) {
|
304 |
+
list($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
|
305 |
+
if ('secure' == $elName) {
|
306 |
+
$cookie['secure'] = true;
|
307 |
+
} elseif ('expires' == $elName) {
|
308 |
+
$cookie['expires'] = str_replace('"', '', $elValue);
|
309 |
+
} elseif ('path' == $elName OR 'domain' == $elName) {
|
310 |
+
$cookie[$elName] = urldecode($elValue);
|
311 |
+
} else {
|
312 |
+
$cookie[$elName] = $elValue;
|
313 |
+
}
|
314 |
+
}
|
315 |
+
}
|
316 |
+
$this->result_cookies[] = $cookie;
|
317 |
+
}
|
318 |
+
}
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Removes HTTP headers from response.
|
323 |
+
*
|
324 |
+
* @return boolean
|
325 |
+
* @access private
|
326 |
+
*/
|
327 |
+
function _parseResponse()
|
328 |
+
{
|
329 |
+
if (!preg_match("/^(.*?)\r?\n\r?\n(.*)/s",
|
330 |
+
$this->incoming_payload,
|
331 |
+
$match)) {
|
332 |
+
$this->_raiseSoapFault('Invalid HTTP Response');
|
333 |
+
return false;
|
334 |
+
}
|
335 |
+
|
336 |
+
$this->response = $match[2];
|
337 |
+
// Find the response error, some servers response with 500 for
|
338 |
+
// SOAP faults.
|
339 |
+
$this->_parseHeaders($match[1]);
|
340 |
+
|
341 |
+
list(, $code, $msg) = sscanf($this->result_headers[0], '%s %s %s');
|
342 |
+
unset($this->result_headers[0]);
|
343 |
+
|
344 |
+
switch($code) {
|
345 |
+
case 100: // Continue
|
346 |
+
$this->incoming_payload = $match[2];
|
347 |
+
return $this->_parseResponse();
|
348 |
+
case 200:
|
349 |
+
case 202:
|
350 |
+
$this->incoming_payload = trim($match[2]);
|
351 |
+
if (!strlen($this->incoming_payload)) {
|
352 |
+
/* Valid one-way message response. */
|
353 |
+
return true;
|
354 |
+
}
|
355 |
+
break;
|
356 |
+
case 400:
|
357 |
+
$this->_raiseSoapFault("HTTP Response $code Bad Request");
|
358 |
+
return false;
|
359 |
+
case 401:
|
360 |
+
$this->_raiseSoapFault("HTTP Response $code Authentication Failed");
|
361 |
+
return false;
|
362 |
+
case 403:
|
363 |
+
$this->_raiseSoapFault("HTTP Response $code Forbidden");
|
364 |
+
return false;
|
365 |
+
case 404:
|
366 |
+
$this->_raiseSoapFault("HTTP Response $code Not Found");
|
367 |
+
return false;
|
368 |
+
case 407:
|
369 |
+
$this->_raiseSoapFault("HTTP Response $code Proxy Authentication Required");
|
370 |
+
return false;
|
371 |
+
case 408:
|
372 |
+
$this->_raiseSoapFault("HTTP Response $code Request Timeout");
|
373 |
+
return false;
|
374 |
+
case 410:
|
375 |
+
$this->_raiseSoapFault("HTTP Response $code Gone");
|
376 |
+
return false;
|
377 |
+
default:
|
378 |
+
if ($code >= 400 && $code < 500) {
|
379 |
+
$this->_raiseSoapFault("HTTP Response $code Not Found, Server message: $msg");
|
380 |
+
return false;
|
381 |
+
}
|
382 |
+
break;
|
383 |
+
}
|
384 |
+
|
385 |
+
$this->_parseEncoding($match[1]);
|
386 |
+
|
387 |
+
if ($this->result_content_type == 'application/dime') {
|
388 |
+
// XXX quick hack insertion of DIME
|
389 |
+
if (PEAR::isError($this->_decodeDIMEMessage($this->response, $this->headers, $this->attachments))) {
|
390 |
+
// _decodeDIMEMessage already raised $this->fault
|
391 |
+
return false;
|
392 |
+
}
|
393 |
+
$this->result_content_type = $this->headers['content-type'];
|
394 |
+
} elseif (stristr($this->result_content_type, 'multipart/related')) {
|
395 |
+
$this->response = $this->incoming_payload;
|
396 |
+
if (PEAR::isError($this->_decodeMimeMessage($this->response, $this->headers, $this->attachments))) {
|
397 |
+
// _decodeMimeMessage already raised $this->fault
|
398 |
+
return false;
|
399 |
+
}
|
400 |
+
} elseif ($this->result_content_type != 'text/xml') {
|
401 |
+
$this->_raiseSoapFault($this->response);
|
402 |
+
return false;
|
403 |
+
}
|
404 |
+
|
405 |
+
// if no content, return false
|
406 |
+
return strlen($this->response) > 0;
|
407 |
+
}
|
408 |
+
|
409 |
+
/**
|
410 |
+
* Creates an HTTP request, including headers, for the outgoing request.
|
411 |
+
*
|
412 |
+
* @access private
|
413 |
+
*
|
414 |
+
* @param string $msg Outgoing SOAP package.
|
415 |
+
* @param array $options Options.
|
416 |
+
*
|
417 |
+
* @return string Outgoing payload.
|
418 |
+
*/
|
419 |
+
function _getRequest($msg, $options)
|
420 |
+
{
|
421 |
+
$this->headers = array();
|
422 |
+
|
423 |
+
$action = isset($options['soapaction']) ? $options['soapaction'] : '';
|
424 |
+
$fullpath = $this->urlparts['path'];
|
425 |
+
if (isset($this->urlparts['query'])) {
|
426 |
+
$fullpath .= '?' . $this->urlparts['query'];
|
427 |
+
}
|
428 |
+
if (isset($this->urlparts['fragment'])) {
|
429 |
+
$fullpath .= '#' . $this->urlparts['fragment'];
|
430 |
+
}
|
431 |
+
|
432 |
+
if (isset($options['proxy_host'])) {
|
433 |
+
$fullpath = 'http://' . $this->urlparts['host'] . ':' .
|
434 |
+
$this->urlparts['port'] . $fullpath;
|
435 |
+
}
|
436 |
+
|
437 |
+
if (isset($options['proxy_user'])) {
|
438 |
+
$this->headers['Proxy-Authorization'] = 'Basic ' .
|
439 |
+
base64_encode($options['proxy_user'] . ':' .
|
440 |
+
$options['proxy_pass']);
|
441 |
+
}
|
442 |
+
|
443 |
+
if (isset($options['user'])) {
|
444 |
+
$this->setCredentials($options['user'], $options['pass']);
|
445 |
+
}
|
446 |
+
|
447 |
+
$this->headers['User-Agent'] = $this->_userAgent;
|
448 |
+
$this->headers['Host'] = $this->urlparts['host'];
|
449 |
+
$this->headers['Content-Type'] = "text/xml; charset=$this->encoding";
|
450 |
+
$this->headers['Content-Length'] = strlen($msg);
|
451 |
+
$this->headers['SOAPAction'] = '"' . $action . '"';
|
452 |
+
$this->headers['Connection'] = 'close';
|
453 |
+
|
454 |
+
if (isset($options['headers'])) {
|
455 |
+
$this->headers = array_merge($this->headers, $options['headers']);
|
456 |
+
}
|
457 |
+
|
458 |
+
$cookies = $this->_generateCookieHeader($options);
|
459 |
+
if ($cookies) {
|
460 |
+
$this->headers['Cookie'] = $cookies;
|
461 |
+
}
|
462 |
+
|
463 |
+
$headers = '';
|
464 |
+
foreach ($this->headers as $k => $v) {
|
465 |
+
$headers .= "$k: $v\r\n";
|
466 |
+
}
|
467 |
+
$this->outgoing_payload = "POST $fullpath HTTP/1.0\r\n" . $headers .
|
468 |
+
"\r\n" . $msg;
|
469 |
+
|
470 |
+
return $this->outgoing_payload;
|
471 |
+
}
|
472 |
+
|
473 |
+
/**
|
474 |
+
* Sends the outgoing HTTP request and reads and parses the response.
|
475 |
+
*
|
476 |
+
* @access private
|
477 |
+
*
|
478 |
+
* @param string $msg Outgoing SOAP package.
|
479 |
+
* @param array $options Options.
|
480 |
+
*
|
481 |
+
* @return string Response data without HTTP headers.
|
482 |
+
*/
|
483 |
+
function _sendHTTP($msg, $options)
|
484 |
+
{
|
485 |
+
$this->incoming_payload = '';
|
486 |
+
$this->_getRequest($msg, $options);
|
487 |
+
$host = $this->urlparts['host'];
|
488 |
+
$port = $this->urlparts['port'];
|
489 |
+
if (isset($options['proxy_host'])) {
|
490 |
+
$host = $options['proxy_host'];
|
491 |
+
$port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
|
492 |
+
}
|
493 |
+
// Send.
|
494 |
+
if ($this->timeout > 0) {
|
495 |
+
$fp = @fsockopen($host, $port, $this->errno, $this->errmsg, $this->timeout);
|
496 |
+
} else {
|
497 |
+
$fp = @fsockopen($host, $port, $this->errno, $this->errmsg);
|
498 |
+
}
|
499 |
+
if (!$fp) {
|
500 |
+
return $this->_raiseSoapFault("Connect Error to $host:$port");
|
501 |
+
}
|
502 |
+
if ($this->timeout > 0) {
|
503 |
+
// some builds of PHP do not support this, silence the warning
|
504 |
+
@socket_set_timeout($fp, $this->timeout);
|
505 |
+
}
|
506 |
+
if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
|
507 |
+
return $this->_raiseSoapFault("Error POSTing Data to $host");
|
508 |
+
}
|
509 |
+
|
510 |
+
// get reponse
|
511 |
+
// XXX time consumer
|
512 |
+
do {
|
513 |
+
$data = fread($fp, 4096);
|
514 |
+
$_tmp_status = socket_get_status($fp);
|
515 |
+
if ($_tmp_status['timed_out']) {
|
516 |
+
return $this->_raiseSoapFault("Timed out read from $host");
|
517 |
+
} else {
|
518 |
+
$this->incoming_payload .= $data;
|
519 |
+
}
|
520 |
+
} while (!$_tmp_status['eof']);
|
521 |
+
|
522 |
+
fclose($fp);
|
523 |
+
|
524 |
+
if (!$this->_parseResponse()) {
|
525 |
+
return $this->fault;
|
526 |
+
}
|
527 |
+
return $this->response;
|
528 |
+
}
|
529 |
+
|
530 |
+
/**
|
531 |
+
* Sends the outgoing HTTPS request and reads and parses the response.
|
532 |
+
*
|
533 |
+
* @access private
|
534 |
+
*
|
535 |
+
* @param string $msg Outgoing SOAP package.
|
536 |
+
* @param array $options Options.
|
537 |
+
*
|
538 |
+
* @return string Response data without HTTP headers.
|
539 |
+
*/
|
540 |
+
function _sendHTTPS($msg, $options)
|
541 |
+
{
|
542 |
+
/* Check if the required curl extension is installed. */
|
543 |
+
if (!extension_loaded('curl')) {
|
544 |
+
return $this->_raiseSoapFault('CURL Extension is required for HTTPS');
|
545 |
+
}
|
546 |
+
|
547 |
+
$ch = curl_init();
|
548 |
+
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
549 |
+
if (isset($options['proxy_host'])) {
|
550 |
+
$port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
|
551 |
+
curl_setopt($ch, CURLOPT_PROXY,
|
552 |
+
$options['proxy_host'] . ':' . $port);
|
553 |
+
}
|
554 |
+
if (isset($options['proxy_user'])) {
|
555 |
+
curl_setopt($ch, CURLOPT_PROXYUSERPWD,
|
556 |
+
$options['proxy_user'] . ':' . $options['proxy_pass']);
|
557 |
+
}
|
558 |
+
|
559 |
+
if (isset($options['user'])) {
|
560 |
+
curl_setopt($ch, CURLOPT_USERPWD,
|
561 |
+
$options['user'] . ':' . $options['pass']);
|
562 |
+
}
|
563 |
+
|
564 |
+
$headers = array();
|
565 |
+
$action = isset($options['soapaction']) ? $options['soapaction'] : '';
|
566 |
+
$headers['Content-Type'] = "text/xml; charset=$this->encoding";
|
567 |
+
$headers['SOAPAction'] = '"' . $action . '"';
|
568 |
+
if (isset($options['headers'])) {
|
569 |
+
$headers = array_merge($headers, $options['headers']);
|
570 |
+
}
|
571 |
+
foreach ($headers as $header => $value) {
|
572 |
+
$headers[$header] = $header . ': ' . $value;
|
573 |
+
}
|
574 |
+
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
575 |
+
curl_setopt($ch, CURLOPT_USERAGENT, $this->_userAgent);
|
576 |
+
|
577 |
+
if ($this->timeout) {
|
578 |
+
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
|
579 |
+
}
|
580 |
+
|
581 |
+
curl_setopt($ch, CURLOPT_POSTFIELDS, $msg);
|
582 |
+
curl_setopt($ch, CURLOPT_URL, $this->url);
|
583 |
+
curl_setopt($ch, CURLOPT_POST, 1);
|
584 |
+
curl_setopt($ch, CURLOPT_FAILONERROR, 0);
|
585 |
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
586 |
+
curl_setopt($ch, CURLOPT_HEADER, 1);
|
587 |
+
if (defined('CURLOPT_HTTP_VERSION')) {
|
588 |
+
curl_setopt($ch, CURLOPT_HTTP_VERSION, 1);
|
589 |
+
}
|
590 |
+
if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
|
591 |
+
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
592 |
+
}
|
593 |
+
$cookies = $this->_generateCookieHeader($options);
|
594 |
+
if ($cookies) {
|
595 |
+
curl_setopt($ch, CURLOPT_COOKIE, $cookies);
|
596 |
+
}
|
597 |
+
|
598 |
+
if (isset($options['curl'])) {
|
599 |
+
foreach ($options['curl'] as $key => $val) {
|
600 |
+
curl_setopt($ch, $key, $val);
|
601 |
+
}
|
602 |
+
}
|
603 |
+
|
604 |
+
// Save the outgoing XML. This doesn't quite match _sendHTTP as CURL
|
605 |
+
// generates the headers, but having the XML is usually the most
|
606 |
+
// important part for tracing/debugging.
|
607 |
+
$this->outgoing_payload = $msg;
|
608 |
+
|
609 |
+
$this->incoming_payload = curl_exec($ch);
|
610 |
+
if (!$this->incoming_payload) {
|
611 |
+
$m = 'curl_exec error ' . curl_errno($ch) . ' ' . curl_error($ch);
|
612 |
+
curl_close($ch);
|
613 |
+
return $this->_raiseSoapFault($m);
|
614 |
+
}
|
615 |
+
curl_close($ch);
|
616 |
+
|
617 |
+
if (!$this->_parseResponse()) {
|
618 |
+
return $this->fault;
|
619 |
+
}
|
620 |
+
|
621 |
+
return $this->response;
|
622 |
+
}
|
623 |
+
|
624 |
+
}
|
lib/PEAR/SOAP/Value.php
ADDED
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file contains the code for converting values between SOAP and PHP.
|
4 |
+
*
|
5 |
+
* PHP versions 4 and 5
|
6 |
+
*
|
7 |
+
* LICENSE: This source file is subject to version 2.02 of the PHP license,
|
8 |
+
* that is bundled with this package in the file LICENSE, and is available at
|
9 |
+
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
|
10 |
+
* did not receive a copy of the PHP license and are unable to obtain it
|
11 |
+
* through the world-wide-web, please send a note to license@php.net so we can
|
12 |
+
* mail you a copy immediately.
|
13 |
+
*
|
14 |
+
* @category Web Services
|
15 |
+
* @package SOAP
|
16 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
17 |
+
* @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
|
18 |
+
* @author Chuck Hagenbuch <chuck@horde.org> Maintenance
|
19 |
+
* @author Jan Schneider <jan@horde.org> Maintenance
|
20 |
+
* @copyright 2003-2007 The PHP Group
|
21 |
+
* @license http://www.php.net/license/2_02.txt PHP License 2.02
|
22 |
+
* @link http://pear.php.net/package/SOAP
|
23 |
+
*/
|
24 |
+
|
25 |
+
require_once 'SOAP/Base.php';
|
26 |
+
|
27 |
+
/**
|
28 |
+
* SOAP::Value
|
29 |
+
*
|
30 |
+
* This class converts values between PHP and SOAP.
|
31 |
+
*
|
32 |
+
* Originally based on SOAPx4 by Dietrich Ayala
|
33 |
+
* http://dietrich.ganx4.com/soapx4
|
34 |
+
*
|
35 |
+
* @access public
|
36 |
+
* @package SOAP
|
37 |
+
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
38 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
39 |
+
*/
|
40 |
+
class SOAP_Value
|
41 |
+
{
|
42 |
+
/**
|
43 |
+
* The actual value.
|
44 |
+
*
|
45 |
+
* @var mixed
|
46 |
+
*/
|
47 |
+
var $value = null;
|
48 |
+
|
49 |
+
/**
|
50 |
+
* QName instance representing the value name.
|
51 |
+
*
|
52 |
+
* @var QName
|
53 |
+
*/
|
54 |
+
var $nqn;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* The value name, without namespace information.
|
58 |
+
*
|
59 |
+
* @var string
|
60 |
+
*/
|
61 |
+
var $name = '';
|
62 |
+
|
63 |
+
/**
|
64 |
+
* The namespace of the value name.
|
65 |
+
*
|
66 |
+
* @var string
|
67 |
+
*/
|
68 |
+
var $namespace = '';
|
69 |
+
|
70 |
+
/**
|
71 |
+
* QName instance representing the value type.
|
72 |
+
*
|
73 |
+
* @var QName
|
74 |
+
*/
|
75 |
+
var $tqn;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* The value type, without namespace information.
|
79 |
+
*
|
80 |
+
* @var string
|
81 |
+
*/
|
82 |
+
var $type = '';
|
83 |
+
|
84 |
+
/**
|
85 |
+
* The namespace of the value type.
|
86 |
+
*
|
87 |
+
* @var string
|
88 |
+
*/
|
89 |
+
var $type_namespace = '';
|
90 |
+
|
91 |
+
/**
|
92 |
+
* The type of the array elements, if this value is an array.
|
93 |
+
*
|
94 |
+
* @var string
|
95 |
+
*/
|
96 |
+
var $arrayType = '';
|
97 |
+
|
98 |
+
/**
|
99 |
+
* A hash of additional attributes.
|
100 |
+
*
|
101 |
+
* @see SOAP_Value()
|
102 |
+
* @var array
|
103 |
+
*/
|
104 |
+
var $attributes = array();
|
105 |
+
|
106 |
+
/**
|
107 |
+
* List of encoding and serialization options.
|
108 |
+
*
|
109 |
+
* @see SOAP_Value()
|
110 |
+
* @var array
|
111 |
+
*/
|
112 |
+
var $options = array();
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Constructor.
|
116 |
+
*
|
117 |
+
* @param string $name Name of the SOAP value {namespace}name.
|
118 |
+
* @param mixed $type SOAP value {namespace}type. Determined
|
119 |
+
* automatically if not set.
|
120 |
+
* @param mixed $value Value to set.
|
121 |
+
* @param array $attributes A has of additional XML attributes to be
|
122 |
+
* added to the serialized value.
|
123 |
+
* @param array $options A list of encoding and serialization options:
|
124 |
+
* - 'attachment': array with information about
|
125 |
+
* the attachment
|
126 |
+
* - 'soap_encoding': defines encoding for SOAP
|
127 |
+
* message part of a MIME encoded SOAP request
|
128 |
+
* (default: base64)
|
129 |
+
* - 'keep_arrays_flat': use the tag name
|
130 |
+
* multiple times for each element when
|
131 |
+
* passing in an array in literal mode
|
132 |
+
* - 'no_type_prefix': supress adding of the
|
133 |
+
* namespace prefix
|
134 |
+
*/
|
135 |
+
function SOAP_Value($name = '', $type = false, $value = null,
|
136 |
+
$attributes = array(), $options = array())
|
137 |
+
{
|
138 |
+
$this->nqn = new QName($name);
|
139 |
+
$this->name = $this->nqn->name;
|
140 |
+
$this->namespace = $this->nqn->namespace;
|
141 |
+
if ($type) {
|
142 |
+
$this->tqn = new QName($type);
|
143 |
+
$this->type = $this->tqn->name;
|
144 |
+
$this->type_namespace = $this->tqn->namespace;
|
145 |
+
}
|
146 |
+
$this->value = $value;
|
147 |
+
$this->attributes = $attributes;
|
148 |
+
$this->options = $options;
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Serializes this value.
|
153 |
+
*
|
154 |
+
* @param SOAP_Base $serializer A SOAP_Base instance or subclass to
|
155 |
+
* serialize with.
|
156 |
+
*
|
157 |
+
* @return string XML representation of $this.
|
158 |
+
*/
|
159 |
+
function serialize(&$serializer)
|
160 |
+
{
|
161 |
+
return $serializer->_serializeValue($this->value,
|
162 |
+
$this->nqn,
|
163 |
+
$this->tqn,
|
164 |
+
$this->options,
|
165 |
+
$this->attributes,
|
166 |
+
$this->arrayType);
|
167 |
+
}
|
168 |
+
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* This class converts values between PHP and SOAP. It is a simple wrapper
|
173 |
+
* around SOAP_Value, adding support for SOAP actor and mustunderstand
|
174 |
+
* parameters.
|
175 |
+
*
|
176 |
+
* Originally based on SOAPx4 by Dietrich Ayala
|
177 |
+
* http://dietrich.ganx4.com/soapx4
|
178 |
+
*
|
179 |
+
* @access public
|
180 |
+
* @package SOAP
|
181 |
+
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
182 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
183 |
+
*/
|
184 |
+
class SOAP_Header extends SOAP_Value
|
185 |
+
{
|
186 |
+
/**
|
187 |
+
* Constructor
|
188 |
+
*
|
189 |
+
* @param string $name Name of the SOAP value {namespace}name.
|
190 |
+
* @param mixed $type SOAP value {namespace}type. Determined
|
191 |
+
* automatically if not set.
|
192 |
+
* @param mixed $value Value to set
|
193 |
+
* @param integer $mustunderstand Zero or one.
|
194 |
+
* @param mixed $attributes Attributes.
|
195 |
+
*/
|
196 |
+
function SOAP_Header($name = '', $type, $value, $mustunderstand = 0,
|
197 |
+
$attributes = array())
|
198 |
+
{
|
199 |
+
if (!is_array($attributes)) {
|
200 |
+
$actor = $attributes;
|
201 |
+
$attributes = array();
|
202 |
+
}
|
203 |
+
|
204 |
+
parent::SOAP_Value($name, $type, $value, $attributes);
|
205 |
+
|
206 |
+
if (isset($actor)) {
|
207 |
+
$this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'] = $actor;
|
208 |
+
} elseif (!isset($this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'])) {
|
209 |
+
$this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'] = 'http://schemas.xmlsoap.org/soap/actor/next';
|
210 |
+
}
|
211 |
+
$this->attributes[SOAP_BASE::SOAPENVPrefix().':mustUnderstand'] = (int)$mustunderstand;
|
212 |
+
}
|
213 |
+
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* This class handles MIME attachements per W3C's Note on Soap Attachements at
|
218 |
+
* http://www.w3.org/TR/SOAP-attachments
|
219 |
+
*
|
220 |
+
* @access public
|
221 |
+
* @package SOAP
|
222 |
+
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
223 |
+
*/
|
224 |
+
class SOAP_Attachment extends SOAP_Value
|
225 |
+
{
|
226 |
+
/**
|
227 |
+
* Constructor.
|
228 |
+
*
|
229 |
+
* @param string $name Name of the SOAP value <value_name>
|
230 |
+
* @param string $type The attachment's MIME type.
|
231 |
+
* @param string $filename The attachment's file name. Ignored if $file
|
232 |
+
* is provide.
|
233 |
+
* @param string $file The attachment data.
|
234 |
+
* @param array $attributes Attributes.
|
235 |
+
*/
|
236 |
+
function SOAP_Attachment($name = '', $type = 'application/octet-stream',
|
237 |
+
$filename, $file = null, $attributes = null)
|
238 |
+
{
|
239 |
+
parent::SOAP_Value($name, null, null);
|
240 |
+
|
241 |
+
$filedata = $file === null ? $this->_file2str($filename) : $file;
|
242 |
+
$filename = basename($filename);
|
243 |
+
if (PEAR::isError($filedata)) {
|
244 |
+
$this->options['attachment'] = $filedata;
|
245 |
+
return;
|
246 |
+
}
|
247 |
+
|
248 |
+
$cid = md5(uniqid(time()));
|
249 |
+
|
250 |
+
$this->attributes = $attributes;
|
251 |
+
$this->attributes['href'] = 'cid:' . $cid;
|
252 |
+
|
253 |
+
$this->options['attachment'] = array('body' => $filedata,
|
254 |
+
'disposition' => $filename,
|
255 |
+
'content_type' => $type,
|
256 |
+
'encoding' => 'base64',
|
257 |
+
'cid' => $cid);
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Returns the contents of the given file name as string.
|
262 |
+
*
|
263 |
+
* @access private
|
264 |
+
*
|
265 |
+
* @param string $file_name The file location.
|
266 |
+
*
|
267 |
+
* @return string The file data or a PEAR_Error.
|
268 |
+
*/
|
269 |
+
function _file2str($file_name)
|
270 |
+
{
|
271 |
+
if (!is_readable($file_name)) {
|
272 |
+
return PEAR::raiseError('File is not readable: ' . $file_name);
|
273 |
+
}
|
274 |
+
|
275 |
+
if (function_exists('file_get_contents')) {
|
276 |
+
return file_get_contents($file_name);
|
277 |
+
}
|
278 |
+
|
279 |
+
if (!$fd = fopen($file_name, 'rb')) {
|
280 |
+
return PEAR::raiseError('Could not open ' . $file_name);
|
281 |
+
}
|
282 |
+
$cont = fread($fd, filesize($file_name));
|
283 |
+
fclose($fd);
|
284 |
+
|
285 |
+
return $cont;
|
286 |
+
}
|
287 |
+
|
288 |
+
}
|
lib/PEAR/SOAP/WSDL.php
ADDED
@@ -0,0 +1,2294 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file contains the code for dealing with WSDL access and services.
|
4 |
+
*
|
5 |
+
* PHP versions 4 and 5
|
6 |
+
*
|
7 |
+
* LICENSE: This source file is subject to version 2.02 of the PHP license,
|
8 |
+
* that is bundled with this package in the file LICENSE, and is available at
|
9 |
+
* through the world-wide-web at http://www.php.net/license/2_02.txt. If you
|
10 |
+
* did not receive a copy of the PHP license and are unable to obtain it
|
11 |
+
* through the world-wide-web, please send a note to license@php.net so we can
|
12 |
+
* mail you a copy immediately.
|
13 |
+
*
|
14 |
+
* @category Web Services
|
15 |
+
* @package SOAP
|
16 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
17 |
+
* @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
|
18 |
+
* @author Chuck Hagenbuch <chuck@horde.org> Maintenance
|
19 |
+
* @author Jan Schneider <jan@horde.org> Maintenance
|
20 |
+
* @copyright 2003-2005 The PHP Group
|
21 |
+
* @license http://www.php.net/license/2_02.txt PHP License 2.02
|
22 |
+
* @link http://pear.php.net/package/SOAP
|
23 |
+
*/
|
24 |
+
|
25 |
+
require_once 'SOAP/Base.php';
|
26 |
+
require_once 'SOAP/Fault.php';
|
27 |
+
require_once 'HTTP/Request.php';
|
28 |
+
|
29 |
+
define('WSDL_CACHE_MAX_AGE', 43200);
|
30 |
+
|
31 |
+
/**
|
32 |
+
* This class parses WSDL files, and can be used by SOAP::Client to properly
|
33 |
+
* register soap values for services.
|
34 |
+
*
|
35 |
+
* Originally based on SOAPx4 by Dietrich Ayala
|
36 |
+
* http://dietrich.ganx4.com/soapx4
|
37 |
+
*
|
38 |
+
* @todo
|
39 |
+
* - refactor namespace handling ($namespace/$ns)
|
40 |
+
* - implement IDL type syntax declaration so we can generate WSDL
|
41 |
+
*
|
42 |
+
* @access public
|
43 |
+
* @package SOAP
|
44 |
+
* @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
|
45 |
+
* @author Dietrich Ayala <dietrich@ganx4.com> Original Author
|
46 |
+
*/
|
47 |
+
class SOAP_WSDL extends SOAP_Base
|
48 |
+
{
|
49 |
+
var $tns = null;
|
50 |
+
var $definition = array();
|
51 |
+
var $namespaces = array();
|
52 |
+
var $ns = array();
|
53 |
+
var $xsd = SOAP_XML_SCHEMA_VERSION;
|
54 |
+
var $complexTypes = array();
|
55 |
+
var $elements = array();
|
56 |
+
var $messages = array();
|
57 |
+
var $portTypes = array();
|
58 |
+
var $bindings = array();
|
59 |
+
var $imports = array();
|
60 |
+
var $services = array();
|
61 |
+
var $service = '';
|
62 |
+
|
63 |
+
/**
|
64 |
+
* URL to WSDL file.
|
65 |
+
*
|
66 |
+
* @var string
|
67 |
+
*/
|
68 |
+
var $uri;
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Parse documentation in the WSDL?
|
72 |
+
*
|
73 |
+
* @var boolean
|
74 |
+
*/
|
75 |
+
var $docs;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Proxy parameters.
|
79 |
+
*
|
80 |
+
* @var array
|
81 |
+
*/
|
82 |
+
var $proxy;
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Enable tracing in the generated proxy class?
|
86 |
+
*
|
87 |
+
* @var boolean
|
88 |
+
*/
|
89 |
+
var $trace = false;
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Use WSDL cache?
|
93 |
+
*
|
94 |
+
* @var boolean
|
95 |
+
*/
|
96 |
+
var $cacheUse;
|
97 |
+
|
98 |
+
/**
|
99 |
+
* WSDL cache directory.
|
100 |
+
*
|
101 |
+
* @var string
|
102 |
+
*/
|
103 |
+
var $cacheDir;
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Cache maximum lifetime (in seconds).
|
107 |
+
*
|
108 |
+
* @var integer
|
109 |
+
*/
|
110 |
+
var $cacheMaxAge;
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Class to use for WSDL parsing. Can be overridden for special cases,
|
114 |
+
* subclasses, etc.
|
115 |
+
*
|
116 |
+
* @var string
|
117 |
+
*/
|
118 |
+
var $wsdlParserClass = 'SOAP_WSDL_Parser';
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Reserved PHP keywords.
|
122 |
+
*
|
123 |
+
* @link http://www.php.net/manual/en/reserved.php
|
124 |
+
*
|
125 |
+
* @var array
|
126 |
+
*/
|
127 |
+
var $_reserved = array('abstract', 'and', 'array', 'as', 'break', 'case',
|
128 |
+
'catch', 'cfunction', 'class', 'clone', 'const',
|
129 |
+
'continue', 'declare', 'default', 'die', 'do',
|
130 |
+
'echo', 'else', 'elseif', 'empty', 'enddeclare',
|
131 |
+
'endfor', 'endforeach', 'endif', 'endswitch',
|
132 |
+
'endwhile', 'eval', 'exception', 'exit', 'extends',
|
133 |
+
'final', 'for', 'foreach', 'function', 'global',
|
134 |
+
'if', 'implements', 'include', 'include_once',
|
135 |
+
'interface', 'isset', 'list', 'new', 'old_function',
|
136 |
+
'or', 'php_user_filter', 'print', 'private',
|
137 |
+
'protected', 'public', 'require', 'require_once',
|
138 |
+
'return', 'static', 'switch', 'this', 'throw',
|
139 |
+
'try', 'unset', 'use', 'var', 'while', 'xor');
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Regular expressions for invalid PHP labels.
|
143 |
+
*
|
144 |
+
* @link http://www.php.net/manual/en/language.variables.php.
|
145 |
+
*
|
146 |
+
* @var string
|
147 |
+
*/
|
148 |
+
var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/');
|
149 |
+
|
150 |
+
/**
|
151 |
+
* SOAP_WSDL constructor.
|
152 |
+
*
|
153 |
+
* @param string $wsdl_uri URL to WSDL file.
|
154 |
+
* @param array $proxy Options for HTTP_Request class
|
155 |
+
* @see HTTP_Request.
|
156 |
+
* @param boolean|string $cacheUse Use WSDL caching? The cache directory
|
157 |
+
* if a string.
|
158 |
+
* @param integer $cacheMaxAge Cache maximum lifetime (in seconds).
|
159 |
+
* @param boolean $docs Parse documentation in the WSDL?
|
160 |
+
*
|
161 |
+
* @access public
|
162 |
+
*/
|
163 |
+
function SOAP_WSDL($wsdl_uri = false,
|
164 |
+
$proxy = array(),
|
165 |
+
$cacheUse = false,
|
166 |
+
$cacheMaxAge = WSDL_CACHE_MAX_AGE,
|
167 |
+
$docs = false)
|
168 |
+
{
|
169 |
+
parent::SOAP_Base('WSDL');
|
170 |
+
$this->uri = $wsdl_uri;
|
171 |
+
$this->proxy = $proxy;
|
172 |
+
$this->cacheUse = !empty($cacheUse);
|
173 |
+
$this->cacheMaxAge = $cacheMaxAge;
|
174 |
+
$this->docs = $docs;
|
175 |
+
if (is_string($cacheUse)) {
|
176 |
+
$this->cacheDir = $cacheUse;
|
177 |
+
}
|
178 |
+
|
179 |
+
if ($wsdl_uri) {
|
180 |
+
if (!PEAR::isError($this->parseURL($wsdl_uri))) {
|
181 |
+
reset($this->services);
|
182 |
+
$this->service = key($this->services);
|
183 |
+
}
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* @deprecated Use setService().
|
189 |
+
*/
|
190 |
+
function set_service($service)
|
191 |
+
{
|
192 |
+
$this->setService($service);
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Sets the service currently to be used.
|
197 |
+
*
|
198 |
+
* @param string $service An (existing) service name.
|
199 |
+
*/
|
200 |
+
function setService($service)
|
201 |
+
{
|
202 |
+
if (array_key_exists($service, $this->services)) {
|
203 |
+
$this->service = $service;
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Fills the WSDL array tree with data from a WSDL file.
|
209 |
+
*
|
210 |
+
* @param string $wsdl_uri URL to WSDL file.
|
211 |
+
*/
|
212 |
+
function parseURL($wsdl_uri)
|
213 |
+
{
|
214 |
+
$parser = new $this->wsdlParserClass($wsdl_uri, $this, $this->docs);
|
215 |
+
|
216 |
+
if ($parser->fault) {
|
217 |
+
$this->_raiseSoapFault($parser->fault);
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Fills the WSDL array tree with data from one or more PHP class objects.
|
223 |
+
*
|
224 |
+
* @param mixed $wsdl_obj An object or array of objects to add to
|
225 |
+
* the internal WSDL tree.
|
226 |
+
* @param string $targetNamespace The target namespace of schema types
|
227 |
+
* etc.
|
228 |
+
* @param string $service_name Name of the WSDL service.
|
229 |
+
* @param string $service_desc Optional description of the WSDL
|
230 |
+
* service.
|
231 |
+
*/
|
232 |
+
function parseObject($wsdl_obj, $targetNamespace, $service_name,
|
233 |
+
$service_desc = '')
|
234 |
+
{
|
235 |
+
$parser = new SOAP_WSDL_ObjectParser($wsdl_obj, $this,
|
236 |
+
$targetNamespace, $service_name,
|
237 |
+
$service_desc);
|
238 |
+
|
239 |
+
if ($parser->fault) {
|
240 |
+
$this->_raiseSoapFault($parser->fault);
|
241 |
+
}
|
242 |
+
}
|
243 |
+
|
244 |
+
function getEndpoint($portName)
|
245 |
+
{
|
246 |
+
if ($this->_isfault()) {
|
247 |
+
return $this->_getfault();
|
248 |
+
}
|
249 |
+
|
250 |
+
return (isset($this->services[$this->service]['ports'][$portName]['address']['location']))
|
251 |
+
? $this->services[$this->service]['ports'][$portName]['address']['location']
|
252 |
+
: $this->_raiseSoapFault("No endpoint for port for $portName", $this->uri);
|
253 |
+
}
|
254 |
+
|
255 |
+
function _getPortName($operation, $service)
|
256 |
+
{
|
257 |
+
if (isset($this->services[$service]['ports'])) {
|
258 |
+
$ports = $this->services[$service]['ports'];
|
259 |
+
foreach ($ports as $port => $portAttrs) {
|
260 |
+
$type = $ports[$port]['type'];
|
261 |
+
if ($type == 'soap' &&
|
262 |
+
isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
|
263 |
+
return $port;
|
264 |
+
}
|
265 |
+
}
|
266 |
+
}
|
267 |
+
return null;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Finds the name of the first port that contains an operation of name
|
272 |
+
* $operation. Always returns a SOAP portName.
|
273 |
+
*/
|
274 |
+
function getPortName($operation, $service = null)
|
275 |
+
{
|
276 |
+
if ($this->_isfault()) {
|
277 |
+
return $this->_getfault();
|
278 |
+
}
|
279 |
+
|
280 |
+
if (!$service) {
|
281 |
+
$service = $this->service;
|
282 |
+
}
|
283 |
+
if (isset($this->services[$service]['ports'])) {
|
284 |
+
if ($portName = $this->_getPortName($operation, $service)) {
|
285 |
+
return $portName;
|
286 |
+
}
|
287 |
+
}
|
288 |
+
// Try any service in the WSDL.
|
289 |
+
foreach ($this->services as $serviceName => $service) {
|
290 |
+
if (isset($this->services[$serviceName]['ports'])) {
|
291 |
+
if ($portName = $this->_getPortName($operation, $serviceName)) {
|
292 |
+
$this->service = $serviceName;
|
293 |
+
return $portName;
|
294 |
+
}
|
295 |
+
}
|
296 |
+
}
|
297 |
+
return $this->_raiseSoapFault("No operation $operation in WSDL.", $this->uri);
|
298 |
+
}
|
299 |
+
|
300 |
+
function getOperationData($portName, $operation)
|
301 |
+
{
|
302 |
+
if ($this->_isfault()) {
|
303 |
+
return $this->_getfault();
|
304 |
+
}
|
305 |
+
|
306 |
+
if (!isset($this->services[$this->service]['ports'][$portName]['binding']) ||
|
307 |
+
!($binding = $this->services[$this->service]['ports'][$portName]['binding'])) {
|
308 |
+
return $this->_raiseSoapFault("No binding for port $portName in WSDL.", $this->uri);
|
309 |
+
}
|
310 |
+
|
311 |
+
// Get operation data from binding.
|
312 |
+
if (is_array($this->bindings[$binding]['operations'][$operation])) {
|
313 |
+
$opData = $this->bindings[$binding]['operations'][$operation];
|
314 |
+
}
|
315 |
+
// Get operation data from porttype.
|
316 |
+
$portType = $this->bindings[$binding]['type'];
|
317 |
+
if (!$portType) {
|
318 |
+
return $this->_raiseSoapFault("No port type for binding $binding in WSDL.", $this->uri);
|
319 |
+
}
|
320 |
+
if (is_array($type = $this->portTypes[$portType][$operation])) {
|
321 |
+
if (isset($type['parameterOrder'])) {
|
322 |
+
$opData['parameterOrder'] = $type['parameterOrder'];
|
323 |
+
}
|
324 |
+
$opData['input'] = array_merge($opData['input'], $type['input']);
|
325 |
+
$opData['output'] = array_merge($opData['output'], $type['output']);
|
326 |
+
}
|
327 |
+
if (!$opData)
|
328 |
+
return $this->_raiseSoapFault("No operation $operation for port $portName in WSDL.", $this->uri);
|
329 |
+
$opData['parameters'] = false;
|
330 |
+
if (isset($this->bindings[$binding]['operations'][$operation]['input']['namespace']))
|
331 |
+
$opData['namespace'] = $this->bindings[$binding]['operations'][$operation]['input']['namespace'];
|
332 |
+
// Message data from messages.
|
333 |
+
$inputMsg = $opData['input']['message'];
|
334 |
+
if (is_array($this->messages[$inputMsg])) {
|
335 |
+
foreach ($this->messages[$inputMsg] as $pname => $pattrs) {
|
336 |
+
if ($opData['style'] == 'document' &&
|
337 |
+
$opData['input']['use'] == 'literal' &&
|
338 |
+
$pname == 'parameters') {
|
339 |
+
$opData['parameters'] = true;
|
340 |
+
$opData['namespace'] = $this->namespaces[$pattrs['namespace']];
|
341 |
+
$el = $this->elements[$pattrs['namespace']][$pattrs['type']];
|
342 |
+
if (isset($el['elements'])) {
|
343 |
+
foreach ($el['elements'] as $elname => $elattrs) {
|
344 |
+
$opData['input']['parts'][$elname] = $elattrs;
|
345 |
+
}
|
346 |
+
}
|
347 |
+
} else {
|
348 |
+
$opData['input']['parts'][$pname] = $pattrs;
|
349 |
+
}
|
350 |
+
}
|
351 |
+
}
|
352 |
+
$outputMsg = $opData['output']['message'];
|
353 |
+
if (is_array($this->messages[$outputMsg])) {
|
354 |
+
foreach ($this->messages[$outputMsg] as $pname => $pattrs) {
|
355 |
+
if ($opData['style'] == 'document' &&
|
356 |
+
$opData['output']['use'] == 'literal' &&
|
357 |
+
$pname == 'parameters') {
|
358 |
+
|
359 |
+
$el = $this->elements[$pattrs['namespace']][$pattrs['type']];
|
360 |
+
if (isset($el['elements'])) {
|
361 |
+
foreach ($el['elements'] as $elname => $elattrs) {
|
362 |
+
$opData['output']['parts'][$elname] = $elattrs;
|
363 |
+
}
|
364 |
+
}
|
365 |
+
} else {
|
366 |
+
$opData['output']['parts'][$pname] = $pattrs;
|
367 |
+
}
|
368 |
+
}
|
369 |
+
}
|
370 |
+
return $opData;
|
371 |
+
}
|
372 |
+
|
373 |
+
function matchMethod(&$operation)
|
374 |
+
{
|
375 |
+
if ($this->_isfault()) {
|
376 |
+
return $this->_getfault();
|
377 |
+
}
|
378 |
+
|
379 |
+
// Overloading lowercases function names :(
|
380 |
+
foreach ($this->services[$this->service]['ports'] as $portAttrs) {
|
381 |
+
foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) {
|
382 |
+
if (strcasecmp($op, $operation) == 0) {
|
383 |
+
$operation = $op;
|
384 |
+
}
|
385 |
+
}
|
386 |
+
}
|
387 |
+
}
|
388 |
+
|
389 |
+
/**
|
390 |
+
* Given a datatype, what function handles the processing?
|
391 |
+
*
|
392 |
+
* This is used for doc/literal requests where we receive a datatype, and
|
393 |
+
* we need to pass it to a method in out server class.
|
394 |
+
*
|
395 |
+
* @param string $datatype
|
396 |
+
* @param string $namespace
|
397 |
+
* @return string
|
398 |
+
* @access public
|
399 |
+
*/
|
400 |
+
function getDataHandler($datatype, $namespace)
|
401 |
+
{
|
402 |
+
// See if we have an element by this name.
|
403 |
+
if (isset($this->namespaces[$namespace])) {
|
404 |
+
$namespace = $this->namespaces[$namespace];
|
405 |
+
}
|
406 |
+
|
407 |
+
if (!isset($this->ns[$namespace])) {
|
408 |
+
return null;
|
409 |
+
}
|
410 |
+
|
411 |
+
$nsp = $this->ns[$namespace];
|
412 |
+
//if (!isset($this->elements[$nsp]))
|
413 |
+
// $nsp = $this->namespaces[$nsp];
|
414 |
+
if (!isset($this->elements[$nsp][$datatype])) {
|
415 |
+
return null;
|
416 |
+
}
|
417 |
+
|
418 |
+
$checkmessages = array();
|
419 |
+
// Find what messages use this datatype.
|
420 |
+
foreach ($this->messages as $messagename => $message) {
|
421 |
+
foreach ($message as $part) {
|
422 |
+
if ($part['type'] == $datatype) {
|
423 |
+
$checkmessages[] = $messagename;
|
424 |
+
break;
|
425 |
+
}
|
426 |
+
}
|
427 |
+
}
|
428 |
+
// Find the operation that uses this message.
|
429 |
+
foreach($this->portTypes as $porttype) {
|
430 |
+
foreach ($porttype as $opname => $opinfo) {
|
431 |
+
foreach ($checkmessages as $messagename) {
|
432 |
+
if ($opinfo['input']['message'] == $messagename) {
|
433 |
+
return $opname;
|
434 |
+
}
|
435 |
+
}
|
436 |
+
}
|
437 |
+
}
|
438 |
+
|
439 |
+
return null;
|
440 |
+
}
|
441 |
+
|
442 |
+
function getSoapAction($portName, $operation)
|
443 |
+
{
|
444 |
+
if ($this->_isfault()) {
|
445 |
+
return $this->_getfault();
|
446 |
+
}
|
447 |
+
|
448 |
+
if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) {
|
449 |
+
return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'];
|
450 |
+
}
|
451 |
+
|
452 |
+
return false;
|
453 |
+
}
|
454 |
+
|
455 |
+
function getNamespace($portName, $operation)
|
456 |
+
{
|
457 |
+
if ($this->_isfault()) {
|
458 |
+
return $this->_getfault();
|
459 |
+
}
|
460 |
+
|
461 |
+
if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) {
|
462 |
+
return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'];
|
463 |
+
}
|
464 |
+
|
465 |
+
return false;
|
466 |
+
}
|
467 |
+
|
468 |
+
function getNamespaceAttributeName($namespace)
|
469 |
+
{
|
470 |
+
/* If it doesn't exist at first, flip the array and check again. */
|
471 |
+
if (empty($this->ns[$namespace])) {
|
472 |
+
$this->ns = array_flip($this->namespaces);
|
473 |
+
}
|
474 |
+
|
475 |
+
/* If it doesn't exist now, add it. */
|
476 |
+
if (empty($this->ns[$namespace])) {
|
477 |
+
return $this->addNamespace($namespace);
|
478 |
+
}
|
479 |
+
|
480 |
+
return $this->ns[$namespace];
|
481 |
+
}
|
482 |
+
|
483 |
+
function addNamespace($namespace)
|
484 |
+
{
|
485 |
+
if (!empty($this->ns[$namespace])) {
|
486 |
+
return $this->ns[$namespace];
|
487 |
+
}
|
488 |
+
|
489 |
+
$n = count($this->ns);
|
490 |
+
$attr = 'ns' . $n;
|
491 |
+
$this->namespaces['ns' . $n] = $namespace;
|
492 |
+
$this->ns[$namespace] = $attr;
|
493 |
+
|
494 |
+
return $attr;
|
495 |
+
}
|
496 |
+
|
497 |
+
function _validateString($string)
|
498 |
+
{
|
499 |
+
return preg_match('/^[\w_:#\/]+$/', $string);
|
500 |
+
}
|
501 |
+
|
502 |
+
function _addArg(&$args, &$argarray, $argname)
|
503 |
+
{
|
504 |
+
if ($args) {
|
505 |
+
$args .= ', ';
|
506 |
+
}
|
507 |
+
$args .= '$' . $argname;
|
508 |
+
if (!$this->_validateString($argname)) {
|
509 |
+
return;
|
510 |
+
}
|
511 |
+
if ($argarray) {
|
512 |
+
$argarray .= ', ';
|
513 |
+
}
|
514 |
+
$argarray .= "'$argname' => $" . $argname;
|
515 |
+
}
|
516 |
+
|
517 |
+
function _elementArg(&$args, &$argarray, &$_argtype, $_argname)
|
518 |
+
{
|
519 |
+
$comments = '';
|
520 |
+
$el = $this->elements[$_argtype['namespace']][$_argtype['type']];
|
521 |
+
$tns = isset($this->ns[$el['namespace']])
|
522 |
+
? $this->ns[$el['namespace']]
|
523 |
+
: $_argtype['namespace'];
|
524 |
+
|
525 |
+
if (!empty($el['complex']) ||
|
526 |
+
(isset($el['type']) &&
|
527 |
+
isset($this->complexTypes[$tns][$el['type']]))) {
|
528 |
+
// The element is a complex type.
|
529 |
+
$comments .= " // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n";
|
530 |
+
$attrname = "{$_argtype['type']}_attr";
|
531 |
+
if (isset($el['type']) &&
|
532 |
+
isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
|
533 |
+
$comments .= " // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n";
|
534 |
+
}
|
535 |
+
$comments .= " \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n";
|
536 |
+
$comments .= " \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n";
|
537 |
+
$this->_addArg($args, $argarray, $_argtype['type']);
|
538 |
+
if (isset($el['type']) &&
|
539 |
+
isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
|
540 |
+
if ($args) {
|
541 |
+
$args .= ', ';
|
542 |
+
}
|
543 |
+
$args .= '$' . $attrname;
|
544 |
+
}
|
545 |
+
} elseif (isset($el['elements'])) {
|
546 |
+
foreach ($el['elements'] as $ename => $element) {
|
547 |
+
$comments .= " \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" .
|
548 |
+
(isset($element['type']) ? $element['type'] : false) .
|
549 |
+
"', \$$ename);\n";
|
550 |
+
$this->_addArg($args, $argarray, $ename);
|
551 |
+
}
|
552 |
+
} else {
|
553 |
+
$comments .= " \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n";
|
554 |
+
$this->_addArg($args, $argarray, $_argname);
|
555 |
+
}
|
556 |
+
|
557 |
+
return $comments;
|
558 |
+
}
|
559 |
+
|
560 |
+
function _complexTypeArg(&$args, &$argarray, &$_argtype, $_argname)
|
561 |
+
{
|
562 |
+
$comments = '';
|
563 |
+
if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) {
|
564 |
+
$comments = " // $_argname is a ComplexType {$_argtype['type']},\n" .
|
565 |
+
" // refer to wsdl for more info\n";
|
566 |
+
if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) {
|
567 |
+
$comments .= " // $_argname may require attributes, refer to wsdl for more info\n";
|
568 |
+
}
|
569 |
+
$wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $_argtype['type'];
|
570 |
+
$comments .= " \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n";
|
571 |
+
}
|
572 |
+
|
573 |
+
$this->_addArg($args, $argarray, $_argname);
|
574 |
+
|
575 |
+
return $comments;
|
576 |
+
}
|
577 |
+
|
578 |
+
/**
|
579 |
+
* Generates stub code from the WSDL that can be saved to a file or eval'd
|
580 |
+
* into existence.
|
581 |
+
*/
|
582 |
+
function generateProxyCode($port = '', $classname = '')
|
583 |
+
{
|
584 |
+
if ($this->_isfault()) {
|
585 |
+
return $this->_getfault();
|
586 |
+
}
|
587 |
+
|
588 |
+
$multiport = count($this->services[$this->service]['ports']) > 1;
|
589 |
+
if (!$port) {
|
590 |
+
reset($this->services[$this->service]['ports']);
|
591 |
+
$port = current($this->services[$this->service]['ports']);
|
592 |
+
}
|
593 |
+
// XXX currently do not support HTTP ports
|
594 |
+
if ($port['type'] != 'soap') {
|
595 |
+
return null;
|
596 |
+
}
|
597 |
+
|
598 |
+
// XXX currentPort is BAD
|
599 |
+
$clienturl = $port['address']['location'];
|
600 |
+
if (!$classname) {
|
601 |
+
if ($multiport || $port) {
|
602 |
+
$classname = 'WebService_' . $this->service . '_' . $port['name'];
|
603 |
+
} else {
|
604 |
+
$classname = 'WebService_' . $this->service;
|
605 |
+
}
|
606 |
+
$classname = $this->_sanitize($classname);
|
607 |
+
}
|
608 |
+
|
609 |
+
if (!$this->_validateString($classname)) {
|
610 |
+
return null;
|
611 |
+
}
|
612 |
+
|
613 |
+
if (is_array($this->proxy) && count($this->proxy)) {
|
614 |
+
$class = "class $classname extends SOAP_Client\n{\n" .
|
615 |
+
" function $classname(\$path = '$clienturl')\n {\n" .
|
616 |
+
" \$this->SOAP_Client(\$path, 0, 0,\n" .
|
617 |
+
' array(';
|
618 |
+
|
619 |
+
foreach ($this->proxy as $key => $val) {
|
620 |
+
if (is_array($val)) {
|
621 |
+
$class .= "'$key' => array(";
|
622 |
+
foreach ($val as $key2 => $val2) {
|
623 |
+
$class .= "'$key2' => '$val2', ";
|
624 |
+
}
|
625 |
+
$class .= ')';
|
626 |
+
} else {
|
627 |
+
$class .= "'$key' => '$val', ";
|
628 |
+
}
|
629 |
+
}
|
630 |
+
$class .= "));\n }\n";
|
631 |
+
$class = str_replace(', ))', '))', $class);
|
632 |
+
} else {
|
633 |
+
$class = "class $classname extends SOAP_Client\n{\n" .
|
634 |
+
" function $classname(\$path = '$clienturl')\n {\n" .
|
635 |
+
" \$this->SOAP_Client(\$path, 0);\n" .
|
636 |
+
" }\n";
|
637 |
+
}
|
638 |
+
|
639 |
+
// Get the binding, from that get the port type.
|
640 |
+
$primaryBinding = $port['binding'];
|
641 |
+
$primaryBinding = preg_replace("/^(.*:)/", '', $primaryBinding);
|
642 |
+
$portType = $this->bindings[$primaryBinding]['type'];
|
643 |
+
$portType = preg_replace("/^(.*:)/", '', $portType);
|
644 |
+
$style = $this->bindings[$primaryBinding]['style'];
|
645 |
+
|
646 |
+
// XXX currentPortType is BAD
|
647 |
+
foreach ($this->portTypes[$portType] as $opname => $operation) {
|
648 |
+
$binding = $this->bindings[$primaryBinding]['operations'][$opname];
|
649 |
+
if (isset($binding['soapAction'])) {
|
650 |
+
$soapaction = $binding['soapAction'];
|
651 |
+
} else {
|
652 |
+
$soapaction = null;
|
653 |
+
}
|
654 |
+
if (isset($binding['style'])) {
|
655 |
+
$opstyle = $binding['style'];
|
656 |
+
} else {
|
657 |
+
$opstyle = $style;
|
658 |
+
}
|
659 |
+
$use = $binding['input']['use'];
|
660 |
+
if ($use == 'encoded') {
|
661 |
+
$namespace = $binding['input']['namespace'];
|
662 |
+
} else {
|
663 |
+
$bindingType = $this->bindings[$primaryBinding]['type'];
|
664 |
+
$ns = $this->portTypes[$bindingType][$opname]['input']['namespace'];
|
665 |
+
$namespace = $this->namespaces[$ns];
|
666 |
+
}
|
667 |
+
|
668 |
+
$args = '';
|
669 |
+
$argarray = '';
|
670 |
+
$comments = '';
|
671 |
+
$wrappers = '';
|
672 |
+
foreach ($operation['input'] as $argname => $argtype) {
|
673 |
+
if ($argname == 'message') {
|
674 |
+
foreach ($this->messages[$argtype] as $_argname => $_argtype) {
|
675 |
+
$_argname = $this->_sanitize($_argname);
|
676 |
+
if ($opstyle == 'document' && $use == 'literal' &&
|
677 |
+
$_argtype['name'] == 'parameters') {
|
678 |
+
// The type or element refered to is used for
|
679 |
+
// parameters.
|
680 |
+
$elattrs = null;
|
681 |
+
$el = $this->elements[$_argtype['namespace']][$_argtype['type']];
|
682 |
+
|
683 |
+
if ($el['complex']) {
|
684 |
+
$namespace = $this->namespaces[$_argtype['namespace']];
|
685 |
+
// XXX need to wrap the parameters in a
|
686 |
+
// SOAP_Value.
|
687 |
+
}
|
688 |
+
if (isset($el['elements'])) {
|
689 |
+
foreach ($el['elements'] as $elname => $elattrs) {
|
690 |
+
$elname = $this->_sanitize($elname);
|
691 |
+
// Is the element a complex type?
|
692 |
+
if (isset($this->complexTypes[$elattrs['namespace']][$elname])) {
|
693 |
+
$comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
|
694 |
+
} else {
|
695 |
+
$this->_addArg($args, $argarray, $elname);
|
696 |
+
}
|
697 |
+
}
|
698 |
+
}
|
699 |
+
if ($el['complex'] && $argarray) {
|
700 |
+
$wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $el['name'];
|
701 |
+
$comments .= " \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n";
|
702 |
+
$argarray = "'{$el['name']}' => \${$el['name']}";
|
703 |
+
}
|
704 |
+
} else {
|
705 |
+
if (isset($_argtype['element'])) {
|
706 |
+
// Element argument.
|
707 |
+
$comments .= $this->_elementArg($args, $argarray, $_argtype, $_argtype['type']);
|
708 |
+
} else {
|
709 |
+
// Complex type argument.
|
710 |
+
$comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
|
711 |
+
}
|
712 |
+
}
|
713 |
+
}
|
714 |
+
}
|
715 |
+
}
|
716 |
+
|
717 |
+
// Validate entries.
|
718 |
+
|
719 |
+
// Operation names are function names, so try to make sure it's
|
720 |
+
// legal. This could potentially cause collisions, but let's try
|
721 |
+
// to make everything callable and see how many problems that
|
722 |
+
// causes.
|
723 |
+
$opname_php = $this->_sanitize($opname);
|
724 |
+
if (!$this->_validateString($opname_php)) {
|
725 |
+
return null;
|
726 |
+
}
|
727 |
+
|
728 |
+
if ($argarray) {
|
729 |
+
$argarray = "array($argarray)";
|
730 |
+
} else {
|
731 |
+
$argarray = 'null';
|
732 |
+
}
|
733 |
+
|
734 |
+
$class .= " function &$opname_php($args)\n {\n$comments$wrappers" .
|
735 |
+
" \$result = \$this->call('$opname',\n" .
|
736 |
+
" \$v = $argarray,\n" .
|
737 |
+
" array('namespace' => '$namespace',\n" .
|
738 |
+
" 'soapaction' => '$soapaction',\n" .
|
739 |
+
" 'style' => '$opstyle',\n" .
|
740 |
+
" 'use' => '$use'" .
|
741 |
+
($this->trace ? ",\n 'trace' => true" : '') . "));\n" .
|
742 |
+
" return \$result;\n" .
|
743 |
+
" }\n";
|
744 |
+
}
|
745 |
+
|
746 |
+
$class .= "}\n";
|
747 |
+
|
748 |
+
return $class;
|
749 |
+
}
|
750 |
+
|
751 |
+
function generateAllProxies()
|
752 |
+
{
|
753 |
+
$proxycode = '';
|
754 |
+
foreach (array_keys($this->services[$this->service]['ports']) as $key) {
|
755 |
+
$port =& $this->services[$this->service]['ports'][$key];
|
756 |
+
$proxycode .= $this->generateProxyCode($port);
|
757 |
+
}
|
758 |
+
return $proxycode;
|
759 |
+
}
|
760 |
+
|
761 |
+
function &getProxy($port = '', $name = '')
|
762 |
+
{
|
763 |
+
if ($this->_isfault()) {
|
764 |
+
$fault =& $this->_getfault();
|
765 |
+
return $fault;
|
766 |
+
}
|
767 |
+
|
768 |
+
$multiport = count($this->services[$this->service]['ports']) > 1;
|
769 |
+
|
770 |
+
if (!$port) {
|
771 |
+
reset($this->services[$this->service]['ports']);
|
772 |
+
$port = current($this->services[$this->service]['ports']);
|
773 |
+
}
|
774 |
+
|
775 |
+
if ($multiport || $port) {
|
776 |
+
$classname = 'WebService_' . $this->service . '_' . $port['name'];
|
777 |
+
} else {
|
778 |
+
$classname = 'WebService_' . $this->service;
|
779 |
+
}
|
780 |
+
|
781 |
+
if ($name) {
|
782 |
+
$classname = $name . '_' . $classname;
|
783 |
+
}
|
784 |
+
|
785 |
+
$classname = $this->_sanitize($classname);
|
786 |
+
if (!class_exists($classname)) {
|
787 |
+
$proxy = $this->generateProxyCode($port, $classname);
|
788 |
+
require_once 'SOAP/Client.php';
|
789 |
+
eval($proxy);
|
790 |
+
}
|
791 |
+
$proxy = new $classname;
|
792 |
+
|
793 |
+
return $proxy;
|
794 |
+
}
|
795 |
+
|
796 |
+
/**
|
797 |
+
* Sanitizes a SOAP value, method or class name so that it can be used as
|
798 |
+
* a valid PHP identifier. Invalid characters are converted into
|
799 |
+
* underscores and reserved words are prefixed with an underscore.
|
800 |
+
*
|
801 |
+
* @param string $name The identifier to sanitize.
|
802 |
+
*
|
803 |
+
* @return string The sanitized identifier.
|
804 |
+
*/
|
805 |
+
function _sanitize($name)
|
806 |
+
{
|
807 |
+
$name = preg_replace($this->_invalid, '_', $name);
|
808 |
+
if (in_array($name, $this->_reserved)) {
|
809 |
+
$name = '_' . $name;
|
810 |
+
}
|
811 |
+
return $name;
|
812 |
+
}
|
813 |
+
|
814 |
+
function &_getComplexTypeForElement($name, $namespace)
|
815 |
+
{
|
816 |
+
$t = null;
|
817 |
+
if (isset($this->ns[$namespace]) &&
|
818 |
+
isset($this->elements[$this->ns[$namespace]][$name]['type'])) {
|
819 |
+
|
820 |
+
$type = $this->elements[$this->ns[$namespace]][$name]['type'];
|
821 |
+
$ns = $this->elements[$this->ns[$namespace]][$name]['namespace'];
|
822 |
+
|
823 |
+
if (isset($this->complexTypes[$ns][$type])) {
|
824 |
+
$t = $this->complexTypes[$ns][$type];
|
825 |
+
}
|
826 |
+
}
|
827 |
+
return $t;
|
828 |
+
}
|
829 |
+
|
830 |
+
function getComplexTypeNameForElement($name, $namespace)
|
831 |
+
{
|
832 |
+
$t = $this->_getComplexTypeForElement($name, $namespace);
|
833 |
+
if ($t) {
|
834 |
+
return $t['name'];
|
835 |
+
}
|
836 |
+
return null;
|
837 |
+
}
|
838 |
+
|
839 |
+
function getComplexTypeChildType($ns, $name, $child_ns, $child_name)
|
840 |
+
{
|
841 |
+
// Is the type an element?
|
842 |
+
$t = $this->_getComplexTypeForElement($name, $ns);
|
843 |
+
if ($t) {
|
844 |
+
// No, get it from complex types directly.
|
845 |
+
if (isset($t['elements'][$child_name]['type']))
|
846 |
+
return $t['elements'][$child_name]['type'];
|
847 |
+
} elseif (isset($this->ns[$ns]) &&
|
848 |
+
isset($this->elements[$this->ns[$ns]][$name]['complex']) &&
|
849 |
+
$this->elements[$this->ns[$ns]][$name]['complex']) {
|
850 |
+
// Type is not an element but complex.
|
851 |
+
return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type'];
|
852 |
+
}
|
853 |
+
return null;
|
854 |
+
}
|
855 |
+
|
856 |
+
/**
|
857 |
+
* @param QName $name A parameter name.
|
858 |
+
* @param QName $type A parameter type.
|
859 |
+
*
|
860 |
+
* @return array A list of [type, array element type, array element
|
861 |
+
* namespace, array length].
|
862 |
+
*/
|
863 |
+
function getSchemaType($type, $name)
|
864 |
+
{
|
865 |
+
// see if it's a complex type so we can deal properly with
|
866 |
+
// SOAPENC:arrayType.
|
867 |
+
if ($name && $type) {
|
868 |
+
// XXX TODO:
|
869 |
+
// look up the name in the wsdl and validate the type.
|
870 |
+
foreach ($this->complexTypes as $types) {
|
871 |
+
if (isset($types[$type->name])) {
|
872 |
+
if (isset($types[$type->name]['type'])) {
|
873 |
+
list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type->name]['arrayType'])
|
874 |
+
? $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType'])
|
875 |
+
: array($this->namespaces[$types[$type->name]['namespace']], null, 0);
|
876 |
+
return array($types[$type->name]['type'], $arraytype, $arraytype_ns, $array_depth);
|
877 |
+
}
|
878 |
+
if (isset($types[$type->name]['arrayType'])) {
|
879 |
+
list($arraytype_ns, $arraytype, $array_depth) =
|
880 |
+
$this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType']);
|
881 |
+
return array('Array', $arraytype, $arraytype_ns, $array_depth);
|
882 |
+
}
|
883 |
+
if (!empty($types[$type->name]['elements'][$name->name])) {
|
884 |
+
$type->name = $types[$type->name]['elements']['type'];
|
885 |
+
return array($type->name, null, $this->namespaces[$types[$type->name]['namespace']], null);
|
886 |
+
}
|
887 |
+
break;
|
888 |
+
}
|
889 |
+
}
|
890 |
+
}
|
891 |
+
if ($type && $type->namespace) {
|
892 |
+
$arrayType = null;
|
893 |
+
// XXX TODO:
|
894 |
+
// this code currently handles only one way of encoding array
|
895 |
+
// types in wsdl need to do a generalized function to figure out
|
896 |
+
// complex types
|
897 |
+
$p = $this->ns[$type->namespace];
|
898 |
+
if ($p && !empty($this->complexTypes[$p][$type->name])) {
|
899 |
+
if ($arrayType = $this->complexTypes[$p][$type->name]['arrayType']) {
|
900 |
+
$type->name = 'Array';
|
901 |
+
} elseif ($this->complexTypes[$p][$type->name]['order'] == 'sequence' &&
|
902 |
+
array_key_exists('elements', $this->complexTypes[$p][$type->name])) {
|
903 |
+
reset($this->complexTypes[$p][$type->name]['elements']);
|
904 |
+
// assume an array
|
905 |
+
if (count($this->complexTypes[$p][$type->name]['elements']) == 1) {
|
906 |
+
$arg = current($this->complexTypes[$p][$type->name]['elements']);
|
907 |
+
$arrayType = $arg['type'];
|
908 |
+
$type->name = 'Array';
|
909 |
+
} else {
|
910 |
+
foreach ($this->complexTypes[$p][$type->name]['elements'] as $element) {
|
911 |
+
if ($element['name'] == $type->name) {
|
912 |
+
$arrayType = $element['type'];
|
913 |
+
$type->name = $element['type'];
|
914 |
+
}
|
915 |
+
}
|
916 |
+
}
|
917 |
+
} else {
|
918 |
+
$type->name = 'Struct';
|
919 |
+
}
|
920 |
+
return array($type->name, $arrayType, $type->namespace, null);
|
921 |
+
}
|
922 |
+
}
|
923 |
+
return array(null, null, null, null);
|
924 |
+
}
|
925 |
+
|
926 |
+
/**
|
927 |
+
* Recurse through the WSDL structure looking for the innermost array type
|
928 |
+
* of multi-dimensional arrays.
|
929 |
+
*
|
930 |
+
* Takes a namespace prefix and a type, which can be in the form 'type' or
|
931 |
+
* 'type[]', and returns the full namespace URI, the type of the most
|
932 |
+
* deeply nested array type found, and the number of levels of nesting.
|
933 |
+
*
|
934 |
+
* @access private
|
935 |
+
* @return mixed array or nothing
|
936 |
+
*/
|
937 |
+
function _getDeepestArrayType($nsPrefix, $arrayType)
|
938 |
+
{
|
939 |
+
static $trail = array();
|
940 |
+
|
941 |
+
$arrayType = ereg_replace('\[\]$', '', $arrayType);
|
942 |
+
|
943 |
+
// Protect against circular references XXX We really need to remove
|
944 |
+
// trail from this altogether (it's very inefficient and in the wrong
|
945 |
+
// place!) and put circular reference checking in when the WSDL info
|
946 |
+
// is generated in the first place
|
947 |
+
if (array_search($nsPrefix . ':' . $arrayType, $trail)) {
|
948 |
+
return array(null, null, -count($trail));
|
949 |
+
}
|
950 |
+
|
951 |
+
if (array_key_exists($nsPrefix, $this->complexTypes) &&
|
952 |
+
array_key_exists($arrayType, $this->complexTypes[$nsPrefix]) &&
|
953 |
+
array_key_exists('arrayType', $this->complexTypes[$nsPrefix][$arrayType])) {
|
954 |
+
$trail[] = $nsPrefix . ':' . $arrayType;
|
955 |
+
$result = $this->_getDeepestArrayType($this->complexTypes[$nsPrefix][$arrayType]['namespace'],
|
956 |
+
$this->complexTypes[$nsPrefix][$arrayType]['arrayType']);
|
957 |
+
return array($result[0], $result[1], $result[2] + 1);
|
958 |
+
}
|
959 |
+
return array($this->namespaces[$nsPrefix], $arrayType, 0);
|
960 |
+
}
|
961 |
+
|
962 |
+
}
|
963 |
+
|
964 |
+
class SOAP_WSDL_Cache extends SOAP_Base
|
965 |
+
{
|
966 |
+
/**
|
967 |
+
* Use WSDL cache?
|
968 |
+
*
|
969 |
+
* @var boolean
|
970 |
+
*/
|
971 |
+
var $_cacheUse;
|
972 |
+
|
973 |
+
/**
|
974 |
+
* WSDL cache directory.
|
975 |
+
*
|
976 |
+
* @var string
|
977 |
+
*/
|
978 |
+
var $_cacheDir;
|
979 |
+
|
980 |
+
/**
|
981 |
+
* Cache maximum lifetime (in seconds)
|
982 |
+
*
|
983 |
+
* @var integer
|
984 |
+
*/
|
985 |
+
var $_cacheMaxAge;
|
986 |
+
|
987 |
+
/**
|
988 |
+
* Constructor.
|
989 |
+
*
|
990 |
+
* @param boolean $cashUse Use caching?
|
991 |
+
* @param integer $cacheMaxAge Cache maximum lifetime (in seconds)
|
992 |
+
*/
|
993 |
+
function SOAP_WSDL_Cache($cacheUse = false,
|
994 |
+
$cacheMaxAge = WSDL_CACHE_MAX_AGE,
|
995 |
+
$cacheDir = null)
|
996 |
+
{
|
997 |
+
parent::SOAP_Base('WSDLCACHE');
|
998 |
+
$this->_cacheUse = $cacheUse;
|
999 |
+
$this->_cacheDir = $cacheDir;
|
1000 |
+
$this->_cacheMaxAge = $cacheMaxAge;
|
1001 |
+
}
|
1002 |
+
|
1003 |
+
/**
|
1004 |
+
* Returns the path to the cache and creates it, if it doesn't exist.
|
1005 |
+
*
|
1006 |
+
* @private
|
1007 |
+
*
|
1008 |
+
* @return string The directory to use for the cache.
|
1009 |
+
*/
|
1010 |
+
function _cacheDir()
|
1011 |
+
{
|
1012 |
+
if (!empty($this->_cacheDir)) {
|
1013 |
+
$dir = $this->_cacheDir;
|
1014 |
+
} else {
|
1015 |
+
$dir = getenv('WSDLCACHE');
|
1016 |
+
if (empty($dir)) {
|
1017 |
+
$dir = './wsdlcache';
|
1018 |
+
}
|
1019 |
+
}
|
1020 |
+
@mkdir($dir, 0700);
|
1021 |
+
return $dir;
|
1022 |
+
}
|
1023 |
+
|
1024 |
+
/**
|
1025 |
+
* Retrieves a file from cache if it exists, otherwise retreive from net,
|
1026 |
+
* add to cache, and return from cache.
|
1027 |
+
*
|
1028 |
+
* @param string URL to WSDL
|
1029 |
+
* @param array proxy parameters
|
1030 |
+
* @param int expected MD5 of WSDL URL
|
1031 |
+
* @access public
|
1032 |
+
* @return string data
|
1033 |
+
*/
|
1034 |
+
function get($wsdl_fname, $proxy_params = array(), $cache = 0)
|
1035 |
+
{
|
1036 |
+
$cachename = $md5_wsdl = $file_data = '';
|
1037 |
+
if ($this->_cacheUse) {
|
1038 |
+
// Try to retrieve WSDL from cache
|
1039 |
+
$cachename = $this->_cacheDir() . '/' . md5($wsdl_fname). ' .wsdl';
|
1040 |
+
if (file_exists($cachename) &&
|
1041 |
+
$file_data = file_get_contents($cachename)) {
|
1042 |
+
$md5_wsdl = md5($file_data);
|
1043 |
+
if ($cache) {
|
1044 |
+
if ($cache != $md5_wsdl) {
|
1045 |
+
return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
|
1046 |
+
}
|
1047 |
+
} else {
|
1048 |
+
$fi = stat($cachename);
|
1049 |
+
$cache_mtime = $fi[8];
|
1050 |
+
if ($cache_mtime + $this->_cacheMaxAge < time()) {
|
1051 |
+
// Expired, refetch.
|
1052 |
+
$md5_wsdl = '';
|
1053 |
+
}
|
1054 |
+
}
|
1055 |
+
}
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
// Not cached or not using cache. Retrieve WSDL from URL
|
1059 |
+
if (!$md5_wsdl) {
|
1060 |
+
// Is it a local file?
|
1061 |
+
if (strpos($wsdl_fname, 'file://') === 0) {
|
1062 |
+
$wsdl_fname = substr($wsdl_fname, 7);
|
1063 |
+
if (!file_exists($wsdl_fname)) {
|
1064 |
+
return $this->_raiseSoapFault('Unable to read local WSDL file', $wsdl_fname);
|
1065 |
+
}
|
1066 |
+
$file_data = file_get_contents($wsdl_fname);
|
1067 |
+
} elseif (!preg_match('|^https?://|', $wsdl_fname)) {
|
1068 |
+
return $this->_raiseSoapFault('Unknown schema of WSDL URL', $wsdl_fname);
|
1069 |
+
} else {
|
1070 |
+
$uri = explode('?', $wsdl_fname);
|
1071 |
+
$rq = new HTTP_Request($uri[0], $proxy_params);
|
1072 |
+
// the user agent HTTP_Request uses fouls things up
|
1073 |
+
if (isset($uri[1])) {
|
1074 |
+
$rq->addRawQueryString($uri[1]);
|
1075 |
+
}
|
1076 |
+
|
1077 |
+
if (isset($proxy_params['proxy_host']) &&
|
1078 |
+
isset($proxy_params['proxy_port']) &&
|
1079 |
+
isset($proxy_params['proxy_user']) &&
|
1080 |
+
isset($proxy_params['proxy_pass'])) {
|
1081 |
+
$rq->setProxy($proxy_params['proxy_host'],
|
1082 |
+
$proxy_params['proxy_port'],
|
1083 |
+
$proxy_params['proxy_user'],
|
1084 |
+
$proxy_params['proxy_pass']);
|
1085 |
+
} elseif (isset($proxy_params['proxy_host']) &&
|
1086 |
+
isset($proxy_params['proxy_port'])) {
|
1087 |
+
$rq->setProxy($proxy_params['proxy_host'],
|
1088 |
+
$proxy_params['proxy_port']);
|
1089 |
+
}
|
1090 |
+
|
1091 |
+
$result = $rq->sendRequest();
|
1092 |
+
if (PEAR::isError($result)) {
|
1093 |
+
return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode(), $wsdl_fname);
|
1094 |
+
}
|
1095 |
+
$file_data = $rq->getResponseBody();
|
1096 |
+
if (!$file_data) {
|
1097 |
+
return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body", $wsdl_fname);
|
1098 |
+
}
|
1099 |
+
}
|
1100 |
+
|
1101 |
+
$md5_wsdl = md5($file_data);
|
1102 |
+
|
1103 |
+
if ($this->_cacheUse) {
|
1104 |
+
$fp = fopen($cachename, "wb");
|
1105 |
+
fwrite($fp, $file_data);
|
1106 |
+
fclose($fp);
|
1107 |
+
}
|
1108 |
+
}
|
1109 |
+
|
1110 |
+
if ($this->_cacheUse && $cache && $cache != $md5_wsdl) {
|
1111 |
+
return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
|
1112 |
+
}
|
1113 |
+
|
1114 |
+
return $file_data;
|
1115 |
+
}
|
1116 |
+
|
1117 |
+
}
|
1118 |
+
|
1119 |
+
class SOAP_WSDL_Parser extends SOAP_Base
|
1120 |
+
{
|
1121 |
+
|
1122 |
+
/**
|
1123 |
+
* Define internal arrays of bindings, ports, operations,
|
1124 |
+
* messages, etc.
|
1125 |
+
*/
|
1126 |
+
var $currentMessage;
|
1127 |
+
var $currentOperation;
|
1128 |
+
var $currentPortType;
|
1129 |
+
var $currentBinding;
|
1130 |
+
var $currentPort;
|
1131 |
+
|
1132 |
+
/**
|
1133 |
+
* Parser vars.
|
1134 |
+
*/
|
1135 |
+
var $cache;
|
1136 |
+
|
1137 |
+
var $tns = null;
|
1138 |
+
var $soapns = array('soap');
|
1139 |
+
var $uri = '';
|
1140 |
+
var $wsdl = null;
|
1141 |
+
|
1142 |
+
var $status = '';
|
1143 |
+
var $element_stack = array();
|
1144 |
+
var $parentElement = '';
|
1145 |
+
|
1146 |
+
var $schema = '';
|
1147 |
+
var $schemaStatus = '';
|
1148 |
+
var $schema_stack = array();
|
1149 |
+
var $currentComplexType;
|
1150 |
+
var $schema_element_stack = array();
|
1151 |
+
var $currentElement;
|
1152 |
+
|
1153 |
+
/**
|
1154 |
+
* Constructor.
|
1155 |
+
*/
|
1156 |
+
function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false)
|
1157 |
+
{
|
1158 |
+
parent::SOAP_Base('WSDLPARSER');
|
1159 |
+
$this->cache = new SOAP_WSDL_Cache($wsdl->cacheUse,
|
1160 |
+
$wsdl->cacheMaxAge,
|
1161 |
+
$wsdl->cacheDir);
|
1162 |
+
$this->uri = $uri;
|
1163 |
+
$this->wsdl = &$wsdl;
|
1164 |
+
$this->docs = $docs;
|
1165 |
+
$this->parse($uri);
|
1166 |
+
}
|
1167 |
+
|
1168 |
+
function parse($uri)
|
1169 |
+
{
|
1170 |
+
// Check whether content has been read.
|
1171 |
+
$fd = $this->cache->get($uri, $this->wsdl->proxy);
|
1172 |
+
if (PEAR::isError($fd)) {
|
1173 |
+
return $this->_raiseSoapFault($fd);
|
1174 |
+
}
|
1175 |
+
|
1176 |
+
// Create an XML parser.
|
1177 |
+
$parser = xml_parser_create();
|
1178 |
+
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
|
1179 |
+
xml_set_object($parser, $this);
|
1180 |
+
xml_set_element_handler($parser, 'startElement', 'endElement');
|
1181 |
+
if ($this->docs) {
|
1182 |
+
xml_set_character_data_handler($parser, 'characterData');
|
1183 |
+
}
|
1184 |
+
|
1185 |
+
if (!xml_parse($parser, $fd, true)) {
|
1186 |
+
$detail = sprintf('XML error on line %d: %s',
|
1187 |
+
xml_get_current_line_number($parser),
|
1188 |
+
xml_error_string(xml_get_error_code($parser)));
|
1189 |
+
return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail");
|
1190 |
+
}
|
1191 |
+
xml_parser_free($parser);
|
1192 |
+
return true;
|
1193 |
+
}
|
1194 |
+
|
1195 |
+
/**
|
1196 |
+
* start-element handler
|
1197 |
+
*/
|
1198 |
+
function startElement($parser, $name, $attrs)
|
1199 |
+
{
|
1200 |
+
// Get element prefix.
|
1201 |
+
$qname = new QName($name);
|
1202 |
+
if ($qname->ns) {
|
1203 |
+
$ns = $qname->ns;
|
1204 |
+
if ($ns && ((!$this->tns && strcasecmp($qname->name, 'definitions') == 0) || $ns == $this->tns)) {
|
1205 |
+
$name = $qname->name;
|
1206 |
+
}
|
1207 |
+
}
|
1208 |
+
$this->currentTag = $qname->name;
|
1209 |
+
$this->parentElement = '';
|
1210 |
+
$stack_size = count($this->element_stack);
|
1211 |
+
if ($stack_size) {
|
1212 |
+
$this->parentElement = $this->element_stack[$stack_size - 1];
|
1213 |
+
}
|
1214 |
+
$this->element_stack[] = $this->currentTag;
|
1215 |
+
|
1216 |
+
// Find status, register data.
|
1217 |
+
switch ($this->status) {
|
1218 |
+
case 'types':
|
1219 |
+
// sect 2.2 wsdl:types
|
1220 |
+
// children: xsd:schema
|
1221 |
+
$parent_tag = '';
|
1222 |
+
$stack_size = count($this->schema_stack);
|
1223 |
+
if ($stack_size) {
|
1224 |
+
$parent_tag = $this->schema_stack[$stack_size - 1];
|
1225 |
+
}
|
1226 |
+
|
1227 |
+
switch ($qname->name) {
|
1228 |
+
case 'schema':
|
1229 |
+
// No parent should be in the stack.
|
1230 |
+
if (!$parent_tag || $parent_tag == 'types') {
|
1231 |
+
if (array_key_exists('targetNamespace', $attrs)) {
|
1232 |
+
$this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
|
1233 |
+
} else {
|
1234 |
+
$this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
|
1235 |
+
}
|
1236 |
+
$this->wsdl->complexTypes[$this->schema] = array();
|
1237 |
+
$this->wsdl->elements[$this->schema] = array();
|
1238 |
+
}
|
1239 |
+
break;
|
1240 |
+
|
1241 |
+
case 'complexType':
|
1242 |
+
if ($parent_tag == 'schema') {
|
1243 |
+
$this->currentComplexType = $attrs['name'];
|
1244 |
+
if (!isset($attrs['namespace'])) {
|
1245 |
+
$attrs['namespace'] = $this->schema;
|
1246 |
+
}
|
1247 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs;
|
1248 |
+
if (array_key_exists('base', $attrs)) {
|
1249 |
+
$qn = new QName($attrs['base']);
|
1250 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
|
1251 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns;
|
1252 |
+
} else {
|
1253 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
|
1254 |
+
}
|
1255 |
+
$this->schemaStatus = 'complexType';
|
1256 |
+
} else {
|
1257 |
+
$this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = true;
|
1258 |
+
}
|
1259 |
+
break;
|
1260 |
+
|
1261 |
+
case 'element':
|
1262 |
+
if (isset($attrs['type'])) {
|
1263 |
+
$qn = new QName($attrs['type']);
|
1264 |
+
$attrs['type'] = $qn->name;
|
1265 |
+
if ($qn->ns && array_key_exists($qn->ns, $this->wsdl->namespaces)) {
|
1266 |
+
$attrs['namespace'] = $qn->ns;
|
1267 |
+
}
|
1268 |
+
}
|
1269 |
+
|
1270 |
+
$parentElement = '';
|
1271 |
+
$stack_size = count($this->schema_element_stack);
|
1272 |
+
if ($stack_size > 0) {
|
1273 |
+
$parentElement = $this->schema_element_stack[$stack_size - 1];
|
1274 |
+
}
|
1275 |
+
|
1276 |
+
if (isset($attrs['ref'])) {
|
1277 |
+
$qn = new QName($attrs['ref']);
|
1278 |
+
$this->currentElement = $qn->name;
|
1279 |
+
} else {
|
1280 |
+
$this->currentElement = $attrs['name'];
|
1281 |
+
}
|
1282 |
+
$this->schema_element_stack[] = $this->currentElement;
|
1283 |
+
if (!isset($attrs['namespace'])) {
|
1284 |
+
$attrs['namespace'] = $this->schema;
|
1285 |
+
}
|
1286 |
+
|
1287 |
+
if ($parent_tag == 'schema') {
|
1288 |
+
$this->wsdl->elements[$this->schema][$this->currentElement] = $attrs;
|
1289 |
+
$this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = false;
|
1290 |
+
$this->schemaStatus = 'element';
|
1291 |
+
} elseif ($this->currentComplexType) {
|
1292 |
+
// we're inside a complexType
|
1293 |
+
if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']) &&
|
1294 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] == 'sequence')
|
1295 |
+
&& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array') {
|
1296 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'] = isset($attrs['type']) ? $attrs['type'] : null;
|
1297 |
+
}
|
1298 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement] = $attrs;
|
1299 |
+
} else {
|
1300 |
+
$this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement] = $attrs;
|
1301 |
+
}
|
1302 |
+
break;
|
1303 |
+
|
1304 |
+
case 'complexContent':
|
1305 |
+
case 'simpleContent':
|
1306 |
+
break;
|
1307 |
+
|
1308 |
+
case 'extension':
|
1309 |
+
case 'restriction':
|
1310 |
+
if ($this->schemaStatus == 'complexType') {
|
1311 |
+
if (!empty($attrs['base'])) {
|
1312 |
+
$qn = new QName($attrs['base']);
|
1313 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
|
1314 |
+
|
1315 |
+
// Types that extend from other types aren't
|
1316 |
+
// *of* those types. Reflect this by denoting
|
1317 |
+
// which type they extend. I'm leaving the
|
1318 |
+
// 'type' setting here since I'm not sure what
|
1319 |
+
// removing it might break at the moment.
|
1320 |
+
if ($qname->name == 'extension') {
|
1321 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['extends'] = $qn->name;
|
1322 |
+
}
|
1323 |
+
} else {
|
1324 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
|
1325 |
+
}
|
1326 |
+
}
|
1327 |
+
break;
|
1328 |
+
|
1329 |
+
case 'sequence':
|
1330 |
+
if ($this->schemaStatus == 'complexType') {
|
1331 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
|
1332 |
+
if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
|
1333 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
|
1334 |
+
}
|
1335 |
+
}
|
1336 |
+
break;
|
1337 |
+
|
1338 |
+
case 'all':
|
1339 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
|
1340 |
+
if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
|
1341 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
|
1342 |
+
}
|
1343 |
+
break;
|
1344 |
+
|
1345 |
+
case 'choice':
|
1346 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
|
1347 |
+
if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
|
1348 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
|
1349 |
+
}
|
1350 |
+
|
1351 |
+
case 'attribute':
|
1352 |
+
if ($this->schemaStatus == 'complexType') {
|
1353 |
+
if (isset($attrs['name'])) {
|
1354 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] = $attrs;
|
1355 |
+
} else {
|
1356 |
+
if (isset($attrs['ref'])) {
|
1357 |
+
$q = new QName($attrs['ref']);
|
1358 |
+
foreach ($attrs as $k => $v) {
|
1359 |
+
if ($k != 'ref' && strstr($k, $q->name)) {
|
1360 |
+
$vq = new QName($v);
|
1361 |
+
if ($q->name == 'arrayType') {
|
1362 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name. $vq->arrayInfo;
|
1363 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
|
1364 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $vq->ns;
|
1365 |
+
} else {
|
1366 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name;
|
1367 |
+
}
|
1368 |
+
}
|
1369 |
+
}
|
1370 |
+
}
|
1371 |
+
}
|
1372 |
+
}
|
1373 |
+
break;
|
1374 |
+
}
|
1375 |
+
|
1376 |
+
$this->schema_stack[] = $qname->name;
|
1377 |
+
break;
|
1378 |
+
|
1379 |
+
case 'message':
|
1380 |
+
// sect 2.3 wsdl:message child wsdl:part
|
1381 |
+
switch ($qname->name) {
|
1382 |
+
case 'part':
|
1383 |
+
$qn = null;
|
1384 |
+
if (isset($attrs['type'])) {
|
1385 |
+
$qn = new QName($attrs['type']);
|
1386 |
+
} elseif (isset($attrs['element'])) {
|
1387 |
+
$qn = new QName($attrs['element']);
|
1388 |
+
}
|
1389 |
+
if ($qn) {
|
1390 |
+
$attrs['type'] = $qn->name;
|
1391 |
+
$attrs['namespace'] = $qn->ns;
|
1392 |
+
}
|
1393 |
+
$this->wsdl->messages[$this->currentMessage][$attrs['name']] = $attrs;
|
1394 |
+
// error in wsdl
|
1395 |
+
|
1396 |
+
case 'documentation':
|
1397 |
+
break;
|
1398 |
+
|
1399 |
+
default:
|
1400 |
+
break;
|
1401 |
+
}
|
1402 |
+
break;
|
1403 |
+
|
1404 |
+
case 'portType':
|
1405 |
+
// sect 2.4
|
1406 |
+
switch ($qname->name) {
|
1407 |
+
case 'operation':
|
1408 |
+
// attributes: name
|
1409 |
+
// children: wsdl:input wsdl:output wsdl:fault
|
1410 |
+
$this->currentOperation = $attrs['name'];
|
1411 |
+
$this->wsdl->portTypes[$this->currentPortType][$this->currentOperation] = $attrs;
|
1412 |
+
break;
|
1413 |
+
|
1414 |
+
case 'input':
|
1415 |
+
case 'output':
|
1416 |
+
case 'fault':
|
1417 |
+
// wsdl:input wsdl:output wsdl:fault
|
1418 |
+
// attributes: name message parameterOrder(optional)
|
1419 |
+
if ($this->currentOperation) {
|
1420 |
+
if (isset($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name])) {
|
1421 |
+
$this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = array_merge($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name], $attrs);
|
1422 |
+
} else {
|
1423 |
+
$this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = $attrs;
|
1424 |
+
}
|
1425 |
+
if (array_key_exists('message', $attrs)) {
|
1426 |
+
$qn = new QName($attrs['message']);
|
1427 |
+
$this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'] = $qn->name;
|
1428 |
+
$this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'] = $qn->ns;
|
1429 |
+
}
|
1430 |
+
}
|
1431 |
+
break;
|
1432 |
+
|
1433 |
+
case 'documentation':
|
1434 |
+
break;
|
1435 |
+
|
1436 |
+
default:
|
1437 |
+
break;
|
1438 |
+
}
|
1439 |
+
break;
|
1440 |
+
|
1441 |
+
case 'binding':
|
1442 |
+
$ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
|
1443 |
+
switch ($ns) {
|
1444 |
+
case SCHEMA_SOAP:
|
1445 |
+
case SCHEMA_SOAP12:
|
1446 |
+
// this deals with wsdl section 3 soap binding
|
1447 |
+
switch ($qname->name) {
|
1448 |
+
case 'binding':
|
1449 |
+
// sect 3.3
|
1450 |
+
// soap:binding, attributes: transport(required), style(optional, default = document)
|
1451 |
+
// if style is missing, it is assumed to be 'document'
|
1452 |
+
if (!isset($attrs['style'])) {
|
1453 |
+
$attrs['style'] = 'document';
|
1454 |
+
}
|
1455 |
+
$this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs);
|
1456 |
+
break;
|
1457 |
+
|
1458 |
+
case 'operation':
|
1459 |
+
// sect 3.4
|
1460 |
+
// soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style)
|
1461 |
+
if (!isset($attrs['style'])) {
|
1462 |
+
$attrs['style'] = $this->wsdl->bindings[$this->currentBinding]['style'];
|
1463 |
+
}
|
1464 |
+
if (isset($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation])) {
|
1465 |
+
$this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = array_merge($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation], $attrs);
|
1466 |
+
} else {
|
1467 |
+
$this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = $attrs;
|
1468 |
+
}
|
1469 |
+
break;
|
1470 |
+
|
1471 |
+
case 'body':
|
1472 |
+
// sect 3.5
|
1473 |
+
// soap:body attributes:
|
1474 |
+
// part - optional. listed parts must appear in body, missing means all parts appear in body
|
1475 |
+
// use - required. encoded|literal
|
1476 |
+
// encodingStyle - optional. space seperated list of encodings (uri's)
|
1477 |
+
$this->wsdl->bindings[$this->currentBinding]
|
1478 |
+
['operations'][$this->currentOperation][$this->opStatus] = $attrs;
|
1479 |
+
break;
|
1480 |
+
|
1481 |
+
case 'fault':
|
1482 |
+
// sect 3.6
|
1483 |
+
// soap:fault attributes: name use encodingStyle namespace
|
1484 |
+
$this->wsdl->bindings[$this->currentBinding]
|
1485 |
+
['operations'][$this->currentOperation][$this->opStatus] = $attrs;
|
1486 |
+
break;
|
1487 |
+
|
1488 |
+
case 'header':
|
1489 |
+
// sect 3.7
|
1490 |
+
// soap:header attributes: message part use encodingStyle namespace
|
1491 |
+
$this->wsdl->bindings[$this->currentBinding]
|
1492 |
+
['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
|
1493 |
+
break;
|
1494 |
+
|
1495 |
+
case 'headerfault':
|
1496 |
+
// sect 3.7
|
1497 |
+
// soap:header attributes: message part use encodingStyle namespace
|
1498 |
+
$header = count($this->wsdl->bindings[$this->currentBinding]
|
1499 |
+
['operations'][$this->currentOperation][$this->opStatus]['headers'])-1;
|
1500 |
+
$this->wsdl->bindings[$this->currentBinding]
|
1501 |
+
['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'] = $attrs;
|
1502 |
+
break;
|
1503 |
+
|
1504 |
+
case 'documentation':
|
1505 |
+
break;
|
1506 |
+
|
1507 |
+
default:
|
1508 |
+
// error! not a valid element inside binding
|
1509 |
+
break;
|
1510 |
+
}
|
1511 |
+
break;
|
1512 |
+
|
1513 |
+
case SCHEMA_WSDL:
|
1514 |
+
// XXX verify correct namespace
|
1515 |
+
// for now, default is the 'wsdl' namespace
|
1516 |
+
// other possible namespaces include smtp, http, etc. for alternate bindings
|
1517 |
+
switch ($qname->name) {
|
1518 |
+
case 'operation':
|
1519 |
+
// sect 2.5
|
1520 |
+
// wsdl:operation attributes: name
|
1521 |
+
$this->currentOperation = $attrs['name'];
|
1522 |
+
break;
|
1523 |
+
|
1524 |
+
case 'output':
|
1525 |
+
case 'input':
|
1526 |
+
case 'fault':
|
1527 |
+
// sect 2.5
|
1528 |
+
// wsdl:input attributes: name
|
1529 |
+
$this->opStatus = $qname->name;
|
1530 |
+
break;
|
1531 |
+
|
1532 |
+
case 'documentation':
|
1533 |
+
break;
|
1534 |
+
|
1535 |
+
default:
|
1536 |
+
break;
|
1537 |
+
}
|
1538 |
+
break;
|
1539 |
+
|
1540 |
+
case SCHEMA_WSDL_HTTP:
|
1541 |
+
switch ($qname->name) {
|
1542 |
+
case 'binding':
|
1543 |
+
// sect 4.4
|
1544 |
+
// http:binding attributes: verb
|
1545 |
+
// parent: wsdl:binding
|
1546 |
+
$this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs);
|
1547 |
+
break;
|
1548 |
+
|
1549 |
+
case 'operation':
|
1550 |
+
// sect 4.5
|
1551 |
+
// http:operation attributes: location
|
1552 |
+
// parent: wsdl:operation
|
1553 |
+
$this->wsdl->bindings[$this->currentBinding]['operations']
|
1554 |
+
[$this->currentOperation] = $attrs;
|
1555 |
+
break;
|
1556 |
+
|
1557 |
+
case 'urlEncoded':
|
1558 |
+
// sect 4.6
|
1559 |
+
// http:urlEncoded attributes: location
|
1560 |
+
// parent: wsdl:input wsdl:output etc.
|
1561 |
+
$this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
|
1562 |
+
[$this->currentOperation]['uri'] = 'urlEncoded';
|
1563 |
+
break;
|
1564 |
+
|
1565 |
+
case 'urlReplacement':
|
1566 |
+
// sect 4.7
|
1567 |
+
// http:urlReplacement attributes: location
|
1568 |
+
// parent: wsdl:input wsdl:output etc.
|
1569 |
+
$this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
|
1570 |
+
[$this->currentOperation]['uri'] = 'urlReplacement';
|
1571 |
+
break;
|
1572 |
+
|
1573 |
+
case 'documentation':
|
1574 |
+
break;
|
1575 |
+
|
1576 |
+
default:
|
1577 |
+
// error
|
1578 |
+
break;
|
1579 |
+
}
|
1580 |
+
|
1581 |
+
case SCHEMA_MIME:
|
1582 |
+
// sect 5
|
1583 |
+
// all mime parts are children of wsdl:input, wsdl:output, etc.
|
1584 |
+
// unsuported as of yet
|
1585 |
+
switch ($qname->name) {
|
1586 |
+
case 'content':
|
1587 |
+
// sect 5.3 mime:content
|
1588 |
+
// <mime:content part="nmtoken"? type="string"?/>
|
1589 |
+
// part attribute only required if content is child of multipart related,
|
1590 |
+
// it contains the name of the part
|
1591 |
+
// type attribute contains the mime type
|
1592 |
+
case 'multipartRelated':
|
1593 |
+
// sect 5.4 mime:multipartRelated
|
1594 |
+
case 'part':
|
1595 |
+
case 'mimeXml':
|
1596 |
+
// sect 5.6 mime:mimeXml
|
1597 |
+
// <mime:mimeXml part="nmtoken"?/>
|
1598 |
+
//
|
1599 |
+
case 'documentation':
|
1600 |
+
break;
|
1601 |
+
|
1602 |
+
default:
|
1603 |
+
// error
|
1604 |
+
break;
|
1605 |
+
}
|
1606 |
+
|
1607 |
+
case SCHEMA_DIME:
|
1608 |
+
// DIME is defined in:
|
1609 |
+
// http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm
|
1610 |
+
// all DIME parts are children of wsdl:input, wsdl:output, etc.
|
1611 |
+
// unsuported as of yet
|
1612 |
+
switch ($qname->name) {
|
1613 |
+
case 'message':
|
1614 |
+
// sect 4.1 dime:message
|
1615 |
+
// appears in binding section
|
1616 |
+
$this->wsdl->bindings[$this->currentBinding]['dime'] = $attrs;
|
1617 |
+
break;
|
1618 |
+
|
1619 |
+
default:
|
1620 |
+
break;
|
1621 |
+
}
|
1622 |
+
|
1623 |
+
default:
|
1624 |
+
break;
|
1625 |
+
}
|
1626 |
+
break;
|
1627 |
+
|
1628 |
+
case 'service':
|
1629 |
+
$ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
|
1630 |
+
|
1631 |
+
switch ($qname->name) {
|
1632 |
+
case 'port':
|
1633 |
+
// sect 2.6 wsdl:port attributes: name binding
|
1634 |
+
$this->currentPort = $attrs['name'];
|
1635 |
+
$this->wsdl->services[$this->currentService]['ports'][$this->currentPort] = $attrs;
|
1636 |
+
// XXX hack to deal with binding namespaces
|
1637 |
+
$qn = new QName($attrs['binding']);
|
1638 |
+
$this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name;
|
1639 |
+
$this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->ns;
|
1640 |
+
break;
|
1641 |
+
|
1642 |
+
case 'address':
|
1643 |
+
$this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['address'] = $attrs;
|
1644 |
+
// what TYPE of port is it? SOAP or HTTP?
|
1645 |
+
$ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
|
1646 |
+
switch ($ns) {
|
1647 |
+
case SCHEMA_WSDL_HTTP:
|
1648 |
+
$this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='http';
|
1649 |
+
break;
|
1650 |
+
|
1651 |
+
case SCHEMA_SOAP:
|
1652 |
+
$this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
|
1653 |
+
break;
|
1654 |
+
|
1655 |
+
default:
|
1656 |
+
// Shouldn't happen, we'll assume SOAP.
|
1657 |
+
$this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
|
1658 |
+
}
|
1659 |
+
|
1660 |
+
break;
|
1661 |
+
|
1662 |
+
case 'documentation':
|
1663 |
+
break;
|
1664 |
+
|
1665 |
+
default:
|
1666 |
+
break;
|
1667 |
+
}
|
1668 |
+
}
|
1669 |
+
|
1670 |
+
// Top level elements found under wsdl:definitions.
|
1671 |
+
switch ($qname->name) {
|
1672 |
+
case 'import':
|
1673 |
+
// sect 2.1.1 wsdl:import attributes: namespace location
|
1674 |
+
if ((isset($attrs['location']) || isset($attrs['schemaLocation'])) &&
|
1675 |
+
!isset($this->wsdl->imports[$attrs['namespace']])) {
|
1676 |
+
$uri = isset($attrs['location']) ? $attrs['location'] : $attrs['schemaLocation'];
|
1677 |
+
$location = @parse_url($uri);
|
1678 |
+
if (!isset($location['scheme'])) {
|
1679 |
+
$base = @parse_url($this->uri);
|
1680 |
+
$uri = $this->mergeUrl($base, $uri);
|
1681 |
+
}
|
1682 |
+
|
1683 |
+
$this->wsdl->imports[$attrs['namespace']] = $attrs;
|
1684 |
+
$import_parser_class = get_class($this);
|
1685 |
+
$import_parser = new $import_parser_class($uri, $this->wsdl, $this->docs);
|
1686 |
+
if ($import_parser->fault) {
|
1687 |
+
unset($this->wsdl->imports[$attrs['namespace']]);
|
1688 |
+
return false;
|
1689 |
+
}
|
1690 |
+
$this->currentImport = $attrs['namespace'];
|
1691 |
+
}
|
1692 |
+
// Continue on to the 'types' case - lack of break; is
|
1693 |
+
// intentional.
|
1694 |
+
|
1695 |
+
case 'types':
|
1696 |
+
// sect 2.2 wsdl:types
|
1697 |
+
$this->status = 'types';
|
1698 |
+
break;
|
1699 |
+
|
1700 |
+
case 'schema':
|
1701 |
+
// We can hit this at the top level if we've been asked to
|
1702 |
+
// import an XSD file.
|
1703 |
+
if (!empty($attrs['targetNamespace'])) {
|
1704 |
+
$this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
|
1705 |
+
} else {
|
1706 |
+
$this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
|
1707 |
+
}
|
1708 |
+
$this->wsdl->complexTypes[$this->schema] = array();
|
1709 |
+
$this->wsdl->elements[$this->schema] = array();
|
1710 |
+
$this->schema_stack[] = $qname->name;
|
1711 |
+
$this->status = 'types';
|
1712 |
+
break;
|
1713 |
+
|
1714 |
+
case 'message':
|
1715 |
+
// sect 2.3 wsdl:message attributes: name children:wsdl:part
|
1716 |
+
$this->status = 'message';
|
1717 |
+
if (isset($attrs['name'])) {
|
1718 |
+
$this->currentMessage = $attrs['name'];
|
1719 |
+
$this->wsdl->messages[$this->currentMessage] = array();
|
1720 |
+
}
|
1721 |
+
break;
|
1722 |
+
|
1723 |
+
case 'portType':
|
1724 |
+
// sect 2.4 wsdl:portType
|
1725 |
+
// attributes: name
|
1726 |
+
// children: wsdl:operation
|
1727 |
+
$this->status = 'portType';
|
1728 |
+
$this->currentPortType = $attrs['name'];
|
1729 |
+
$this->wsdl->portTypes[$this->currentPortType] = array();
|
1730 |
+
break;
|
1731 |
+
|
1732 |
+
case 'binding':
|
1733 |
+
// sect 2.5 wsdl:binding attributes: name type
|
1734 |
+
// children: wsdl:operation soap:binding http:binding
|
1735 |
+
if ($qname->ns && $qname->ns != $this->tns) {
|
1736 |
+
break;
|
1737 |
+
}
|
1738 |
+
$this->status = 'binding';
|
1739 |
+
$this->currentBinding = $attrs['name'];
|
1740 |
+
$qn = new QName($attrs['type']);
|
1741 |
+
$this->wsdl->bindings[$this->currentBinding]['type'] = $qn->name;
|
1742 |
+
$this->wsdl->bindings[$this->currentBinding]['namespace'] = $qn->ns;
|
1743 |
+
break;
|
1744 |
+
|
1745 |
+
case 'service':
|
1746 |
+
// sect 2.7 wsdl:service attributes: name children: ports
|
1747 |
+
$this->currentService = $attrs['name'];
|
1748 |
+
$this->wsdl->services[$this->currentService]['ports'] = array();
|
1749 |
+
$this->status = 'service';
|
1750 |
+
break;
|
1751 |
+
|
1752 |
+
case 'definitions':
|
1753 |
+
// sec 2.1 wsdl:definitions
|
1754 |
+
// attributes: name targetNamespace xmlns:*
|
1755 |
+
// children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service
|
1756 |
+
$this->wsdl->definition = $attrs;
|
1757 |
+
foreach ($attrs as $key => $value) {
|
1758 |
+
if (strstr($key, 'xmlns:') !== false) {
|
1759 |
+
$qn = new QName($key);
|
1760 |
+
// XXX need to refactor ns handling.
|
1761 |
+
$this->wsdl->namespaces[$qn->name] = $value;
|
1762 |
+
$this->wsdl->ns[$value] = $qn->name;
|
1763 |
+
if ($key == 'targetNamespace' ||
|
1764 |
+
strcasecmp($value,SOAP_SCHEMA) == 0) {
|
1765 |
+
$this->soapns[] = $qn->name;
|
1766 |
+
} else {
|
1767 |
+
if (in_array($value, $this->_XMLSchema)) {
|
1768 |
+
$this->wsdl->xsd = $value;
|
1769 |
+
}
|
1770 |
+
}
|
1771 |
+
}
|
1772 |
+
}
|
1773 |
+
if (isset($ns) && $ns) {
|
1774 |
+
$namespace = 'xmlns:' . $ns;
|
1775 |
+
if (!$this->wsdl->definition[$namespace]) {
|
1776 |
+
return $this->_raiseSoapFault("parse error, no namespace for $namespace", $this->uri);
|
1777 |
+
}
|
1778 |
+
$this->tns = $ns;
|
1779 |
+
}
|
1780 |
+
break;
|
1781 |
+
}
|
1782 |
+
}
|
1783 |
+
|
1784 |
+
/**
|
1785 |
+
* end-element handler.
|
1786 |
+
*/
|
1787 |
+
function endElement($parser, $name)
|
1788 |
+
{
|
1789 |
+
$stacksize = count($this->element_stack);
|
1790 |
+
if ($stacksize) {
|
1791 |
+
if ($this->element_stack[$stacksize - 1] == 'definitions') {
|
1792 |
+
$this->status = '';
|
1793 |
+
}
|
1794 |
+
array_pop($this->element_stack);
|
1795 |
+
}
|
1796 |
+
|
1797 |
+
if (stristr($name, 'schema')) {
|
1798 |
+
array_pop($this->schema_stack);
|
1799 |
+
$this->schema = '';
|
1800 |
+
}
|
1801 |
+
|
1802 |
+
if ($this->schema) {
|
1803 |
+
array_pop($this->schema_stack);
|
1804 |
+
if (count($this->schema_stack) <= 1) {
|
1805 |
+
/* Correct the type for sequences with multiple
|
1806 |
+
* elements. */
|
1807 |
+
if (isset($this->currentComplexType) && isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])
|
1808 |
+
&& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array'
|
1809 |
+
&& array_key_exists('elements', $this->wsdl->complexTypes[$this->schema][$this->currentComplexType])
|
1810 |
+
&& count($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements']) > 1) {
|
1811 |
+
$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
|
1812 |
+
}
|
1813 |
+
}
|
1814 |
+
if (stristr($name, 'complexType')) {
|
1815 |
+
$this->currentComplexType = '';
|
1816 |
+
if (count($this->schema_element_stack)) {
|
1817 |
+
$this->currentElement = array_pop($this->schema_element_stack);
|
1818 |
+
} else {
|
1819 |
+
$this->currentElement = '';
|
1820 |
+
}
|
1821 |
+
} elseif (stristr($name, 'element')) {
|
1822 |
+
if (count($this->schema_element_stack)) {
|
1823 |
+
$this->currentElement = array_pop($this->schema_element_stack);
|
1824 |
+
} else {
|
1825 |
+
$this->currentElement = '';
|
1826 |
+
}
|
1827 |
+
}
|
1828 |
+
}
|
1829 |
+
}
|
1830 |
+
|
1831 |
+
/**
|
1832 |
+
* Element content handler.
|
1833 |
+
*/
|
1834 |
+
function characterData($parser, $data)
|
1835 |
+
{
|
1836 |
+
// Store the documentation in the WSDL file.
|
1837 |
+
if ($this->currentTag == 'documentation') {
|
1838 |
+
$data = trim(preg_replace('/\s+/', ' ', $data));
|
1839 |
+
if (!strlen($data)) {
|
1840 |
+
return;
|
1841 |
+
}
|
1842 |
+
|
1843 |
+
switch ($this->status) {
|
1844 |
+
case 'service':
|
1845 |
+
$ptr =& $this->wsdl->services[$this->currentService];
|
1846 |
+
break;
|
1847 |
+
|
1848 |
+
case 'portType':
|
1849 |
+
$ptr =& $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation];
|
1850 |
+
break;
|
1851 |
+
|
1852 |
+
case 'binding':
|
1853 |
+
$ptr =& $this->wsdl->bindings[$this->currentBinding];
|
1854 |
+
break;
|
1855 |
+
|
1856 |
+
case 'message':
|
1857 |
+
$ptr =& $this->wsdl->messages[$this->currentMessage];
|
1858 |
+
break;
|
1859 |
+
|
1860 |
+
case 'operation':
|
1861 |
+
break;
|
1862 |
+
|
1863 |
+
case 'types':
|
1864 |
+
if (isset($this->currentComplexType) &&
|
1865 |
+
isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
|
1866 |
+
if ($this->currentElement) {
|
1867 |
+
$ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement];
|
1868 |
+
} else {
|
1869 |
+
$ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType];
|
1870 |
+
}
|
1871 |
+
}
|
1872 |
+
break;
|
1873 |
+
}
|
1874 |
+
|
1875 |
+
if (isset($ptr)) {
|
1876 |
+
if (!isset($ptr['documentation'])) {
|
1877 |
+
$ptr['documentation'] = '';
|
1878 |
+
} else {
|
1879 |
+
$ptr['documentation'] .= ' ';
|
1880 |
+
}
|
1881 |
+
$ptr['documentation'] .= $data;
|
1882 |
+
}
|
1883 |
+
}
|
1884 |
+
}
|
1885 |
+
|
1886 |
+
/**
|
1887 |
+
* $parsed is an array returned by parse_url().
|
1888 |
+
*
|
1889 |
+
* @access private
|
1890 |
+
*/
|
1891 |
+
function mergeUrl($parsed, $path)
|
1892 |
+
{
|
1893 |
+
if (!is_array($parsed)) {
|
1894 |
+
return false;
|
1895 |
+
}
|
1896 |
+
|
1897 |
+
$uri = '';
|
1898 |
+
if (!empty($parsed['scheme'])) {
|
1899 |
+
$sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://');
|
1900 |
+
$uri = $parsed['scheme'] . $sep;
|
1901 |
+
}
|
1902 |
+
|
1903 |
+
if (isset($parsed['pass'])) {
|
1904 |
+
$uri .= "$parsed[user]:$parsed[pass]@";
|
1905 |
+
} elseif (isset($parsed['user'])) {
|
1906 |
+
$uri .= "$parsed[user]@";
|
1907 |
+
}
|
1908 |
+
|
1909 |
+
if (isset($parsed['host'])) {
|
1910 |
+
$uri .= $parsed['host'];
|
1911 |
+
}
|
1912 |
+
if (isset($parsed['port'])) {
|
1913 |
+
$uri .= ":$parsed[port]";
|
1914 |
+
}
|
1915 |
+
if ($path[0] != '/' && isset($parsed['path'])) {
|
1916 |
+
if ($parsed['path'][strlen($parsed['path']) - 1] != '/') {
|
1917 |
+
$path = dirname($parsed['path']) . '/' . $path;
|
1918 |
+
} else {
|
1919 |
+
$path = $parsed['path'] . $path;
|
1920 |
+
}
|
1921 |
+
$path = $this->_normalize($path);
|
1922 |
+
}
|
1923 |
+
$sep = $path[0] == '/' ? '' : '/';
|
1924 |
+
$uri .= $sep . $path;
|
1925 |
+
|
1926 |
+
return $uri;
|
1927 |
+
}
|
1928 |
+
|
1929 |
+
function _normalize($path_str)
|
1930 |
+
{
|
1931 |
+
$pwd = '';
|
1932 |
+
$strArr = preg_split('/(\/)/', $path_str, -1, PREG_SPLIT_NO_EMPTY);
|
1933 |
+
$pwdArr = '';
|
1934 |
+
$j = 0;
|
1935 |
+
for ($i = 0; $i < count($strArr); $i++) {
|
1936 |
+
if ($strArr[$i] != ' ..') {
|
1937 |
+
if ($strArr[$i] != ' .') {
|
1938 |
+
$pwdArr[$j] = $strArr[$i];
|
1939 |
+
$j++;
|
1940 |
+
}
|
1941 |
+
} else {
|
1942 |
+
array_pop($pwdArr);
|
1943 |
+
$j--;
|
1944 |
+
}
|
1945 |
+
}
|
1946 |
+
$pStr = implode('/', $pwdArr);
|
1947 |
+
$pwd = (strlen($pStr) > 0) ? ('/' . $pStr) : '/';
|
1948 |
+
return $pwd;
|
1949 |
+
}
|
1950 |
+
|
1951 |
+
}
|
1952 |
+
|
1953 |
+
/**
|
1954 |
+
* Parses the types and methods used in web service objects into the internal
|
1955 |
+
* data structures used by SOAP_WSDL.
|
1956 |
+
*
|
1957 |
+
* Assumes the SOAP_WSDL class is unpopulated to start with.
|
1958 |
+
*
|
1959 |
+
* @author Chris Coe <info@intelligentstreaming.com>
|
1960 |
+
*/
|
1961 |
+
class SOAP_WSDL_ObjectParser extends SOAP_Base
|
1962 |
+
{
|
1963 |
+
/**
|
1964 |
+
* Target namespace for the WSDL document will have the following
|
1965 |
+
* prefix.
|
1966 |
+
*/
|
1967 |
+
var $tnsPrefix = 'tns';
|
1968 |
+
|
1969 |
+
/**
|
1970 |
+
* Reference to the SOAP_WSDL object to populate.
|
1971 |
+
*/
|
1972 |
+
var $wsdl = null;
|
1973 |
+
|
1974 |
+
/**
|
1975 |
+
* Constructor.
|
1976 |
+
*
|
1977 |
+
* @param object|array $objects Reference to the object or array of
|
1978 |
+
* objects to parse.
|
1979 |
+
* @param SOAP_WSDL $wsdl Reference to the SOAP_WSDL object to
|
1980 |
+
* populate.
|
1981 |
+
* @param string $targetNamespace The target namespace of schema types
|
1982 |
+
* etc.
|
1983 |
+
* @param string $service_name Name of the WSDL <service>.
|
1984 |
+
* @param string $service_desc Optional description of the WSDL
|
1985 |
+
* <service>.
|
1986 |
+
*/
|
1987 |
+
function SOAP_WSDL_ObjectParser($objects, &$wsdl, $targetNamespace,
|
1988 |
+
$service_name, $service_desc = '')
|
1989 |
+
{
|
1990 |
+
parent::SOAP_Base('WSDLOBJECTPARSER');
|
1991 |
+
|
1992 |
+
$this->wsdl = &$wsdl;
|
1993 |
+
|
1994 |
+
// Set up the SOAP_WSDL object
|
1995 |
+
$this->_initialise($service_name);
|
1996 |
+
|
1997 |
+
// Parse each web service object
|
1998 |
+
$wsdl_ref = is_array($objects) ? $objects : array($objects);
|
1999 |
+
|
2000 |
+
foreach ($wsdl_ref as $ref_item) {
|
2001 |
+
if (!is_object($ref_item)) {
|
2002 |
+
$this->_raiseSoapFault('Invalid web service object passed to object parser');
|
2003 |
+
continue;
|
2004 |
+
}
|
2005 |
+
|
2006 |
+
if (!$this->_parse($ref_item, $targetNamespace, $service_name)) {
|
2007 |
+
break;
|
2008 |
+
}
|
2009 |
+
}
|
2010 |
+
|
2011 |
+
// Build bindings from abstract data.
|
2012 |
+
if ($this->fault == null) {
|
2013 |
+
$this->_generateBindingsAndServices($targetNamespace, $service_name, $service_desc);
|
2014 |
+
}
|
2015 |
+
}
|
2016 |
+
|
2017 |
+
/**
|
2018 |
+
* Initialise the SOAP_WSDL tree (destructive).
|
2019 |
+
*
|
2020 |
+
* If the object has already been initialised, the only effect
|
2021 |
+
* will be to change the tns namespace to the new service name.
|
2022 |
+
*
|
2023 |
+
* @param $service_name Name of the WSDL <service>
|
2024 |
+
* @access private
|
2025 |
+
*/
|
2026 |
+
function _initialise($service_name)
|
2027 |
+
{
|
2028 |
+
// Set up the basic namespaces that all WSDL definitions use.
|
2029 |
+
$this->wsdl->namespaces['wsdl'] = SCHEMA_WSDL; // WSDL language
|
2030 |
+
$this->wsdl->namespaces['soap'] = SCHEMA_SOAP; // WSDL SOAP bindings
|
2031 |
+
$this->wsdl->namespaces[$this->tnsPrefix] = 'urn:' . $service_name; // Target namespace
|
2032 |
+
$this->wsdl->namespaces['xsd'] = array_search('xsd', $this->_namespaces); // XML Schema
|
2033 |
+
$this->wsdl->namespaces[SOAP_BASE::SOAPENCPrefix()] = array_search(SOAP_BASE::SOAPENCPrefix(), $this->_namespaces); // SOAP types
|
2034 |
+
|
2035 |
+
// XXX Refactor $namespace/$ns for Shane :-)
|
2036 |
+
unset($this->wsdl->ns['urn:' . $service_name]);
|
2037 |
+
$this->wsdl->ns += array_flip($this->wsdl->namespaces);
|
2038 |
+
|
2039 |
+
// Imports are not implemented in WSDL generation from classes.
|
2040 |
+
// *** <wsdl:import> ***
|
2041 |
+
}
|
2042 |
+
|
2043 |
+
/**
|
2044 |
+
* Parser - takes a single object to add to tree (non-destructive).
|
2045 |
+
*
|
2046 |
+
* @access private
|
2047 |
+
*
|
2048 |
+
* @param object $object Reference to the object to parse.
|
2049 |
+
* @param string $schemaNamespace
|
2050 |
+
* @param string $service_name Name of the WSDL <service>.
|
2051 |
+
*/
|
2052 |
+
function _parse($object, $schemaNamespace, $service_name)
|
2053 |
+
{
|
2054 |
+
// Create namespace prefix for the schema
|
2055 |
+
list($schPrefix,) = $this->_getTypeNs('{' . $schemaNamespace . '}');
|
2056 |
+
|
2057 |
+
// Parse all the types defined by the object in whatever
|
2058 |
+
// schema language we are using (currently __typedef arrays)
|
2059 |
+
// *** <wsdl:types> ***
|
2060 |
+
foreach ($object->__typedef as $typeName => $typeValue) {
|
2061 |
+
// Get/create namespace definition
|
2062 |
+
list($nsPrefix, $typeName) = $this->_getTypeNs($typeName);
|
2063 |
+
|
2064 |
+
// Create type definition
|
2065 |
+
$this->wsdl->complexTypes[$schPrefix][$typeName] = array('name' => $typeName);
|
2066 |
+
$thisType =& $this->wsdl->complexTypes[$schPrefix][$typeName];
|
2067 |
+
|
2068 |
+
// According to Dmitri's documentation, __typedef comes in two
|
2069 |
+
// flavors:
|
2070 |
+
// Array = array(array("item" => "value"))
|
2071 |
+
// Struct = array("item1" => "value1", "item2" => "value2", ...)
|
2072 |
+
if (is_array($typeValue)) {
|
2073 |
+
if (is_array(current($typeValue)) && count($typeValue) == 1
|
2074 |
+
&& count(current($typeValue)) == 1) {
|
2075 |
+
// It's an array
|
2076 |
+
$thisType['type'] = 'Array';
|
2077 |
+
$nsType = current(current($typeValue));
|
2078 |
+
list($nsPrefix, $typeName) = $this->_getTypeNs($nsType);
|
2079 |
+
$thisType['namespace'] = $nsPrefix;
|
2080 |
+
$thisType['arrayType'] = $typeName . '[]';
|
2081 |
+
} elseif (!is_array(current($typeValue))) {
|
2082 |
+
// It's a struct
|
2083 |
+
$thisType['type'] = 'Struct';
|
2084 |
+
$thisType['order'] = 'all';
|
2085 |
+
$thisType['namespace'] = $nsPrefix;
|
2086 |
+
$thisType['elements'] = array();
|
2087 |
+
|
2088 |
+
foreach ($typeValue as $elementName => $elementType) {
|
2089 |
+
list($nsPrefix, $typeName) = $this->_getTypeNs($elementType);
|
2090 |
+
$thisType['elements'][$elementName]['name'] = $elementName;
|
2091 |
+
$thisType['elements'][$elementName]['type'] = $typeName;
|
2092 |
+
$thisType['elements'][$elementName]['namespace'] = $nsPrefix;
|
2093 |
+
}
|
2094 |
+
} else {
|
2095 |
+
// It's erroneous
|
2096 |
+
return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
|
2097 |
+
}
|
2098 |
+
} else {
|
2099 |
+
// It's erroneous
|
2100 |
+
return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
|
2101 |
+
}
|
2102 |
+
}
|
2103 |
+
|
2104 |
+
// Create an empty element array with the target namespace
|
2105 |
+
// prefix, to match the results of WSDL parsing.
|
2106 |
+
$this->wsdl->elements[$schPrefix] = array();
|
2107 |
+
|
2108 |
+
// Populate tree with message information
|
2109 |
+
// *** <wsdl:message> ***
|
2110 |
+
foreach ($object->__dispatch_map as $operationName => $messages) {
|
2111 |
+
// We need at least 'in' and 'out' parameters.
|
2112 |
+
if (!isset($messages['in']) || !isset($messages['out'])) {
|
2113 |
+
return $this->_raiseSoapFault('The dispatch map for the method "' . $operationName . '" is missing an "in" or "out" definition.', 'urn:' . get_class($object));
|
2114 |
+
}
|
2115 |
+
foreach ($messages as $messageType => $messageParts) {
|
2116 |
+
unset($thisMessage);
|
2117 |
+
|
2118 |
+
switch ($messageType) {
|
2119 |
+
case 'in':
|
2120 |
+
$this->wsdl->messages[$operationName . 'Request'] = array();
|
2121 |
+
$thisMessage =& $this->wsdl->messages[$operationName . 'Request'];
|
2122 |
+
break;
|
2123 |
+
|
2124 |
+
case 'out':
|
2125 |
+
$this->wsdl->messages[$operationName . 'Response'] = array();
|
2126 |
+
$thisMessage =& $this->wsdl->messages[$operationName . 'Response'];
|
2127 |
+
break;
|
2128 |
+
|
2129 |
+
case 'alias':
|
2130 |
+
// Do nothing
|
2131 |
+
break;
|
2132 |
+
|
2133 |
+
default:
|
2134 |
+
// Error condition
|
2135 |
+
break;
|
2136 |
+
}
|
2137 |
+
|
2138 |
+
if (isset($thisMessage)) {
|
2139 |
+
foreach ($messageParts as $partName => $partType) {
|
2140 |
+
list ($nsPrefix, $typeName) = $this->_getTypeNs($partType);
|
2141 |
+
|
2142 |
+
$thisMessage[$partName] = array(
|
2143 |
+
'name' => $partName,
|
2144 |
+
'type' => $typeName,
|
2145 |
+
'namespace' => $nsPrefix
|
2146 |
+
);
|
2147 |
+
}
|
2148 |
+
}
|
2149 |
+
}
|
2150 |
+
}
|
2151 |
+
|
2152 |
+
// Populate tree with portType information
|
2153 |
+
// XXX Current implementation only supports one portType that
|
2154 |
+
// encompasses all of the operations available.
|
2155 |
+
// *** <wsdl:portType> ***
|
2156 |
+
if (!isset($this->wsdl->portTypes[$service_name . 'Port'])) {
|
2157 |
+
$this->wsdl->portTypes[$service_name . 'Port'] = array();
|
2158 |
+
}
|
2159 |
+
$thisPortType =& $this->wsdl->portTypes[$service_name . 'Port'];
|
2160 |
+
|
2161 |
+
foreach ($object->__dispatch_map as $operationName => $messages) {
|
2162 |
+
$thisPortType[$operationName] = array('name' => $operationName);
|
2163 |
+
|
2164 |
+
foreach ($messages as $messageType => $messageParts) {
|
2165 |
+
switch ($messageType) {
|
2166 |
+
case 'in':
|
2167 |
+
$thisPortType[$operationName]['input'] = array(
|
2168 |
+
'message' => $operationName . 'Request',
|
2169 |
+
'namespace' => $this->tnsPrefix);
|
2170 |
+
break;
|
2171 |
+
|
2172 |
+
case 'out':
|
2173 |
+
$thisPortType[$operationName]['output'] = array(
|
2174 |
+
'message' => $operationName . 'Response',
|
2175 |
+
'namespace' => $this->tnsPrefix);
|
2176 |
+
break;
|
2177 |
+
}
|
2178 |
+
}
|
2179 |
+
}
|
2180 |
+
|
2181 |
+
return true;
|
2182 |
+
}
|
2183 |
+
|
2184 |
+
/**
|
2185 |
+
* Takes all the abstract WSDL data and builds concrete bindings and
|
2186 |
+
* services (destructive).
|
2187 |
+
*
|
2188 |
+
* @access private
|
2189 |
+
* @todo Current implementation discards $service_desc.
|
2190 |
+
*
|
2191 |
+
* @param string $schemaNamespace Namespace for types etc.
|
2192 |
+
* @param string $service_name Name of the WSDL <service>.
|
2193 |
+
* @param string $service_desc Optional description of the WSDL
|
2194 |
+
* <service>.
|
2195 |
+
*/
|
2196 |
+
function _generateBindingsAndServices($schemaNamespace, $service_name,
|
2197 |
+
$service_desc = '')
|
2198 |
+
{
|
2199 |
+
// Populate tree with bindings information
|
2200 |
+
// XXX Current implementation only supports one binding that
|
2201 |
+
// matches the single portType and all of its operations.
|
2202 |
+
// XXX Is this the correct use of $schemaNamespace here?
|
2203 |
+
// *** <wsdl:binding> ***
|
2204 |
+
$this->wsdl->bindings[$service_name . 'Binding'] = array(
|
2205 |
+
'type' => $service_name . 'Port',
|
2206 |
+
'namespace' => $this->tnsPrefix,
|
2207 |
+
'style' => 'rpc',
|
2208 |
+
'transport' => SCHEMA_SOAP_HTTP,
|
2209 |
+
'operations' => array());
|
2210 |
+
$thisBinding =& $this->wsdl->bindings[$service_name . 'Binding'];
|
2211 |
+
|
2212 |
+
foreach ($this->wsdl->portTypes[$service_name . 'Port'] as $operationName => $operationData) {
|
2213 |
+
$thisBinding['operations'][$operationName] = array(
|
2214 |
+
'soapAction' => $schemaNamespace . '#' . $operationName,
|
2215 |
+
'style' => $thisBinding['style']);
|
2216 |
+
|
2217 |
+
foreach (array('input', 'output') as $messageType)
|
2218 |
+
if (isset($operationData[$messageType])) {
|
2219 |
+
$thisBinding['operations'][$operationName][$messageType] = array(
|
2220 |
+
'use' => 'encoded',
|
2221 |
+
'namespace' => $schemaNamespace,
|
2222 |
+
'encodingStyle' => SOAP_SCHEMA_ENCODING);
|
2223 |
+
}
|
2224 |
+
}
|
2225 |
+
|
2226 |
+
// Populate tree with service information
|
2227 |
+
// XXX Current implementation supports one service which groups
|
2228 |
+
// all of the ports together, one port per binding
|
2229 |
+
// *** <wsdl:service> ***
|
2230 |
+
|
2231 |
+
$this->wsdl->services[$service_name . 'Service'] = array('ports' => array());
|
2232 |
+
$thisService =& $this->wsdl->services[$service_name . 'Service']['ports'];
|
2233 |
+
$https = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) ||
|
2234 |
+
getenv('SSL_PROTOCOL_VERSION');
|
2235 |
+
|
2236 |
+
foreach ($this->wsdl->bindings as $bindingName => $bindingData) {
|
2237 |
+
$thisService[$bindingData['type']] = array(
|
2238 |
+
'name' => $bindingData['type'],
|
2239 |
+
'binding' => $bindingName,
|
2240 |
+
'namespace' => $this->tnsPrefix,
|
2241 |
+
'address' => array('location' =>
|
2242 |
+
($https ? 'https://' : 'http://') .
|
2243 |
+
$_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] .
|
2244 |
+
(isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')),
|
2245 |
+
'type' => 'soap');
|
2246 |
+
}
|
2247 |
+
|
2248 |
+
// Set service
|
2249 |
+
$this->wsdl->set_service($service_name . 'Service');
|
2250 |
+
$this->wsdl->uri = $this->wsdl->namespaces[$this->tnsPrefix];
|
2251 |
+
|
2252 |
+
// Create WSDL definition
|
2253 |
+
// *** <wsdl:definitions> ***
|
2254 |
+
|
2255 |
+
$this->wsdl->definition = array(
|
2256 |
+
'name' => $service_name,
|
2257 |
+
'targetNamespace' => $this->wsdl->namespaces[$this->tnsPrefix],
|
2258 |
+
'xmlns' => SCHEMA_WSDL);
|
2259 |
+
|
2260 |
+
foreach ($this->wsdl->namespaces as $nsPrefix => $namespace) {
|
2261 |
+
$this->wsdl->definition['xmlns:' . $nsPrefix] = $namespace;
|
2262 |
+
}
|
2263 |
+
}
|
2264 |
+
|
2265 |
+
/**
|
2266 |
+
* This function is adapted from Dmitri V's implementation of
|
2267 |
+
* DISCO/WSDL generation. It separates namespace from type name in
|
2268 |
+
* a __typedef key and creates a new namespace entry in the WSDL
|
2269 |
+
* structure if the namespace has not been used before. The
|
2270 |
+
* namespace prefix and type name are returned. If no namespace is
|
2271 |
+
* specified, xsd is assumed.
|
2272 |
+
*
|
2273 |
+
* We will not need this function anymore once __typedef is
|
2274 |
+
* eliminated.
|
2275 |
+
*/
|
2276 |
+
function _getTypeNs($type)
|
2277 |
+
{
|
2278 |
+
preg_match_all('/\{(.*)\}/sm', $type, $m);
|
2279 |
+
if (!empty($m[1][0])) {
|
2280 |
+
if (!isset($this->wsdl->ns[$m[1][0]])) {
|
2281 |
+
$ns_pref = 'ns' . count($this->wsdl->namespaces);
|
2282 |
+
$this->wsdl->ns[$m[1][0]] = $ns_pref;
|
2283 |
+
$this->wsdl->namespaces[$ns_pref] = $m[1][0];
|
2284 |
+
}
|
2285 |
+
$typens = $this->wsdl->ns[$m[1][0]];
|
2286 |
+
$type = str_replace($m[0][0], '', $type);
|
2287 |
+
} else {
|
2288 |
+
$typens = 'xsd';
|
2289 |
+
}
|
2290 |
+
|
2291 |
+
return array($typens, $type);
|
2292 |
+
}
|
2293 |
+
|
2294 |
+
}
|
lib/PEAR/XML/Parser.php
ADDED
@@ -0,0 +1,768 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
4 |
+
|
5 |
+
/**
|
6 |
+
* XML_Parser
|
7 |
+
*
|
8 |
+
* XML Parser package
|
9 |
+
*
|
10 |
+
* PHP versions 4 and 5
|
11 |
+
*
|
12 |
+
* LICENSE:
|
13 |
+
*
|
14 |
+
* Copyright (c) 2002-2008 The PHP Group
|
15 |
+
* All rights reserved.
|
16 |
+
*
|
17 |
+
* Redistribution and use in source and binary forms, with or without
|
18 |
+
* modification, are permitted provided that the following conditions
|
19 |
+
* are met:
|
20 |
+
*
|
21 |
+
* * Redistributions of source code must retain the above copyright
|
22 |
+
* notice, this list of conditions and the following disclaimer.
|
23 |
+
* * Redistributions in binary form must reproduce the above copyright
|
24 |
+
* notice, this list of conditions and the following disclaimer in the
|
25 |
+
* documentation and/or other materials provided with the distribution.
|
26 |
+
* * The name of the author may not be used to endorse or promote products
|
27 |
+
* derived from this software without specific prior written permission.
|
28 |
+
*
|
29 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
30 |
+
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
31 |
+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
32 |
+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
33 |
+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
34 |
+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
35 |
+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
36 |
+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
37 |
+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
38 |
+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
39 |
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
40 |
+
*
|
41 |
+
* @category XML
|
42 |
+
* @package XML_Parser
|
43 |
+
* @author Stig Bakken <ssb@fast.no>
|
44 |
+
* @author Tomas V.V.Cox <cox@idecnet.com>
|
45 |
+
* @author Stephan Schmidt <schst@php.net>
|
46 |
+
* @copyright 2002-2008 The PHP Group
|
47 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
48 |
+
* @version CVS: $Id: Parser.php,v 1.30 2008/09/16 16:06:22 ashnazg Exp $
|
49 |
+
* @link http://pear.php.net/package/XML_Parser
|
50 |
+
*/
|
51 |
+
|
52 |
+
/**
|
53 |
+
* uses PEAR's error handling
|
54 |
+
*/
|
55 |
+
require_once 'PEAR.php';
|
56 |
+
|
57 |
+
/**
|
58 |
+
* resource could not be created
|
59 |
+
*/
|
60 |
+
define('XML_PARSER_ERROR_NO_RESOURCE', 200);
|
61 |
+
|
62 |
+
/**
|
63 |
+
* unsupported mode
|
64 |
+
*/
|
65 |
+
define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
|
66 |
+
|
67 |
+
/**
|
68 |
+
* invalid encoding was given
|
69 |
+
*/
|
70 |
+
define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
|
71 |
+
|
72 |
+
/**
|
73 |
+
* specified file could not be read
|
74 |
+
*/
|
75 |
+
define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
|
76 |
+
|
77 |
+
/**
|
78 |
+
* invalid input
|
79 |
+
*/
|
80 |
+
define('XML_PARSER_ERROR_INVALID_INPUT', 204);
|
81 |
+
|
82 |
+
/**
|
83 |
+
* remote file cannot be retrieved in safe mode
|
84 |
+
*/
|
85 |
+
define('XML_PARSER_ERROR_REMOTE', 205);
|
86 |
+
|
87 |
+
/**
|
88 |
+
* XML Parser class.
|
89 |
+
*
|
90 |
+
* This is an XML parser based on PHP's "xml" extension,
|
91 |
+
* based on the bundled expat library.
|
92 |
+
*
|
93 |
+
* Notes:
|
94 |
+
* - It requires PHP 4.0.4pl1 or greater
|
95 |
+
* - From revision 1.17, the function names used by the 'func' mode
|
96 |
+
* are in the format "xmltag_$elem", for example: use "xmltag_name"
|
97 |
+
* to handle the <name></name> tags of your xml file.
|
98 |
+
* - different parsing modes
|
99 |
+
*
|
100 |
+
* @category XML
|
101 |
+
* @package XML_Parser
|
102 |
+
* @author Stig Bakken <ssb@fast.no>
|
103 |
+
* @author Tomas V.V.Cox <cox@idecnet.com>
|
104 |
+
* @author Stephan Schmidt <schst@php.net>
|
105 |
+
* @copyright 2002-2008 The PHP Group
|
106 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
107 |
+
* @version Release: @package_version@
|
108 |
+
* @link http://pear.php.net/package/XML_Parser
|
109 |
+
* @todo create XML_Parser_Namespace to parse documents with namespaces
|
110 |
+
* @todo create XML_Parser_Pull
|
111 |
+
* @todo Tests that need to be made:
|
112 |
+
* - mixing character encodings
|
113 |
+
* - a test using all expat handlers
|
114 |
+
* - options (folding, output charset)
|
115 |
+
*/
|
116 |
+
class XML_Parser extends PEAR
|
117 |
+
{
|
118 |
+
// {{{ properties
|
119 |
+
|
120 |
+
/**
|
121 |
+
* XML parser handle
|
122 |
+
*
|
123 |
+
* @var resource
|
124 |
+
* @see xml_parser_create()
|
125 |
+
*/
|
126 |
+
var $parser;
|
127 |
+
|
128 |
+
/**
|
129 |
+
* File handle if parsing from a file
|
130 |
+
*
|
131 |
+
* @var resource
|
132 |
+
*/
|
133 |
+
var $fp;
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Whether to do case folding
|
137 |
+
*
|
138 |
+
* If set to true, all tag and attribute names will
|
139 |
+
* be converted to UPPER CASE.
|
140 |
+
*
|
141 |
+
* @var boolean
|
142 |
+
*/
|
143 |
+
var $folding = true;
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Mode of operation, one of "event" or "func"
|
147 |
+
*
|
148 |
+
* @var string
|
149 |
+
*/
|
150 |
+
var $mode;
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Mapping from expat handler function to class method.
|
154 |
+
*
|
155 |
+
* @var array
|
156 |
+
*/
|
157 |
+
var $handler = array(
|
158 |
+
'character_data_handler' => 'cdataHandler',
|
159 |
+
'default_handler' => 'defaultHandler',
|
160 |
+
'processing_instruction_handler' => 'piHandler',
|
161 |
+
'unparsed_entity_decl_handler' => 'unparsedHandler',
|
162 |
+
'notation_decl_handler' => 'notationHandler',
|
163 |
+
'external_entity_ref_handler' => 'entityrefHandler'
|
164 |
+
);
|
165 |
+
|
166 |
+
/**
|
167 |
+
* source encoding
|
168 |
+
*
|
169 |
+
* @var string
|
170 |
+
*/
|
171 |
+
var $srcenc;
|
172 |
+
|
173 |
+
/**
|
174 |
+
* target encoding
|
175 |
+
*
|
176 |
+
* @var string
|
177 |
+
*/
|
178 |
+
var $tgtenc;
|
179 |
+
|
180 |
+
/**
|
181 |
+
* handler object
|
182 |
+
*
|
183 |
+
* @var object
|
184 |
+
*/
|
185 |
+
var $_handlerObj;
|
186 |
+
|
187 |
+
/**
|
188 |
+
* valid encodings
|
189 |
+
*
|
190 |
+
* @var array
|
191 |
+
*/
|
192 |
+
var $_validEncodings = array('ISO-8859-1', 'UTF-8', 'US-ASCII');
|
193 |
+
|
194 |
+
// }}}
|
195 |
+
// {{{ php4 constructor
|
196 |
+
|
197 |
+
/**
|
198 |
+
* Creates an XML parser.
|
199 |
+
*
|
200 |
+
* This is needed for PHP4 compatibility, it will
|
201 |
+
* call the constructor, when a new instance is created.
|
202 |
+
*
|
203 |
+
* @param string $srcenc source charset encoding, use NULL (default) to use
|
204 |
+
* whatever the document specifies
|
205 |
+
* @param string $mode how this parser object should work, "event" for
|
206 |
+
* startelement/endelement-type events, "func"
|
207 |
+
* to have it call functions named after elements
|
208 |
+
* @param string $tgtenc a valid target encoding
|
209 |
+
*/
|
210 |
+
function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null)
|
211 |
+
{
|
212 |
+
XML_Parser::__construct($srcenc, $mode, $tgtenc);
|
213 |
+
}
|
214 |
+
// }}}
|
215 |
+
// {{{ php5 constructor
|
216 |
+
|
217 |
+
/**
|
218 |
+
* PHP5 constructor
|
219 |
+
*
|
220 |
+
* @param string $srcenc source charset encoding, use NULL (default) to use
|
221 |
+
* whatever the document specifies
|
222 |
+
* @param string $mode how this parser object should work, "event" for
|
223 |
+
* startelement/endelement-type events, "func"
|
224 |
+
* to have it call functions named after elements
|
225 |
+
* @param string $tgtenc a valid target encoding
|
226 |
+
*/
|
227 |
+
function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
|
228 |
+
{
|
229 |
+
$this->PEAR('XML_Parser_Error');
|
230 |
+
|
231 |
+
$this->mode = $mode;
|
232 |
+
$this->srcenc = $srcenc;
|
233 |
+
$this->tgtenc = $tgtenc;
|
234 |
+
}
|
235 |
+
// }}}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Sets the mode of the parser.
|
239 |
+
*
|
240 |
+
* Possible modes are:
|
241 |
+
* - func
|
242 |
+
* - event
|
243 |
+
*
|
244 |
+
* You can set the mode using the second parameter
|
245 |
+
* in the constructor.
|
246 |
+
*
|
247 |
+
* This method is only needed, when switching to a new
|
248 |
+
* mode at a later point.
|
249 |
+
*
|
250 |
+
* @param string $mode mode, either 'func' or 'event'
|
251 |
+
*
|
252 |
+
* @return boolean|object true on success, PEAR_Error otherwise
|
253 |
+
* @access public
|
254 |
+
*/
|
255 |
+
function setMode($mode)
|
256 |
+
{
|
257 |
+
if ($mode != 'func' && $mode != 'event') {
|
258 |
+
$this->raiseError('Unsupported mode given',
|
259 |
+
XML_PARSER_ERROR_UNSUPPORTED_MODE);
|
260 |
+
}
|
261 |
+
|
262 |
+
$this->mode = $mode;
|
263 |
+
return true;
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Sets the object, that will handle the XML events
|
268 |
+
*
|
269 |
+
* This allows you to create a handler object independent of the
|
270 |
+
* parser object that you are using and easily switch the underlying
|
271 |
+
* parser.
|
272 |
+
*
|
273 |
+
* If no object will be set, XML_Parser assumes that you
|
274 |
+
* extend this class and handle the events in $this.
|
275 |
+
*
|
276 |
+
* @param object &$obj object to handle the events
|
277 |
+
*
|
278 |
+
* @return boolean will always return true
|
279 |
+
* @access public
|
280 |
+
* @since v1.2.0beta3
|
281 |
+
*/
|
282 |
+
function setHandlerObj(&$obj)
|
283 |
+
{
|
284 |
+
$this->_handlerObj = &$obj;
|
285 |
+
return true;
|
286 |
+
}
|
287 |
+
|
288 |
+
/**
|
289 |
+
* Init the element handlers
|
290 |
+
*
|
291 |
+
* @return mixed
|
292 |
+
* @access private
|
293 |
+
*/
|
294 |
+
function _initHandlers()
|
295 |
+
{
|
296 |
+
if (!is_resource($this->parser)) {
|
297 |
+
return false;
|
298 |
+
}
|
299 |
+
|
300 |
+
if (!is_object($this->_handlerObj)) {
|
301 |
+
$this->_handlerObj = &$this;
|
302 |
+
}
|
303 |
+
switch ($this->mode) {
|
304 |
+
|
305 |
+
case 'func':
|
306 |
+
xml_set_object($this->parser, $this->_handlerObj);
|
307 |
+
xml_set_element_handler($this->parser,
|
308 |
+
array(&$this, 'funcStartHandler'), array(&$this, 'funcEndHandler'));
|
309 |
+
break;
|
310 |
+
|
311 |
+
case 'event':
|
312 |
+
xml_set_object($this->parser, $this->_handlerObj);
|
313 |
+
xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
|
314 |
+
break;
|
315 |
+
default:
|
316 |
+
return $this->raiseError('Unsupported mode given',
|
317 |
+
XML_PARSER_ERROR_UNSUPPORTED_MODE);
|
318 |
+
break;
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* set additional handlers for character data, entities, etc.
|
323 |
+
*/
|
324 |
+
foreach ($this->handler as $xml_func => $method) {
|
325 |
+
if (method_exists($this->_handlerObj, $method)) {
|
326 |
+
$xml_func = 'xml_set_' . $xml_func;
|
327 |
+
$xml_func($this->parser, $method);
|
328 |
+
}
|
329 |
+
}
|
330 |
+
}
|
331 |
+
|
332 |
+
// {{{ _create()
|
333 |
+
|
334 |
+
/**
|
335 |
+
* create the XML parser resource
|
336 |
+
*
|
337 |
+
* Has been moved from the constructor to avoid
|
338 |
+
* problems with object references.
|
339 |
+
*
|
340 |
+
* Furthermore it allows us returning an error
|
341 |
+
* if something fails.
|
342 |
+
*
|
343 |
+
* NOTE: uses '@' error suppresion in this method
|
344 |
+
*
|
345 |
+
* @return bool|PEAR_Error true on success, PEAR_Error otherwise
|
346 |
+
* @access private
|
347 |
+
* @see xml_parser_create
|
348 |
+
*/
|
349 |
+
function _create()
|
350 |
+
{
|
351 |
+
if ($this->srcenc === null) {
|
352 |
+
$xp = @xml_parser_create();
|
353 |
+
} else {
|
354 |
+
$xp = @xml_parser_create($this->srcenc);
|
355 |
+
}
|
356 |
+
if (is_resource($xp)) {
|
357 |
+
if ($this->tgtenc !== null) {
|
358 |
+
if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
|
359 |
+
$this->tgtenc)
|
360 |
+
) {
|
361 |
+
return $this->raiseError('invalid target encoding',
|
362 |
+
XML_PARSER_ERROR_INVALID_ENCODING);
|
363 |
+
}
|
364 |
+
}
|
365 |
+
$this->parser = $xp;
|
366 |
+
$result = $this->_initHandlers($this->mode);
|
367 |
+
if ($this->isError($result)) {
|
368 |
+
return $result;
|
369 |
+
}
|
370 |
+
xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
|
371 |
+
return true;
|
372 |
+
}
|
373 |
+
if (!in_array(strtoupper($this->srcenc), $this->_validEncodings)) {
|
374 |
+
return $this->raiseError('invalid source encoding',
|
375 |
+
XML_PARSER_ERROR_INVALID_ENCODING);
|
376 |
+
}
|
377 |
+
return $this->raiseError('Unable to create XML parser resource.',
|
378 |
+
XML_PARSER_ERROR_NO_RESOURCE);
|
379 |
+
}
|
380 |
+
|
381 |
+
// }}}
|
382 |
+
// {{{ reset()
|
383 |
+
|
384 |
+
/**
|
385 |
+
* Reset the parser.
|
386 |
+
*
|
387 |
+
* This allows you to use one parser instance
|
388 |
+
* to parse multiple XML documents.
|
389 |
+
*
|
390 |
+
* @access public
|
391 |
+
* @return boolean|object true on success, PEAR_Error otherwise
|
392 |
+
*/
|
393 |
+
function reset()
|
394 |
+
{
|
395 |
+
$result = $this->_create();
|
396 |
+
if ($this->isError($result)) {
|
397 |
+
return $result;
|
398 |
+
}
|
399 |
+
return true;
|
400 |
+
}
|
401 |
+
|
402 |
+
// }}}
|
403 |
+
// {{{ setInputFile()
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Sets the input xml file to be parsed
|
407 |
+
*
|
408 |
+
* @param string $file Filename (full path)
|
409 |
+
*
|
410 |
+
* @return resource fopen handle of the given file
|
411 |
+
* @access public
|
412 |
+
* @throws XML_Parser_Error
|
413 |
+
* @see setInput(), setInputString(), parse()
|
414 |
+
*/
|
415 |
+
function setInputFile($file)
|
416 |
+
{
|
417 |
+
/**
|
418 |
+
* check, if file is a remote file
|
419 |
+
*/
|
420 |
+
if (eregi('^(http|ftp)://', substr($file, 0, 10))) {
|
421 |
+
if (!ini_get('allow_url_fopen')) {
|
422 |
+
return $this->
|
423 |
+
raiseError('Remote files cannot be parsed, as safe mode is enabled.',
|
424 |
+
XML_PARSER_ERROR_REMOTE);
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
$fp = @fopen($file, 'rb');
|
429 |
+
if (is_resource($fp)) {
|
430 |
+
$this->fp = $fp;
|
431 |
+
return $fp;
|
432 |
+
}
|
433 |
+
return $this->raiseError('File could not be opened.',
|
434 |
+
XML_PARSER_ERROR_FILE_NOT_READABLE);
|
435 |
+
}
|
436 |
+
|
437 |
+
// }}}
|
438 |
+
// {{{ setInputString()
|
439 |
+
|
440 |
+
/**
|
441 |
+
* XML_Parser::setInputString()
|
442 |
+
*
|
443 |
+
* Sets the xml input from a string
|
444 |
+
*
|
445 |
+
* @param string $data a string containing the XML document
|
446 |
+
*
|
447 |
+
* @return null
|
448 |
+
*/
|
449 |
+
function setInputString($data)
|
450 |
+
{
|
451 |
+
$this->fp = $data;
|
452 |
+
return null;
|
453 |
+
}
|
454 |
+
|
455 |
+
// }}}
|
456 |
+
// {{{ setInput()
|
457 |
+
|
458 |
+
/**
|
459 |
+
* Sets the file handle to use with parse().
|
460 |
+
*
|
461 |
+
* You should use setInputFile() or setInputString() if you
|
462 |
+
* pass a string
|
463 |
+
*
|
464 |
+
* @param mixed $fp Can be either a resource returned from fopen(),
|
465 |
+
* a URL, a local filename or a string.
|
466 |
+
*
|
467 |
+
* @return mixed
|
468 |
+
* @access public
|
469 |
+
* @see parse()
|
470 |
+
* @uses setInputString(), setInputFile()
|
471 |
+
*/
|
472 |
+
function setInput($fp)
|
473 |
+
{
|
474 |
+
if (is_resource($fp)) {
|
475 |
+
$this->fp = $fp;
|
476 |
+
return true;
|
477 |
+
} elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) {
|
478 |
+
// see if it's an absolute URL (has a scheme at the beginning)
|
479 |
+
return $this->setInputFile($fp);
|
480 |
+
} elseif (file_exists($fp)) {
|
481 |
+
// see if it's a local file
|
482 |
+
return $this->setInputFile($fp);
|
483 |
+
} else {
|
484 |
+
// it must be a string
|
485 |
+
$this->fp = $fp;
|
486 |
+
return true;
|
487 |
+
}
|
488 |
+
|
489 |
+
return $this->raiseError('Illegal input format',
|
490 |
+
XML_PARSER_ERROR_INVALID_INPUT);
|
491 |
+
}
|
492 |
+
|
493 |
+
// }}}
|
494 |
+
// {{{ parse()
|
495 |
+
|
496 |
+
/**
|
497 |
+
* Central parsing function.
|
498 |
+
*
|
499 |
+
* @return bool|PEAR_Error returns true on success, or a PEAR_Error otherwise
|
500 |
+
* @access public
|
501 |
+
*/
|
502 |
+
function parse()
|
503 |
+
{
|
504 |
+
/**
|
505 |
+
* reset the parser
|
506 |
+
*/
|
507 |
+
$result = $this->reset();
|
508 |
+
if ($this->isError($result)) {
|
509 |
+
return $result;
|
510 |
+
}
|
511 |
+
// if $this->fp was fopened previously
|
512 |
+
if (is_resource($this->fp)) {
|
513 |
+
|
514 |
+
while ($data = fread($this->fp, 4096)) {
|
515 |
+
if (!$this->_parseString($data, feof($this->fp))) {
|
516 |
+
$error = &$this->raiseError();
|
517 |
+
$this->free();
|
518 |
+
return $error;
|
519 |
+
}
|
520 |
+
}
|
521 |
+
} else {
|
522 |
+
// otherwise, $this->fp must be a string
|
523 |
+
if (!$this->_parseString($this->fp, true)) {
|
524 |
+
$error = &$this->raiseError();
|
525 |
+
$this->free();
|
526 |
+
return $error;
|
527 |
+
}
|
528 |
+
}
|
529 |
+
$this->free();
|
530 |
+
|
531 |
+
return true;
|
532 |
+
}
|
533 |
+
|
534 |
+
/**
|
535 |
+
* XML_Parser::_parseString()
|
536 |
+
*
|
537 |
+
* @param string $data data
|
538 |
+
* @param bool $eof end-of-file flag
|
539 |
+
*
|
540 |
+
* @return bool
|
541 |
+
* @access private
|
542 |
+
* @see parseString()
|
543 |
+
**/
|
544 |
+
function _parseString($data, $eof = false)
|
545 |
+
{
|
546 |
+
return xml_parse($this->parser, $data, $eof);
|
547 |
+
}
|
548 |
+
|
549 |
+
// }}}
|
550 |
+
// {{{ parseString()
|
551 |
+
|
552 |
+
/**
|
553 |
+
* XML_Parser::parseString()
|
554 |
+
*
|
555 |
+
* Parses a string.
|
556 |
+
*
|
557 |
+
* @param string $data XML data
|
558 |
+
* @param boolean $eof If set and TRUE, data is the last piece
|
559 |
+
* of data sent in this parser
|
560 |
+
*
|
561 |
+
* @return bool|PEAR_Error true on success or a PEAR Error
|
562 |
+
* @throws XML_Parser_Error
|
563 |
+
* @see _parseString()
|
564 |
+
*/
|
565 |
+
function parseString($data, $eof = false)
|
566 |
+
{
|
567 |
+
if (!isset($this->parser) || !is_resource($this->parser)) {
|
568 |
+
$this->reset();
|
569 |
+
}
|
570 |
+
|
571 |
+
if (!$this->_parseString($data, $eof)) {
|
572 |
+
$error = &$this->raiseError();
|
573 |
+
$this->free();
|
574 |
+
return $error;
|
575 |
+
}
|
576 |
+
|
577 |
+
if ($eof === true) {
|
578 |
+
$this->free();
|
579 |
+
}
|
580 |
+
return true;
|
581 |
+
}
|
582 |
+
|
583 |
+
/**
|
584 |
+
* XML_Parser::free()
|
585 |
+
*
|
586 |
+
* Free the internal resources associated with the parser
|
587 |
+
*
|
588 |
+
* @return null
|
589 |
+
**/
|
590 |
+
function free()
|
591 |
+
{
|
592 |
+
if (isset($this->parser) && is_resource($this->parser)) {
|
593 |
+
xml_parser_free($this->parser);
|
594 |
+
unset( $this->parser );
|
595 |
+
}
|
596 |
+
if (isset($this->fp) && is_resource($this->fp)) {
|
597 |
+
fclose($this->fp);
|
598 |
+
}
|
599 |
+
unset($this->fp);
|
600 |
+
return null;
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* XML_Parser::raiseError()
|
605 |
+
*
|
606 |
+
* Throws a XML_Parser_Error
|
607 |
+
*
|
608 |
+
* @param string $msg the error message
|
609 |
+
* @param integer $ecode the error message code
|
610 |
+
*
|
611 |
+
* @return XML_Parser_Error reference to the error object
|
612 |
+
**/
|
613 |
+
function &raiseError($msg = null, $ecode = 0)
|
614 |
+
{
|
615 |
+
$msg = !is_null($msg) ? $msg : $this->parser;
|
616 |
+
$err = &new XML_Parser_Error($msg, $ecode);
|
617 |
+
return parent::raiseError($err);
|
618 |
+
}
|
619 |
+
|
620 |
+
// }}}
|
621 |
+
// {{{ funcStartHandler()
|
622 |
+
|
623 |
+
/**
|
624 |
+
* derives and calls the Start Handler function
|
625 |
+
*
|
626 |
+
* @param mixed $xp ??
|
627 |
+
* @param mixed $elem ??
|
628 |
+
* @param mixed $attribs ??
|
629 |
+
*
|
630 |
+
* @return void
|
631 |
+
*/
|
632 |
+
function funcStartHandler($xp, $elem, $attribs)
|
633 |
+
{
|
634 |
+
$func = 'xmltag_' . $elem;
|
635 |
+
$func = str_replace(array('.', '-', ':'), '_', $func);
|
636 |
+
if (method_exists($this->_handlerObj, $func)) {
|
637 |
+
call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
|
638 |
+
} elseif (method_exists($this->_handlerObj, 'xmltag')) {
|
639 |
+
call_user_func(array(&$this->_handlerObj, 'xmltag'),
|
640 |
+
$xp, $elem, $attribs);
|
641 |
+
}
|
642 |
+
}
|
643 |
+
|
644 |
+
// }}}
|
645 |
+
// {{{ funcEndHandler()
|
646 |
+
|
647 |
+
/**
|
648 |
+
* derives and calls the End Handler function
|
649 |
+
*
|
650 |
+
* @param mixed $xp ??
|
651 |
+
* @param mixed $elem ??
|
652 |
+
*
|
653 |
+
* @return void
|
654 |
+
*/
|
655 |
+
function funcEndHandler($xp, $elem)
|
656 |
+
{
|
657 |
+
$func = 'xmltag_' . $elem . '_';
|
658 |
+
$func = str_replace(array('.', '-', ':'), '_', $func);
|
659 |
+
if (method_exists($this->_handlerObj, $func)) {
|
660 |
+
call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
|
661 |
+
} elseif (method_exists($this->_handlerObj, 'xmltag_')) {
|
662 |
+
call_user_func(array(&$this->_handlerObj, 'xmltag_'), $xp, $elem);
|
663 |
+
}
|
664 |
+
}
|
665 |
+
|
666 |
+
// }}}
|
667 |
+
// {{{ startHandler()
|
668 |
+
|
669 |
+
/**
|
670 |
+
* abstract method signature for Start Handler
|
671 |
+
*
|
672 |
+
* @param mixed $xp ??
|
673 |
+
* @param mixed $elem ??
|
674 |
+
* @param mixed &$attribs ??
|
675 |
+
*
|
676 |
+
* @return null
|
677 |
+
* @abstract
|
678 |
+
*/
|
679 |
+
function startHandler($xp, $elem, &$attribs)
|
680 |
+
{
|
681 |
+
return null;
|
682 |
+
}
|
683 |
+
|
684 |
+
// }}}
|
685 |
+
// {{{ endHandler()
|
686 |
+
|
687 |
+
/**
|
688 |
+
* abstract method signature for End Handler
|
689 |
+
*
|
690 |
+
* @param mixed $xp ??
|
691 |
+
* @param mixed $elem ??
|
692 |
+
*
|
693 |
+
* @return null
|
694 |
+
* @abstract
|
695 |
+
*/
|
696 |
+
function endHandler($xp, $elem)
|
697 |
+
{
|
698 |
+
return null;
|
699 |
+
}
|
700 |
+
|
701 |
+
|
702 |
+
// }}}me
|
703 |
+
}
|
704 |
+
|
705 |
+
/**
|
706 |
+
* error class, replaces PEAR_Error
|
707 |
+
*
|
708 |
+
* An instance of this class will be returned
|
709 |
+
* if an error occurs inside XML_Parser.
|
710 |
+
*
|
711 |
+
* There are three advantages over using the standard PEAR_Error:
|
712 |
+
* - All messages will be prefixed
|
713 |
+
* - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
|
714 |
+
* - messages can be generated from the xml_parser resource
|
715 |
+
*
|
716 |
+
* @category XML
|
717 |
+
* @package XML_Parser
|
718 |
+
* @author Stig Bakken <ssb@fast.no>
|
719 |
+
* @author Tomas V.V.Cox <cox@idecnet.com>
|
720 |
+
* @author Stephan Schmidt <schst@php.net>
|
721 |
+
* @copyright 2002-2008 The PHP Group
|
722 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
723 |
+
* @version Release: @package_version@
|
724 |
+
* @link http://pear.php.net/package/XML_Parser
|
725 |
+
* @see PEAR_Error
|
726 |
+
*/
|
727 |
+
class XML_Parser_Error extends PEAR_Error
|
728 |
+
{
|
729 |
+
// {{{ properties
|
730 |
+
|
731 |
+
/**
|
732 |
+
* prefix for all messages
|
733 |
+
*
|
734 |
+
* @var string
|
735 |
+
*/
|
736 |
+
var $error_message_prefix = 'XML_Parser: ';
|
737 |
+
|
738 |
+
// }}}
|
739 |
+
// {{{ constructor()
|
740 |
+
/**
|
741 |
+
* construct a new error instance
|
742 |
+
*
|
743 |
+
* You may either pass a message or an xml_parser resource as first
|
744 |
+
* parameter. If a resource has been passed, the last error that
|
745 |
+
* happened will be retrieved and returned.
|
746 |
+
*
|
747 |
+
* @param string|resource $msgorparser message or parser resource
|
748 |
+
* @param integer $code error code
|
749 |
+
* @param integer $mode error handling
|
750 |
+
* @param integer $level error level
|
751 |
+
*
|
752 |
+
* @access public
|
753 |
+
* @todo PEAR CS - can't meet 85char line limit without arg refactoring
|
754 |
+
*/
|
755 |
+
function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
|
756 |
+
{
|
757 |
+
if (is_resource($msgorparser)) {
|
758 |
+
$code = xml_get_error_code($msgorparser);
|
759 |
+
$msgorparser = sprintf('%s at XML input line %d:%d',
|
760 |
+
xml_error_string($code),
|
761 |
+
xml_get_current_line_number($msgorparser),
|
762 |
+
xml_get_current_column_number($msgorparser));
|
763 |
+
}
|
764 |
+
$this->PEAR_Error($msgorparser, $code, $mode, $level);
|
765 |
+
}
|
766 |
+
// }}}
|
767 |
+
}
|
768 |
+
?>
|
lib/PEAR/XML/Parser/Simple.php
ADDED
@@ -0,0 +1,326 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
4 |
+
|
5 |
+
/**
|
6 |
+
* XML_Parser
|
7 |
+
*
|
8 |
+
* XML Parser's Simple parser class
|
9 |
+
*
|
10 |
+
* PHP versions 4 and 5
|
11 |
+
*
|
12 |
+
* LICENSE:
|
13 |
+
*
|
14 |
+
* Copyright (c) 2002-2008 The PHP Group
|
15 |
+
* All rights reserved.
|
16 |
+
*
|
17 |
+
* Redistribution and use in source and binary forms, with or without
|
18 |
+
* modification, are permitted provided that the following conditions
|
19 |
+
* are met:
|
20 |
+
*
|
21 |
+
* * Redistributions of source code must retain the above copyright
|
22 |
+
* notice, this list of conditions and the following disclaimer.
|
23 |
+
* * Redistributions in binary form must reproduce the above copyright
|
24 |
+
* notice, this list of conditions and the following disclaimer in the
|
25 |
+
* documentation and/or other materials provided with the distribution.
|
26 |
+
* * The name of the author may not be used to endorse or promote products
|
27 |
+
* derived from this software without specific prior written permission.
|
28 |
+
*
|
29 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
30 |
+
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
31 |
+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
32 |
+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
33 |
+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
34 |
+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
35 |
+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
36 |
+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
37 |
+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
38 |
+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
39 |
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
40 |
+
*
|
41 |
+
* @category XML
|
42 |
+
* @package XML_Parser
|
43 |
+
* @author Stephan Schmidt <schst@php.net>
|
44 |
+
* @copyright 2004-2008 Stephan Schmidt <schst@php.net>
|
45 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
46 |
+
* @version CVS: $Id: Simple.php,v 1.7 2008/08/24 21:48:21 ashnazg Exp $
|
47 |
+
* @link http://pear.php.net/package/XML_Parser
|
48 |
+
*/
|
49 |
+
|
50 |
+
/**
|
51 |
+
* built on XML_Parser
|
52 |
+
*/
|
53 |
+
require_once 'XML/Parser.php';
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Simple XML parser class.
|
57 |
+
*
|
58 |
+
* This class is a simplified version of XML_Parser.
|
59 |
+
* In most XML applications the real action is executed,
|
60 |
+
* when a closing tag is found.
|
61 |
+
*
|
62 |
+
* XML_Parser_Simple allows you to just implement one callback
|
63 |
+
* for each tag that will receive the tag with its attributes
|
64 |
+
* and CData.
|
65 |
+
*
|
66 |
+
* <code>
|
67 |
+
* require_once '../Parser/Simple.php';
|
68 |
+
*
|
69 |
+
* class myParser extends XML_Parser_Simple
|
70 |
+
* {
|
71 |
+
* function myParser()
|
72 |
+
* {
|
73 |
+
* $this->XML_Parser_Simple();
|
74 |
+
* }
|
75 |
+
*
|
76 |
+
* function handleElement($name, $attribs, $data)
|
77 |
+
* {
|
78 |
+
* printf('handle %s<br>', $name);
|
79 |
+
* }
|
80 |
+
* }
|
81 |
+
*
|
82 |
+
* $p = &new myParser();
|
83 |
+
*
|
84 |
+
* $result = $p->setInputFile('myDoc.xml');
|
85 |
+
* $result = $p->parse();
|
86 |
+
* </code>
|
87 |
+
*
|
88 |
+
* @category XML
|
89 |
+
* @package XML_Parser
|
90 |
+
* @author Stephan Schmidt <schst@php.net>
|
91 |
+
* @copyright 2004-2008 The PHP Group
|
92 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
93 |
+
* @version Release: @package_version@
|
94 |
+
* @link http://pear.php.net/package/XML_Parser
|
95 |
+
*/
|
96 |
+
class XML_Parser_Simple extends XML_Parser
|
97 |
+
{
|
98 |
+
/**
|
99 |
+
* element stack
|
100 |
+
*
|
101 |
+
* @access private
|
102 |
+
* @var array
|
103 |
+
*/
|
104 |
+
var $_elStack = array();
|
105 |
+
|
106 |
+
/**
|
107 |
+
* all character data
|
108 |
+
*
|
109 |
+
* @access private
|
110 |
+
* @var array
|
111 |
+
*/
|
112 |
+
var $_data = array();
|
113 |
+
|
114 |
+
/**
|
115 |
+
* element depth
|
116 |
+
*
|
117 |
+
* @access private
|
118 |
+
* @var integer
|
119 |
+
*/
|
120 |
+
var $_depth = 0;
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Mapping from expat handler function to class method.
|
124 |
+
*
|
125 |
+
* @var array
|
126 |
+
*/
|
127 |
+
var $handler = array(
|
128 |
+
'default_handler' => 'defaultHandler',
|
129 |
+
'processing_instruction_handler' => 'piHandler',
|
130 |
+
'unparsed_entity_decl_handler' => 'unparsedHandler',
|
131 |
+
'notation_decl_handler' => 'notationHandler',
|
132 |
+
'external_entity_ref_handler' => 'entityrefHandler'
|
133 |
+
);
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Creates an XML parser.
|
137 |
+
*
|
138 |
+
* This is needed for PHP4 compatibility, it will
|
139 |
+
* call the constructor, when a new instance is created.
|
140 |
+
*
|
141 |
+
* @param string $srcenc source charset encoding, use NULL (default) to use
|
142 |
+
* whatever the document specifies
|
143 |
+
* @param string $mode how this parser object should work, "event" for
|
144 |
+
* handleElement(), "func" to have it call functions
|
145 |
+
* named after elements (handleElement_$name())
|
146 |
+
* @param string $tgtenc a valid target encoding
|
147 |
+
*/
|
148 |
+
function XML_Parser_Simple($srcenc = null, $mode = 'event', $tgtenc = null)
|
149 |
+
{
|
150 |
+
$this->XML_Parser($srcenc, $mode, $tgtenc);
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* inits the handlers
|
155 |
+
*
|
156 |
+
* @return mixed
|
157 |
+
* @access private
|
158 |
+
*/
|
159 |
+
function _initHandlers()
|
160 |
+
{
|
161 |
+
if (!is_object($this->_handlerObj)) {
|
162 |
+
$this->_handlerObj = &$this;
|
163 |
+
}
|
164 |
+
|
165 |
+
if ($this->mode != 'func' && $this->mode != 'event') {
|
166 |
+
return $this->raiseError('Unsupported mode given',
|
167 |
+
XML_PARSER_ERROR_UNSUPPORTED_MODE);
|
168 |
+
}
|
169 |
+
xml_set_object($this->parser, $this->_handlerObj);
|
170 |
+
|
171 |
+
xml_set_element_handler($this->parser, array(&$this, 'startHandler'),
|
172 |
+
array(&$this, 'endHandler'));
|
173 |
+
xml_set_character_data_handler($this->parser, array(&$this, 'cdataHandler'));
|
174 |
+
|
175 |
+
/**
|
176 |
+
* set additional handlers for character data, entities, etc.
|
177 |
+
*/
|
178 |
+
foreach ($this->handler as $xml_func => $method) {
|
179 |
+
if (method_exists($this->_handlerObj, $method)) {
|
180 |
+
$xml_func = 'xml_set_' . $xml_func;
|
181 |
+
$xml_func($this->parser, $method);
|
182 |
+
}
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Reset the parser.
|
188 |
+
*
|
189 |
+
* This allows you to use one parser instance
|
190 |
+
* to parse multiple XML documents.
|
191 |
+
*
|
192 |
+
* @access public
|
193 |
+
* @return boolean|object true on success, PEAR_Error otherwise
|
194 |
+
*/
|
195 |
+
function reset()
|
196 |
+
{
|
197 |
+
$this->_elStack = array();
|
198 |
+
$this->_data = array();
|
199 |
+
$this->_depth = 0;
|
200 |
+
|
201 |
+
$result = $this->_create();
|
202 |
+
if ($this->isError($result)) {
|
203 |
+
return $result;
|
204 |
+
}
|
205 |
+
return true;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* start handler
|
210 |
+
*
|
211 |
+
* Pushes attributes and tagname onto a stack
|
212 |
+
*
|
213 |
+
* @param resource $xp xml parser resource
|
214 |
+
* @param string $elem element name
|
215 |
+
* @param array &$attribs attributes
|
216 |
+
*
|
217 |
+
* @return mixed
|
218 |
+
* @access private
|
219 |
+
* @final
|
220 |
+
*/
|
221 |
+
function startHandler($xp, $elem, &$attribs)
|
222 |
+
{
|
223 |
+
array_push($this->_elStack, array(
|
224 |
+
'name' => $elem,
|
225 |
+
'attribs' => $attribs
|
226 |
+
));
|
227 |
+
$this->_depth++;
|
228 |
+
$this->_data[$this->_depth] = '';
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* end handler
|
233 |
+
*
|
234 |
+
* Pulls attributes and tagname from a stack
|
235 |
+
*
|
236 |
+
* @param resource $xp xml parser resource
|
237 |
+
* @param string $elem element name
|
238 |
+
*
|
239 |
+
* @return mixed
|
240 |
+
* @access private
|
241 |
+
* @final
|
242 |
+
*/
|
243 |
+
function endHandler($xp, $elem)
|
244 |
+
{
|
245 |
+
$el = array_pop($this->_elStack);
|
246 |
+
$data = $this->_data[$this->_depth];
|
247 |
+
$this->_depth--;
|
248 |
+
|
249 |
+
switch ($this->mode) {
|
250 |
+
case 'event':
|
251 |
+
$this->_handlerObj->handleElement($el['name'], $el['attribs'], $data);
|
252 |
+
break;
|
253 |
+
case 'func':
|
254 |
+
$func = 'handleElement_' . $elem;
|
255 |
+
if (strchr($func, '.')) {
|
256 |
+
$func = str_replace('.', '_', $func);
|
257 |
+
}
|
258 |
+
if (method_exists($this->_handlerObj, $func)) {
|
259 |
+
call_user_func(array(&$this->_handlerObj, $func),
|
260 |
+
$el['name'], $el['attribs'], $data);
|
261 |
+
}
|
262 |
+
break;
|
263 |
+
}
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* handle character data
|
268 |
+
*
|
269 |
+
* @param resource $xp xml parser resource
|
270 |
+
* @param string $data data
|
271 |
+
*
|
272 |
+
* @return void
|
273 |
+
* @access private
|
274 |
+
* @final
|
275 |
+
*/
|
276 |
+
function cdataHandler($xp, $data)
|
277 |
+
{
|
278 |
+
$this->_data[$this->_depth] .= $data;
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* handle a tag
|
283 |
+
*
|
284 |
+
* Implement this in your parser
|
285 |
+
*
|
286 |
+
* @param string $name element name
|
287 |
+
* @param array $attribs attributes
|
288 |
+
* @param string $data character data
|
289 |
+
*
|
290 |
+
* @return void
|
291 |
+
* @access public
|
292 |
+
* @abstract
|
293 |
+
*/
|
294 |
+
function handleElement($name, $attribs, $data)
|
295 |
+
{
|
296 |
+
}
|
297 |
+
|
298 |
+
/**
|
299 |
+
* get the current tag depth
|
300 |
+
*
|
301 |
+
* The root tag is in depth 0.
|
302 |
+
*
|
303 |
+
* @access public
|
304 |
+
* @return integer
|
305 |
+
*/
|
306 |
+
function getCurrentDepth()
|
307 |
+
{
|
308 |
+
return $this->_depth;
|
309 |
+
}
|
310 |
+
|
311 |
+
/**
|
312 |
+
* add some string to the current ddata.
|
313 |
+
*
|
314 |
+
* This is commonly needed, when a document is parsed recursively.
|
315 |
+
*
|
316 |
+
* @param string $data data to add
|
317 |
+
*
|
318 |
+
* @return void
|
319 |
+
* @access public
|
320 |
+
*/
|
321 |
+
function addToData($data)
|
322 |
+
{
|
323 |
+
$this->_data[$this->_depth] .= $data;
|
324 |
+
}
|
325 |
+
}
|
326 |
+
?>
|
lib/PEAR/XML/Serializer.php
ADDED
@@ -0,0 +1,1222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?PHP
|
2 |
+
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
3 |
+
|
4 |
+
/**
|
5 |
+
* XML_Serializer
|
6 |
+
*
|
7 |
+
* Creates XML documents from PHP data structures like arrays, objects or scalars.
|
8 |
+
*
|
9 |
+
* PHP versions 4 and 5
|
10 |
+
*
|
11 |
+
* LICENSE:
|
12 |
+
*
|
13 |
+
* Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
|
14 |
+
* All rights reserved.
|
15 |
+
*
|
16 |
+
* Redistribution and use in source and binary forms, with or without
|
17 |
+
* modification, are permitted provided that the following conditions
|
18 |
+
* are met:
|
19 |
+
*
|
20 |
+
* * Redistributions of source code must retain the above copyright
|
21 |
+
* notice, this list of conditions and the following disclaimer.
|
22 |
+
* * Redistributions in binary form must reproduce the above copyright
|
23 |
+
* notice, this list of conditions and the following disclaimer in the
|
24 |
+
* documentation and/or other materials provided with the distribution.
|
25 |
+
* * The name of the author may not be used to endorse or promote products
|
26 |
+
* derived from this software without specific prior written permission.
|
27 |
+
*
|
28 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
29 |
+
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
30 |
+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
31 |
+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
32 |
+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
33 |
+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
34 |
+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
35 |
+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
36 |
+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
37 |
+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
38 |
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
39 |
+
*
|
40 |
+
* @category XML
|
41 |
+
* @package XML_Serializer
|
42 |
+
* @author Stephan Schmidt <schst@php.net>
|
43 |
+
* @copyright 2003-2008 Stephan Schmidt <schst@php.net>
|
44 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
45 |
+
* @version CVS: $Id: Serializer.php,v 1.57 2009/01/25 03:51:11 ashnazg Exp $
|
46 |
+
* @link http://pear.php.net/package/XML_Serializer
|
47 |
+
* @see XML_Unserializer
|
48 |
+
*/
|
49 |
+
|
50 |
+
/**
|
51 |
+
* uses PEAR error management
|
52 |
+
*/
|
53 |
+
require_once 'PEAR.php';
|
54 |
+
|
55 |
+
/**
|
56 |
+
* uses XML_Util to create XML tags
|
57 |
+
*/
|
58 |
+
require_once 'XML/Util.php';
|
59 |
+
|
60 |
+
/**
|
61 |
+
* option: string used for indentation
|
62 |
+
*
|
63 |
+
* Possible values:
|
64 |
+
* - any string (default is any string)
|
65 |
+
*/
|
66 |
+
define('XML_SERIALIZER_OPTION_INDENT', 'indent');
|
67 |
+
|
68 |
+
/**
|
69 |
+
* option: string used for linebreaks
|
70 |
+
*
|
71 |
+
* Possible values:
|
72 |
+
* - any string (default is \n)
|
73 |
+
*/
|
74 |
+
define('XML_SERIALIZER_OPTION_LINEBREAKS', 'linebreak');
|
75 |
+
|
76 |
+
/**
|
77 |
+
* option: enable type hints
|
78 |
+
*
|
79 |
+
* Possible values:
|
80 |
+
* - true
|
81 |
+
* - false
|
82 |
+
*/
|
83 |
+
define('XML_SERIALIZER_OPTION_TYPEHINTS', 'typeHints');
|
84 |
+
|
85 |
+
/**
|
86 |
+
* option: add an XML declaration
|
87 |
+
*
|
88 |
+
* Possible values:
|
89 |
+
* - true
|
90 |
+
* - false
|
91 |
+
*/
|
92 |
+
define('XML_SERIALIZER_OPTION_XML_DECL_ENABLED', 'addDecl');
|
93 |
+
|
94 |
+
/**
|
95 |
+
* option: encoding of the document
|
96 |
+
*
|
97 |
+
* Possible values:
|
98 |
+
* - any valid encoding
|
99 |
+
* - null (default)
|
100 |
+
*/
|
101 |
+
define('XML_SERIALIZER_OPTION_XML_ENCODING', 'encoding');
|
102 |
+
|
103 |
+
/**
|
104 |
+
* option: default name for tags
|
105 |
+
*
|
106 |
+
* Possible values:
|
107 |
+
* - any string (XML_Serializer_Tag is default)
|
108 |
+
*/
|
109 |
+
define('XML_SERIALIZER_OPTION_DEFAULT_TAG', 'defaultTagName');
|
110 |
+
|
111 |
+
/**
|
112 |
+
* option: use classname for objects in indexed arrays
|
113 |
+
*
|
114 |
+
* Possible values:
|
115 |
+
* - true
|
116 |
+
* - false (default)
|
117 |
+
*/
|
118 |
+
define('XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME', 'classAsTagName');
|
119 |
+
|
120 |
+
/**
|
121 |
+
* option: attribute where original key is stored
|
122 |
+
*
|
123 |
+
* Possible values:
|
124 |
+
* - any string (default is _originalKey)
|
125 |
+
*/
|
126 |
+
define('XML_SERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
|
127 |
+
|
128 |
+
/**
|
129 |
+
* option: attribute for type (only if typeHints => true)
|
130 |
+
*
|
131 |
+
* Possible values:
|
132 |
+
* - any string (default is _type)
|
133 |
+
*/
|
134 |
+
define('XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
|
135 |
+
|
136 |
+
/**
|
137 |
+
* option: attribute for class (only if typeHints => true)
|
138 |
+
*
|
139 |
+
* Possible values:
|
140 |
+
* - any string (default is _class)
|
141 |
+
*/
|
142 |
+
define('XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
|
143 |
+
|
144 |
+
/**
|
145 |
+
* option: scalar values (strings, ints,..) will be serialized as attribute
|
146 |
+
*
|
147 |
+
* Possible values:
|
148 |
+
* - true
|
149 |
+
* - false (default)
|
150 |
+
* - array which sets this option on a per-tag basis
|
151 |
+
*/
|
152 |
+
define('XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES', 'scalarAsAttributes');
|
153 |
+
|
154 |
+
/**
|
155 |
+
* option: prepend string for attributes
|
156 |
+
*
|
157 |
+
* Possible values:
|
158 |
+
* - any string (default is any string)
|
159 |
+
*/
|
160 |
+
define('XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES', 'prependAttributes');
|
161 |
+
|
162 |
+
/**
|
163 |
+
* option: indent the attributes, if set to '_auto',
|
164 |
+
* it will indent attributes so they all start at the same column
|
165 |
+
*
|
166 |
+
* Possible values:
|
167 |
+
* - true
|
168 |
+
* - false (default)
|
169 |
+
* - '_auto'
|
170 |
+
*/
|
171 |
+
define('XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES', 'indentAttributes');
|
172 |
+
|
173 |
+
/**
|
174 |
+
* option: use 'simplexml' to use parent name as tagname
|
175 |
+
* if transforming an indexed array
|
176 |
+
*
|
177 |
+
* Possible values:
|
178 |
+
* - XML_SERIALIZER_MODE_DEFAULT (default)
|
179 |
+
* - XML_SERIALIZER_MODE_SIMPLEXML
|
180 |
+
*/
|
181 |
+
define('XML_SERIALIZER_OPTION_MODE', 'mode');
|
182 |
+
|
183 |
+
/**
|
184 |
+
* option: add a doctype declaration
|
185 |
+
*
|
186 |
+
* Possible values:
|
187 |
+
* - true
|
188 |
+
* - false (default)
|
189 |
+
*/
|
190 |
+
define('XML_SERIALIZER_OPTION_DOCTYPE_ENABLED', 'addDoctype');
|
191 |
+
|
192 |
+
/**
|
193 |
+
* option: supply a string or an array with id and uri
|
194 |
+
* ({@see XML_Util::getDoctypeDeclaration()}
|
195 |
+
*
|
196 |
+
* Possible values:
|
197 |
+
* - string
|
198 |
+
* - array
|
199 |
+
*/
|
200 |
+
define('XML_SERIALIZER_OPTION_DOCTYPE', 'doctype');
|
201 |
+
|
202 |
+
/**
|
203 |
+
* option: name of the root tag
|
204 |
+
*
|
205 |
+
* Possible values:
|
206 |
+
* - string
|
207 |
+
* - null (default)
|
208 |
+
*/
|
209 |
+
define('XML_SERIALIZER_OPTION_ROOT_NAME', 'rootName');
|
210 |
+
|
211 |
+
/**
|
212 |
+
* option: attributes of the root tag
|
213 |
+
*
|
214 |
+
* Possible values:
|
215 |
+
* - array
|
216 |
+
*/
|
217 |
+
define('XML_SERIALIZER_OPTION_ROOT_ATTRIBS', 'rootAttributes');
|
218 |
+
|
219 |
+
/**
|
220 |
+
* option: all values in this key will be treated as attributes
|
221 |
+
*
|
222 |
+
* Possible values:
|
223 |
+
* - string
|
224 |
+
*/
|
225 |
+
define('XML_SERIALIZER_OPTION_ATTRIBUTES_KEY', 'attributesArray');
|
226 |
+
|
227 |
+
/**
|
228 |
+
* option: this value will be used directly as content,
|
229 |
+
* instead of creating a new tag, may only be used
|
230 |
+
* in conjuction with attributesArray
|
231 |
+
*
|
232 |
+
* Possible values:
|
233 |
+
* - string
|
234 |
+
* - null (default)
|
235 |
+
*/
|
236 |
+
define('XML_SERIALIZER_OPTION_CONTENT_KEY', 'contentName');
|
237 |
+
|
238 |
+
/**
|
239 |
+
* option: this value will be used in a comment, instead of creating a new tag
|
240 |
+
*
|
241 |
+
* Possible values:
|
242 |
+
* - string
|
243 |
+
* - null (default)
|
244 |
+
*/
|
245 |
+
define('XML_SERIALIZER_OPTION_COMMENT_KEY', 'commentName');
|
246 |
+
|
247 |
+
/**
|
248 |
+
* option: tag names that will be changed
|
249 |
+
*
|
250 |
+
* Possible values:
|
251 |
+
* - array
|
252 |
+
*/
|
253 |
+
define('XML_SERIALIZER_OPTION_TAGMAP', 'tagMap');
|
254 |
+
|
255 |
+
/**
|
256 |
+
* option: function that will be applied before serializing
|
257 |
+
*
|
258 |
+
* Possible values:
|
259 |
+
* - any valid PHP callback
|
260 |
+
*/
|
261 |
+
define('XML_SERIALIZER_OPTION_ENCODE_FUNC', 'encodeFunction');
|
262 |
+
|
263 |
+
/**
|
264 |
+
* option: namespace to use for the document
|
265 |
+
*
|
266 |
+
* Possible values:
|
267 |
+
* - string
|
268 |
+
* - null (default)
|
269 |
+
*/
|
270 |
+
define('XML_SERIALIZER_OPTION_NAMESPACE', 'namespace');
|
271 |
+
|
272 |
+
/**
|
273 |
+
* option: type of entities to replace
|
274 |
+
*
|
275 |
+
* Possible values:
|
276 |
+
* - XML_SERIALIZER_ENTITIES_NONE
|
277 |
+
* - XML_SERIALIZER_ENTITIES_XML (default)
|
278 |
+
* - XML_SERIALIZER_ENTITIES_XML_REQUIRED
|
279 |
+
* - XML_SERIALIZER_ENTITIES_HTML
|
280 |
+
*/
|
281 |
+
define('XML_SERIALIZER_OPTION_ENTITIES', 'replaceEntities');
|
282 |
+
|
283 |
+
/**
|
284 |
+
* option: whether to return the result of the serialization from serialize()
|
285 |
+
*
|
286 |
+
* Possible values:
|
287 |
+
* - true
|
288 |
+
* - false (default)
|
289 |
+
*/
|
290 |
+
define('XML_SERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
|
291 |
+
|
292 |
+
/**
|
293 |
+
* option: whether to ignore properties that are set to null
|
294 |
+
*
|
295 |
+
* Possible values:
|
296 |
+
* - true
|
297 |
+
* - false (default)
|
298 |
+
*/
|
299 |
+
define('XML_SERIALIZER_OPTION_IGNORE_NULL', 'ignoreNull');
|
300 |
+
|
301 |
+
/**
|
302 |
+
* option: whether to use cdata sections for character data
|
303 |
+
*
|
304 |
+
* Possible values:
|
305 |
+
* - true
|
306 |
+
* - false (default)
|
307 |
+
*/
|
308 |
+
define('XML_SERIALIZER_OPTION_CDATA_SECTIONS', 'cdata');
|
309 |
+
|
310 |
+
|
311 |
+
/**
|
312 |
+
* default mode
|
313 |
+
*/
|
314 |
+
define('XML_SERIALIZER_MODE_DEFAULT', 'default');
|
315 |
+
|
316 |
+
/**
|
317 |
+
* SimpleXML mode
|
318 |
+
*
|
319 |
+
* When serializing indexed arrays, the key of the parent value is used as a tagname.
|
320 |
+
*/
|
321 |
+
define('XML_SERIALIZER_MODE_SIMPLEXML', 'simplexml');
|
322 |
+
|
323 |
+
/**
|
324 |
+
* error code for no serialization done
|
325 |
+
*/
|
326 |
+
define('XML_SERIALIZER_ERROR_NO_SERIALIZATION', 51);
|
327 |
+
|
328 |
+
/**
|
329 |
+
* do not replace entitites
|
330 |
+
*/
|
331 |
+
define('XML_SERIALIZER_ENTITIES_NONE', XML_UTIL_ENTITIES_NONE);
|
332 |
+
|
333 |
+
/**
|
334 |
+
* replace all XML entitites
|
335 |
+
* This setting will replace <, >, ", ' and &
|
336 |
+
*/
|
337 |
+
define('XML_SERIALIZER_ENTITIES_XML', XML_UTIL_ENTITIES_XML);
|
338 |
+
|
339 |
+
/**
|
340 |
+
* replace only required XML entitites
|
341 |
+
* This setting will replace <, " and &
|
342 |
+
*/
|
343 |
+
define('XML_SERIALIZER_ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED);
|
344 |
+
|
345 |
+
/**
|
346 |
+
* replace HTML entitites
|
347 |
+
* @link http://www.php.net/htmlentities
|
348 |
+
*/
|
349 |
+
define('XML_SERIALIZER_ENTITIES_HTML', XML_UTIL_ENTITIES_HTML);
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Creates XML documents from PHP data structures like arrays, objects or scalars.
|
353 |
+
*
|
354 |
+
* this class can be used in two modes:
|
355 |
+
*
|
356 |
+
* 1. create an XML document from an array or object that is processed by other
|
357 |
+
* applications. That means, you can create a RDF document from an array in the
|
358 |
+
* following format:
|
359 |
+
*
|
360 |
+
* $data = array(
|
361 |
+
* 'channel' => array(
|
362 |
+
* 'title' => 'Example RDF channel',
|
363 |
+
* 'link' => 'http://www.php-tools.de',
|
364 |
+
* 'image' => array(
|
365 |
+
* 'title' => 'Example image',
|
366 |
+
* 'url' => 'http://www.php-tools.de/image.gif',
|
367 |
+
* 'link' => 'http://www.php-tools.de'
|
368 |
+
* ),
|
369 |
+
* array(
|
370 |
+
* 'title' => 'Example item',
|
371 |
+
* 'link' => 'http://example.com'
|
372 |
+
* ),
|
373 |
+
* array(
|
374 |
+
* 'title' => 'Another Example item',
|
375 |
+
* 'link' => 'http://example.org'
|
376 |
+
* )
|
377 |
+
* )
|
378 |
+
* );
|
379 |
+
*
|
380 |
+
* to create a RDF document from this array do the following:
|
381 |
+
*
|
382 |
+
* require_once 'XML/Serializer.php';
|
383 |
+
*
|
384 |
+
* $options = array(
|
385 |
+
* XML_SERIALIZER_OPTION_INDENT => "\t", // indent with tabs
|
386 |
+
* XML_SERIALIZER_OPTION_LINEBREAKS => "\n", // use UNIX line breaks
|
387 |
+
* XML_SERIALIZER_OPTION_ROOT_NAME => 'rdf:RDF',// root tag
|
388 |
+
* XML_SERIALIZER_OPTION_DEFAULT_TAG => 'item' // tag for values
|
389 |
+
* // with numeric keys
|
390 |
+
* );
|
391 |
+
*
|
392 |
+
* $serializer = new XML_Serializer($options);
|
393 |
+
* $rdf = $serializer->serialize($data);
|
394 |
+
*
|
395 |
+
* You will get a complete XML document that can be processed like any RDF document.
|
396 |
+
*
|
397 |
+
* 2. this classes can be used to serialize any data structure in a way that it can
|
398 |
+
* later be unserialized again.
|
399 |
+
* XML_Serializer will store the type of the value and additional meta information
|
400 |
+
* in attributes of the surrounding tag. This meat information can later be used
|
401 |
+
* to restore the original data structure in PHP. If you want XML_Serializer
|
402 |
+
* to add meta information to the tags, add
|
403 |
+
*
|
404 |
+
* XML_SERIALIZER_OPTION_TYPEHINTS => true
|
405 |
+
*
|
406 |
+
* to the options array in the constructor.
|
407 |
+
*
|
408 |
+
* @category XML
|
409 |
+
* @package XML_Serializer
|
410 |
+
* @author Stephan Schmidt <schst@php.net>
|
411 |
+
* @copyright 2003-2008 Stephan Schmidt <schst@php.net>
|
412 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
413 |
+
* @version Release: 0.19.2
|
414 |
+
* @link http://pear.php.net/package/XML_Serializer
|
415 |
+
* @see XML_Unserializer
|
416 |
+
*/
|
417 |
+
class XML_Serializer extends PEAR
|
418 |
+
{
|
419 |
+
/**
|
420 |
+
* list of all available options
|
421 |
+
*
|
422 |
+
* @access private
|
423 |
+
* @var array
|
424 |
+
*/
|
425 |
+
var $_knownOptions = array(
|
426 |
+
XML_SERIALIZER_OPTION_INDENT,
|
427 |
+
XML_SERIALIZER_OPTION_LINEBREAKS,
|
428 |
+
XML_SERIALIZER_OPTION_TYPEHINTS,
|
429 |
+
XML_SERIALIZER_OPTION_XML_DECL_ENABLED,
|
430 |
+
XML_SERIALIZER_OPTION_XML_ENCODING,
|
431 |
+
XML_SERIALIZER_OPTION_DEFAULT_TAG,
|
432 |
+
XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME,
|
433 |
+
XML_SERIALIZER_OPTION_ATTRIBUTE_KEY,
|
434 |
+
XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE,
|
435 |
+
XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS,
|
436 |
+
XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES,
|
437 |
+
XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES,
|
438 |
+
XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES,
|
439 |
+
XML_SERIALIZER_OPTION_MODE,
|
440 |
+
XML_SERIALIZER_OPTION_DOCTYPE_ENABLED,
|
441 |
+
XML_SERIALIZER_OPTION_DOCTYPE,
|
442 |
+
XML_SERIALIZER_OPTION_ROOT_NAME,
|
443 |
+
XML_SERIALIZER_OPTION_ROOT_ATTRIBS,
|
444 |
+
XML_SERIALIZER_OPTION_ATTRIBUTES_KEY,
|
445 |
+
XML_SERIALIZER_OPTION_CONTENT_KEY,
|
446 |
+
XML_SERIALIZER_OPTION_COMMENT_KEY,
|
447 |
+
XML_SERIALIZER_OPTION_TAGMAP,
|
448 |
+
XML_SERIALIZER_OPTION_ENCODE_FUNC,
|
449 |
+
XML_SERIALIZER_OPTION_NAMESPACE,
|
450 |
+
XML_SERIALIZER_OPTION_ENTITIES,
|
451 |
+
XML_SERIALIZER_OPTION_RETURN_RESULT,
|
452 |
+
XML_SERIALIZER_OPTION_IGNORE_NULL,
|
453 |
+
XML_SERIALIZER_OPTION_CDATA_SECTIONS
|
454 |
+
);
|
455 |
+
|
456 |
+
/**
|
457 |
+
* default options for the serialization
|
458 |
+
*
|
459 |
+
* @access private
|
460 |
+
* @var array
|
461 |
+
*/
|
462 |
+
var $_defaultOptions = array(
|
463 |
+
|
464 |
+
// string used for indentation
|
465 |
+
XML_SERIALIZER_OPTION_INDENT => '',
|
466 |
+
|
467 |
+
// string used for newlines
|
468 |
+
XML_SERIALIZER_OPTION_LINEBREAKS => "\n",
|
469 |
+
|
470 |
+
// automatically add type hin attributes
|
471 |
+
XML_SERIALIZER_OPTION_TYPEHINTS => false,
|
472 |
+
|
473 |
+
// add an XML declaration
|
474 |
+
XML_SERIALIZER_OPTION_XML_DECL_ENABLED => false,
|
475 |
+
|
476 |
+
// encoding specified in the XML declaration
|
477 |
+
XML_SERIALIZER_OPTION_XML_ENCODING => null,
|
478 |
+
|
479 |
+
// tag used for indexed arrays or invalid names
|
480 |
+
XML_SERIALIZER_OPTION_DEFAULT_TAG => 'XML_Serializer_Tag',
|
481 |
+
|
482 |
+
// use classname for objects in indexed arrays
|
483 |
+
XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME => false,
|
484 |
+
|
485 |
+
// attribute where original key is stored
|
486 |
+
XML_SERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey',
|
487 |
+
|
488 |
+
// attribute for type (only if typeHints => true)
|
489 |
+
XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type',
|
490 |
+
|
491 |
+
// attribute for class of objects (only if typeHints => true)
|
492 |
+
XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class',
|
493 |
+
|
494 |
+
// scalar values (strings, ints,..) will be serialized as attribute
|
495 |
+
XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES => false,
|
496 |
+
|
497 |
+
// prepend string for attributes
|
498 |
+
XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES => '',
|
499 |
+
|
500 |
+
// indent the attributes, if set to '_auto',
|
501 |
+
// it will indent attributes so they all start at the same column
|
502 |
+
XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES => false,
|
503 |
+
|
504 |
+
// use XML_SERIALIZER_MODE_SIMPLEXML to use parent name as tagname
|
505 |
+
// if transforming an indexed array
|
506 |
+
XML_SERIALIZER_OPTION_MODE => XML_SERIALIZER_MODE_DEFAULT,
|
507 |
+
|
508 |
+
// add a doctype declaration
|
509 |
+
XML_SERIALIZER_OPTION_DOCTYPE_ENABLED => false,
|
510 |
+
|
511 |
+
// supply a string or an array with id and uri
|
512 |
+
// ({@see XML_Util::getDoctypeDeclaration()}
|
513 |
+
XML_SERIALIZER_OPTION_DOCTYPE => null,
|
514 |
+
|
515 |
+
// name of the root tag
|
516 |
+
XML_SERIALIZER_OPTION_ROOT_NAME => null,
|
517 |
+
|
518 |
+
// attributes of the root tag
|
519 |
+
XML_SERIALIZER_OPTION_ROOT_ATTRIBS => array(),
|
520 |
+
|
521 |
+
// all values in this key will be treated as attributes
|
522 |
+
XML_SERIALIZER_OPTION_ATTRIBUTES_KEY => null,
|
523 |
+
|
524 |
+
// this value will be used directly as content,
|
525 |
+
// instead of creating a new tag, may only be used
|
526 |
+
// in conjuction with attributesArray
|
527 |
+
XML_SERIALIZER_OPTION_CONTENT_KEY => null,
|
528 |
+
|
529 |
+
// this value will be used directly as comment,
|
530 |
+
// instead of creating a new tag, may only be used
|
531 |
+
// in conjuction with attributesArray
|
532 |
+
XML_SERIALIZER_OPTION_COMMENT_KEY => null,
|
533 |
+
|
534 |
+
// tag names that will be changed
|
535 |
+
XML_SERIALIZER_OPTION_TAGMAP => array(),
|
536 |
+
|
537 |
+
// function that will be applied before serializing
|
538 |
+
XML_SERIALIZER_OPTION_ENCODE_FUNC => null,
|
539 |
+
|
540 |
+
// namespace to use
|
541 |
+
XML_SERIALIZER_OPTION_NAMESPACE => null,
|
542 |
+
|
543 |
+
// type of entities to replace,
|
544 |
+
XML_SERIALIZER_OPTION_ENTITIES => XML_SERIALIZER_ENTITIES_XML,
|
545 |
+
|
546 |
+
// serialize() returns the result of the serialization instead of true
|
547 |
+
XML_SERIALIZER_OPTION_RETURN_RESULT => false,
|
548 |
+
|
549 |
+
// ignore properties that are set to null
|
550 |
+
XML_SERIALIZER_OPTION_IGNORE_NULL => false,
|
551 |
+
|
552 |
+
// Whether to use cdata sections for plain character data
|
553 |
+
XML_SERIALIZER_OPTION_CDATA_SECTIONS => false
|
554 |
+
);
|
555 |
+
|
556 |
+
/**
|
557 |
+
* options for the serialization
|
558 |
+
*
|
559 |
+
* @access public
|
560 |
+
* @var array
|
561 |
+
*/
|
562 |
+
var $options = array();
|
563 |
+
|
564 |
+
/**
|
565 |
+
* current tag depth
|
566 |
+
*
|
567 |
+
* @access private
|
568 |
+
* @var integer
|
569 |
+
*/
|
570 |
+
var $_tagDepth = 0;
|
571 |
+
|
572 |
+
/**
|
573 |
+
* serilialized representation of the data
|
574 |
+
*
|
575 |
+
* @access private
|
576 |
+
* @var string
|
577 |
+
*/
|
578 |
+
var $_serializedData = null;
|
579 |
+
|
580 |
+
/**
|
581 |
+
* constructor
|
582 |
+
*
|
583 |
+
* @param mixed $options array containing options for the serialization
|
584 |
+
*
|
585 |
+
* @return void
|
586 |
+
* @access public
|
587 |
+
*/
|
588 |
+
function XML_Serializer( $options = null )
|
589 |
+
{
|
590 |
+
$this->PEAR();
|
591 |
+
if (is_array($options)) {
|
592 |
+
$this->options = array_merge($this->_defaultOptions, $options);
|
593 |
+
} else {
|
594 |
+
$this->options = $this->_defaultOptions;
|
595 |
+
}
|
596 |
+
}
|
597 |
+
|
598 |
+
/**
|
599 |
+
* return API version
|
600 |
+
*
|
601 |
+
* @access public
|
602 |
+
* @static
|
603 |
+
* @return string $version API version
|
604 |
+
*/
|
605 |
+
function apiVersion()
|
606 |
+
{
|
607 |
+
return '0.19.2';
|
608 |
+
}
|
609 |
+
|
610 |
+
/**
|
611 |
+
* reset all options to default options
|
612 |
+
*
|
613 |
+
* @return void
|
614 |
+
* @access public
|
615 |
+
* @see setOption(), XML_Serializer()
|
616 |
+
*/
|
617 |
+
function resetOptions()
|
618 |
+
{
|
619 |
+
$this->options = $this->_defaultOptions;
|
620 |
+
}
|
621 |
+
|
622 |
+
/**
|
623 |
+
* set an option
|
624 |
+
*
|
625 |
+
* You can use this method if you do not want
|
626 |
+
* to set all options in the constructor
|
627 |
+
*
|
628 |
+
* @param string $name option name
|
629 |
+
* @param mixed $value option value
|
630 |
+
*
|
631 |
+
* @return void
|
632 |
+
* @access public
|
633 |
+
* @see resetOption(), XML_Serializer()
|
634 |
+
*/
|
635 |
+
function setOption($name, $value)
|
636 |
+
{
|
637 |
+
$this->options[$name] = $value;
|
638 |
+
}
|
639 |
+
|
640 |
+
/**
|
641 |
+
* sets several options at once
|
642 |
+
*
|
643 |
+
* You can use this method if you do not want
|
644 |
+
* to set all options in the constructor
|
645 |
+
*
|
646 |
+
* @param array $options options array
|
647 |
+
*
|
648 |
+
* @return void
|
649 |
+
* @access public
|
650 |
+
* @see resetOption(), XML_Unserializer(), setOption()
|
651 |
+
*/
|
652 |
+
function setOptions($options)
|
653 |
+
{
|
654 |
+
$this->options = array_merge($this->options, $options);
|
655 |
+
}
|
656 |
+
|
657 |
+
/**
|
658 |
+
* serialize data
|
659 |
+
*
|
660 |
+
* @param mixed $data data to serialize
|
661 |
+
* @param array $options options array
|
662 |
+
*
|
663 |
+
* @return boolean true on success, pear error on failure
|
664 |
+
* @access public
|
665 |
+
*/
|
666 |
+
function serialize($data, $options = null)
|
667 |
+
{
|
668 |
+
// if options have been specified, use them instead
|
669 |
+
// of the previously defined ones
|
670 |
+
if (is_array($options)) {
|
671 |
+
$optionsBak = $this->options;
|
672 |
+
if (isset($options['overrideOptions'])
|
673 |
+
&& $options['overrideOptions'] == true
|
674 |
+
) {
|
675 |
+
$this->options = array_merge($this->_defaultOptions, $options);
|
676 |
+
} else {
|
677 |
+
$this->options = array_merge($this->options, $options);
|
678 |
+
}
|
679 |
+
} else {
|
680 |
+
$optionsBak = null;
|
681 |
+
}
|
682 |
+
|
683 |
+
// start depth is zero
|
684 |
+
$this->_tagDepth = 0;
|
685 |
+
|
686 |
+
$rootAttributes = $this->options[XML_SERIALIZER_OPTION_ROOT_ATTRIBS];
|
687 |
+
if (isset($this->options[XML_SERIALIZER_OPTION_NAMESPACE])
|
688 |
+
&& is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])
|
689 |
+
) {
|
690 |
+
$rootAttributes['xmlns:'
|
691 |
+
. $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]] =
|
692 |
+
$this->options[XML_SERIALIZER_OPTION_NAMESPACE][1];
|
693 |
+
}
|
694 |
+
|
695 |
+
$this->_serializedData = '';
|
696 |
+
// serialize an array
|
697 |
+
if (is_array($data)) {
|
698 |
+
if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
|
699 |
+
$tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
|
700 |
+
} else {
|
701 |
+
$tagName = 'array';
|
702 |
+
}
|
703 |
+
|
704 |
+
$this->_serializedData .=
|
705 |
+
$this->_serializeArray($data, $tagName, $rootAttributes);
|
706 |
+
} elseif (is_object($data)) {
|
707 |
+
// serialize an object
|
708 |
+
if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
|
709 |
+
$tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
|
710 |
+
} else {
|
711 |
+
$tagName = get_class($data);
|
712 |
+
}
|
713 |
+
$this->_serializedData .=
|
714 |
+
$this->_serializeObject($data, $tagName, $rootAttributes);
|
715 |
+
} else {
|
716 |
+
$tag = array();
|
717 |
+
if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
|
718 |
+
$tag['qname'] = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
|
719 |
+
} else {
|
720 |
+
$tag['qname'] = gettype($data);
|
721 |
+
}
|
722 |
+
$tagName = $tag['qname'];
|
723 |
+
if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
|
724 |
+
$rootAttributes[$this->
|
725 |
+
options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($data);
|
726 |
+
}
|
727 |
+
@settype($data, 'string');
|
728 |
+
$tag['content'] = $data;
|
729 |
+
$tag['attributes'] = $rootAttributes;
|
730 |
+
$this->_serializedData = $this->_createXMLTag($tag);
|
731 |
+
}
|
732 |
+
|
733 |
+
// add doctype declaration
|
734 |
+
if ($this->options[XML_SERIALIZER_OPTION_DOCTYPE_ENABLED] === true) {
|
735 |
+
$this->_serializedData =
|
736 |
+
XML_Util::getDoctypeDeclaration($tagName,
|
737 |
+
$this->options[XML_SERIALIZER_OPTION_DOCTYPE])
|
738 |
+
. $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
|
739 |
+
. $this->_serializedData;
|
740 |
+
}
|
741 |
+
|
742 |
+
// build xml declaration
|
743 |
+
if ($this->options[XML_SERIALIZER_OPTION_XML_DECL_ENABLED]) {
|
744 |
+
$atts = array();
|
745 |
+
$this->_serializedData = XML_Util::getXMLDeclaration('1.0',
|
746 |
+
$this->options[XML_SERIALIZER_OPTION_XML_ENCODING])
|
747 |
+
. $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
|
748 |
+
. $this->_serializedData;
|
749 |
+
}
|
750 |
+
|
751 |
+
if ($this->options[XML_SERIALIZER_OPTION_RETURN_RESULT] === true) {
|
752 |
+
$result = $this->_serializedData;
|
753 |
+
} else {
|
754 |
+
$result = true;
|
755 |
+
}
|
756 |
+
|
757 |
+
if ($optionsBak !== null) {
|
758 |
+
$this->options = $optionsBak;
|
759 |
+
}
|
760 |
+
|
761 |
+
return $result;
|
762 |
+
}
|
763 |
+
|
764 |
+
/**
|
765 |
+
* get the result of the serialization
|
766 |
+
*
|
767 |
+
* @access public
|
768 |
+
* @return string serialized XML
|
769 |
+
*/
|
770 |
+
function getSerializedData()
|
771 |
+
{
|
772 |
+
if ($this->_serializedData == null) {
|
773 |
+
return $this->raiseError('No serialized data available. '
|
774 |
+
. 'Use XML_Serializer::serialize() first.',
|
775 |
+
XML_SERIALIZER_ERROR_NO_SERIALIZATION);
|
776 |
+
}
|
777 |
+
return $this->_serializedData;
|
778 |
+
}
|
779 |
+
|
780 |
+
/**
|
781 |
+
* serialize any value
|
782 |
+
*
|
783 |
+
* This method checks for the type of the value and calls the appropriate method
|
784 |
+
*
|
785 |
+
* @param mixed $value tag value
|
786 |
+
* @param string $tagName tag name
|
787 |
+
* @param array $attributes attributes
|
788 |
+
*
|
789 |
+
* @return string
|
790 |
+
* @access private
|
791 |
+
*/
|
792 |
+
function _serializeValue($value, $tagName = null, $attributes = array())
|
793 |
+
{
|
794 |
+
if (is_array($value)) {
|
795 |
+
$xml = $this->_serializeArray($value, $tagName, $attributes);
|
796 |
+
} elseif (is_object($value)) {
|
797 |
+
$xml = $this->_serializeObject($value, $tagName);
|
798 |
+
} else {
|
799 |
+
$tag = array(
|
800 |
+
'qname' => $tagName,
|
801 |
+
'attributes' => $attributes,
|
802 |
+
'content' => $value
|
803 |
+
);
|
804 |
+
$xml = $this->_createXMLTag($tag);
|
805 |
+
}
|
806 |
+
return $xml;
|
807 |
+
}
|
808 |
+
|
809 |
+
/**
|
810 |
+
* serialize an array
|
811 |
+
*
|
812 |
+
* @param array &$array array to serialize
|
813 |
+
* @param string $tagName name of the root tag
|
814 |
+
* @param array $attributes attributes for the root tag
|
815 |
+
*
|
816 |
+
* @return string $string serialized data
|
817 |
+
* @access private
|
818 |
+
* @uses XML_Util::isValidName() to check, whether key has to be substituted
|
819 |
+
*/
|
820 |
+
function _serializeArray(&$array, $tagName = null, $attributes = array())
|
821 |
+
{
|
822 |
+
$_content = null;
|
823 |
+
$_comment = null;
|
824 |
+
|
825 |
+
// check for comment
|
826 |
+
if ($this->options[XML_SERIALIZER_OPTION_COMMENT_KEY] !== null) {
|
827 |
+
if (isset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]])
|
828 |
+
) {
|
829 |
+
$_comment =
|
830 |
+
$array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]];
|
831 |
+
unset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]]);
|
832 |
+
}
|
833 |
+
}
|
834 |
+
|
835 |
+
/**
|
836 |
+
* check for special attributes
|
837 |
+
*/
|
838 |
+
if ($this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY] !== null) {
|
839 |
+
if (isset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]])
|
840 |
+
) {
|
841 |
+
$attributes =
|
842 |
+
$array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]];
|
843 |
+
unset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]]);
|
844 |
+
}
|
845 |
+
/**
|
846 |
+
* check for special content
|
847 |
+
*/
|
848 |
+
if ($this->options[XML_SERIALIZER_OPTION_CONTENT_KEY] !== null) {
|
849 |
+
if (isset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]])
|
850 |
+
) {
|
851 |
+
$_content =
|
852 |
+
XML_Util::replaceEntities($array
|
853 |
+
[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
|
854 |
+
unset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
|
855 |
+
}
|
856 |
+
}
|
857 |
+
}
|
858 |
+
|
859 |
+
if ($this->options[XML_SERIALIZER_OPTION_IGNORE_NULL] === true) {
|
860 |
+
foreach (array_keys($array) as $key) {
|
861 |
+
if (is_null($array[$key])) {
|
862 |
+
unset($array[$key]);
|
863 |
+
}
|
864 |
+
}
|
865 |
+
}
|
866 |
+
|
867 |
+
/*
|
868 |
+
* if mode is set to simpleXML, check whether
|
869 |
+
* the array is associative or indexed
|
870 |
+
*/
|
871 |
+
if (is_array($array) && !empty($array)
|
872 |
+
&& $this->options[XML_SERIALIZER_OPTION_MODE]
|
873 |
+
== XML_SERIALIZER_MODE_SIMPLEXML
|
874 |
+
) {
|
875 |
+
$indexed = true;
|
876 |
+
foreach ($array as $key => $val) {
|
877 |
+
if (!is_int($key)) {
|
878 |
+
$indexed = false;
|
879 |
+
break;
|
880 |
+
}
|
881 |
+
}
|
882 |
+
|
883 |
+
if ($indexed
|
884 |
+
&& $this->options[XML_SERIALIZER_OPTION_MODE]
|
885 |
+
== XML_SERIALIZER_MODE_SIMPLEXML
|
886 |
+
) {
|
887 |
+
$string = '';
|
888 |
+
foreach ($array as $key => $val) {
|
889 |
+
$string .= $this->_serializeValue($val, $tagName, $attributes);
|
890 |
+
|
891 |
+
$string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
|
892 |
+
// do indentation
|
893 |
+
if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
|
894 |
+
&& $this->_tagDepth>0
|
895 |
+
) {
|
896 |
+
$string .=
|
897 |
+
str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
|
898 |
+
$this->_tagDepth);
|
899 |
+
}
|
900 |
+
}
|
901 |
+
return rtrim($string);
|
902 |
+
}
|
903 |
+
}
|
904 |
+
|
905 |
+
$scalarAsAttributes = false;
|
906 |
+
if (is_array($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES])
|
907 |
+
&& isset($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES]
|
908 |
+
[$tagName])
|
909 |
+
) {
|
910 |
+
$scalarAsAttributes =
|
911 |
+
$this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName];
|
912 |
+
} elseif ($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES] === true
|
913 |
+
) {
|
914 |
+
$scalarAsAttributes = true;
|
915 |
+
}
|
916 |
+
|
917 |
+
if ($scalarAsAttributes === true) {
|
918 |
+
$this->expectError('*');
|
919 |
+
foreach ($array as $key => $value) {
|
920 |
+
if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
|
921 |
+
unset($array[$key]);
|
922 |
+
$attributes[$this->options
|
923 |
+
[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
|
924 |
+
}
|
925 |
+
}
|
926 |
+
$this->popExpect();
|
927 |
+
} elseif (is_array($scalarAsAttributes)) {
|
928 |
+
$this->expectError('*');
|
929 |
+
foreach ($scalarAsAttributes as $key) {
|
930 |
+
if (!isset($array[$key])) {
|
931 |
+
continue;
|
932 |
+
}
|
933 |
+
$value = $array[$key];
|
934 |
+
if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
|
935 |
+
unset($array[$key]);
|
936 |
+
$attributes[$this->options
|
937 |
+
[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
|
938 |
+
}
|
939 |
+
}
|
940 |
+
$this->popExpect();
|
941 |
+
}
|
942 |
+
|
943 |
+
// check for empty array => create empty tag
|
944 |
+
if (empty($array)) {
|
945 |
+
$tag = array(
|
946 |
+
'qname' => $tagName,
|
947 |
+
'content' => $_content,
|
948 |
+
'attributes' => $attributes
|
949 |
+
);
|
950 |
+
} else {
|
951 |
+
$this->_tagDepth++;
|
952 |
+
$tmp = $_content . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
|
953 |
+
foreach ($array as $key => $value) {
|
954 |
+
// do indentation
|
955 |
+
if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
|
956 |
+
&& $this->_tagDepth>0
|
957 |
+
) {
|
958 |
+
$tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
|
959 |
+
$this->_tagDepth);
|
960 |
+
}
|
961 |
+
|
962 |
+
// copy key
|
963 |
+
$origKey = $key;
|
964 |
+
$this->expectError('*');
|
965 |
+
// key cannot be used as tagname => use default tag
|
966 |
+
$valid = XML_Util::isValidName($key);
|
967 |
+
$this->popExpect();
|
968 |
+
if (PEAR::isError($valid)) {
|
969 |
+
if ($this->options[XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME]
|
970 |
+
&& is_object($value)
|
971 |
+
) {
|
972 |
+
$key = get_class($value);
|
973 |
+
} else {
|
974 |
+
$key = $this->_getDefaultTagname($tagName);
|
975 |
+
}
|
976 |
+
}
|
977 |
+
|
978 |
+
// once we've established the true $key, is there a tagmap for it?
|
979 |
+
if (isset($this->options[XML_SERIALIZER_OPTION_TAGMAP][$key])) {
|
980 |
+
$key = $this->options[XML_SERIALIZER_OPTION_TAGMAP][$key];
|
981 |
+
}
|
982 |
+
|
983 |
+
$atts = array();
|
984 |
+
if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
|
985 |
+
$atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] =
|
986 |
+
gettype($value);
|
987 |
+
if ($key !== $origKey) {
|
988 |
+
$atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_KEY]] =
|
989 |
+
(string)$origKey;
|
990 |
+
}
|
991 |
+
}
|
992 |
+
|
993 |
+
$tmp .= $this->_createXMLTag(array(
|
994 |
+
'qname' => $key,
|
995 |
+
'attributes' => $atts,
|
996 |
+
'content' => $value
|
997 |
+
));
|
998 |
+
$tmp .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
|
999 |
+
}
|
1000 |
+
|
1001 |
+
$this->_tagDepth--;
|
1002 |
+
if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
|
1003 |
+
&& $this->_tagDepth>0
|
1004 |
+
) {
|
1005 |
+
$tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
|
1006 |
+
$this->_tagDepth);
|
1007 |
+
}
|
1008 |
+
|
1009 |
+
if (trim($tmp) === '') {
|
1010 |
+
$tmp = null;
|
1011 |
+
}
|
1012 |
+
|
1013 |
+
$tag = array(
|
1014 |
+
'qname' => $tagName,
|
1015 |
+
'content' => $tmp,
|
1016 |
+
'attributes' => $attributes
|
1017 |
+
);
|
1018 |
+
}
|
1019 |
+
if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
|
1020 |
+
if (!isset($tag['attributes']
|
1021 |
+
[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]])
|
1022 |
+
) {
|
1023 |
+
$tag['attributes']
|
1024 |
+
[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'array';
|
1025 |
+
}
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
$string = '';
|
1029 |
+
if (!is_null($_comment)) {
|
1030 |
+
$string .= XML_Util::createComment($_comment);
|
1031 |
+
$string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
|
1032 |
+
if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
|
1033 |
+
&& $this->_tagDepth>0
|
1034 |
+
) {
|
1035 |
+
$string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
|
1036 |
+
$this->_tagDepth);
|
1037 |
+
}
|
1038 |
+
}
|
1039 |
+
$string .= $this->_createXMLTag($tag, false);
|
1040 |
+
return $string;
|
1041 |
+
}
|
1042 |
+
|
1043 |
+
/**
|
1044 |
+
* get the name of the default tag.
|
1045 |
+
*
|
1046 |
+
* The name of the parent tag needs to be passed as the
|
1047 |
+
* default name can depend on the context.
|
1048 |
+
*
|
1049 |
+
* @param string $parent name of the parent tag
|
1050 |
+
*
|
1051 |
+
* @return string default tag name
|
1052 |
+
*/
|
1053 |
+
function _getDefaultTagname($parent)
|
1054 |
+
{
|
1055 |
+
if (is_string($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG])) {
|
1056 |
+
return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG];
|
1057 |
+
}
|
1058 |
+
if (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent])) {
|
1059 |
+
return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent];
|
1060 |
+
} elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]
|
1061 |
+
['#default'])
|
1062 |
+
) {
|
1063 |
+
return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'];
|
1064 |
+
} elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]
|
1065 |
+
['__default'])
|
1066 |
+
) {
|
1067 |
+
// keep this for BC
|
1068 |
+
return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'];
|
1069 |
+
}
|
1070 |
+
return 'XML_Serializer_Tag';
|
1071 |
+
}
|
1072 |
+
|
1073 |
+
/**
|
1074 |
+
* serialize an object
|
1075 |
+
*
|
1076 |
+
* @param object &$object object to serialize
|
1077 |
+
* @param string $tagName tag name
|
1078 |
+
* @param array $attributes attributes
|
1079 |
+
*
|
1080 |
+
* @return string $string serialized data
|
1081 |
+
* @access private
|
1082 |
+
*/
|
1083 |
+
function _serializeObject(&$object, $tagName = null, $attributes = array())
|
1084 |
+
{
|
1085 |
+
// check for magic function
|
1086 |
+
if (method_exists($object, '__sleep')) {
|
1087 |
+
$propNames = $object->__sleep();
|
1088 |
+
if (is_array($propNames)) {
|
1089 |
+
$properties = array();
|
1090 |
+
foreach ($propNames as $propName) {
|
1091 |
+
$properties[$propName] = $object->$propName;
|
1092 |
+
}
|
1093 |
+
} else {
|
1094 |
+
$properties = get_object_vars($object);
|
1095 |
+
}
|
1096 |
+
} else {
|
1097 |
+
$properties = get_object_vars($object);
|
1098 |
+
}
|
1099 |
+
|
1100 |
+
if (empty($tagName)) {
|
1101 |
+
$tagName = get_class($object);
|
1102 |
+
}
|
1103 |
+
|
1104 |
+
// typehints activated?
|
1105 |
+
if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
|
1106 |
+
$attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] =
|
1107 |
+
'object';
|
1108 |
+
$attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS]] =
|
1109 |
+
get_class($object);
|
1110 |
+
}
|
1111 |
+
$string = $this->_serializeArray($properties, $tagName, $attributes);
|
1112 |
+
return $string;
|
1113 |
+
}
|
1114 |
+
|
1115 |
+
/**
|
1116 |
+
* create a tag from an array
|
1117 |
+
* this method awaits an array in the following format
|
1118 |
+
* array(
|
1119 |
+
* 'qname' => $tagName,
|
1120 |
+
* 'attributes' => array(),
|
1121 |
+
* 'content' => $content, // optional
|
1122 |
+
* 'namespace' => $namespace // optional
|
1123 |
+
* 'namespaceUri' => $namespaceUri // optional
|
1124 |
+
* )
|
1125 |
+
*
|
1126 |
+
* @param array $tag tag definition
|
1127 |
+
* @param boolean $firstCall whether or not this is the first call
|
1128 |
+
*
|
1129 |
+
* @return string $string XML tag
|
1130 |
+
* @access private
|
1131 |
+
*/
|
1132 |
+
function _createXMLTag($tag, $firstCall = true)
|
1133 |
+
{
|
1134 |
+
// build fully qualified tag name
|
1135 |
+
if ($this->options[XML_SERIALIZER_OPTION_NAMESPACE] !== null) {
|
1136 |
+
if (is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
|
1137 |
+
$tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]
|
1138 |
+
. ':' . $tag['qname'];
|
1139 |
+
} else {
|
1140 |
+
$tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE]
|
1141 |
+
. ':' . $tag['qname'];
|
1142 |
+
}
|
1143 |
+
}
|
1144 |
+
|
1145 |
+
// attribute indentation
|
1146 |
+
if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] !== false) {
|
1147 |
+
$multiline = true;
|
1148 |
+
$indent = str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
|
1149 |
+
$this->_tagDepth);
|
1150 |
+
|
1151 |
+
if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] == '_auto') {
|
1152 |
+
$indent .= str_repeat(' ', (strlen($tag['qname'])+2));
|
1153 |
+
|
1154 |
+
} else {
|
1155 |
+
$indent .= $this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES];
|
1156 |
+
}
|
1157 |
+
} else {
|
1158 |
+
$multiline = false;
|
1159 |
+
$indent = false;
|
1160 |
+
}
|
1161 |
+
|
1162 |
+
if (is_array($tag['content'])) {
|
1163 |
+
if (empty($tag['content'])) {
|
1164 |
+
$tag['content'] = '';
|
1165 |
+
}
|
1166 |
+
} elseif (is_scalar($tag['content']) && (string)$tag['content'] == '') {
|
1167 |
+
$tag['content'] = '';
|
1168 |
+
}
|
1169 |
+
|
1170 |
+
// replace XML entities
|
1171 |
+
if ($firstCall === true) {
|
1172 |
+
if ($this->options[XML_SERIALIZER_OPTION_CDATA_SECTIONS] === true) {
|
1173 |
+
$replaceEntities = XML_UTIL_CDATA_SECTION;
|
1174 |
+
} else {
|
1175 |
+
$replaceEntities = $this->options[XML_SERIALIZER_OPTION_ENTITIES];
|
1176 |
+
}
|
1177 |
+
} else {
|
1178 |
+
// this is a nested call, so value is already encoded
|
1179 |
+
// and must not be encoded again
|
1180 |
+
$replaceEntities = XML_SERIALIZER_ENTITIES_NONE;
|
1181 |
+
// but attributes need to be encoded anyways
|
1182 |
+
// (done here because the rest of the code assumes the same encoding
|
1183 |
+
// can be used both for attributes and content)
|
1184 |
+
foreach ($tag['attributes'] as $k => &$v) {
|
1185 |
+
$v = XML_Util::replaceEntities($v,
|
1186 |
+
$this->options[XML_SERIALIZER_OPTION_ENTITIES]);
|
1187 |
+
}
|
1188 |
+
}
|
1189 |
+
if (is_scalar($tag['content']) || is_null($tag['content'])) {
|
1190 |
+
if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
|
1191 |
+
if ($firstCall === true) {
|
1192 |
+
$tag['content'] = call_user_func($this->
|
1193 |
+
options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
|
1194 |
+
}
|
1195 |
+
$tag['attributes'] = array_map($this->
|
1196 |
+
options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
|
1197 |
+
}
|
1198 |
+
$tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline,
|
1199 |
+
$indent, $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]);
|
1200 |
+
} elseif (is_array($tag['content'])) {
|
1201 |
+
$tag = $this->_serializeArray($tag['content'], $tag['qname'],
|
1202 |
+
$tag['attributes']);
|
1203 |
+
} elseif (is_object($tag['content'])) {
|
1204 |
+
$tag = $this->_serializeObject($tag['content'], $tag['qname'],
|
1205 |
+
$tag['attributes']);
|
1206 |
+
} elseif (is_resource($tag['content'])) {
|
1207 |
+
settype($tag['content'], 'string');
|
1208 |
+
if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
|
1209 |
+
if ($replaceEntities === true) {
|
1210 |
+
$tag['content'] = call_user_func($this->
|
1211 |
+
options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
|
1212 |
+
}
|
1213 |
+
$tag['attributes'] = array_map($this->
|
1214 |
+
options[XML_SERIALIZER_OPTION_ENCODE_FUNC],
|
1215 |
+
$tag['attributes']);
|
1216 |
+
}
|
1217 |
+
$tag = XML_Util::createTagFromArray($tag, $replaceEntities);
|
1218 |
+
}
|
1219 |
+
return $tag;
|
1220 |
+
}
|
1221 |
+
}
|
1222 |
+
?>
|
lib/PEAR/XML/Unserializer.php
ADDED
@@ -0,0 +1,983 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?PHP
|
2 |
+
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
3 |
+
|
4 |
+
/**
|
5 |
+
* XML_Unserializer
|
6 |
+
*
|
7 |
+
* Parses any XML document into PHP data structures.
|
8 |
+
*
|
9 |
+
* PHP versions 4 and 5
|
10 |
+
*
|
11 |
+
* LICENSE:
|
12 |
+
*
|
13 |
+
* Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
|
14 |
+
* All rights reserved.
|
15 |
+
*
|
16 |
+
* Redistribution and use in source and binary forms, with or without
|
17 |
+
* modification, are permitted provided that the following conditions
|
18 |
+
* are met:
|
19 |
+
*
|
20 |
+
* * Redistributions of source code must retain the above copyright
|
21 |
+
* notice, this list of conditions and the following disclaimer.
|
22 |
+
* * Redistributions in binary form must reproduce the above copyright
|
23 |
+
* notice, this list of conditions and the following disclaimer in the
|
24 |
+
* documentation and/or other materials provided with the distribution.
|
25 |
+
* * The name of the author may not be used to endorse or promote products
|
26 |
+
* derived from this software without specific prior written permission.
|
27 |
+
*
|
28 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
29 |
+
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
30 |
+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
31 |
+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
32 |
+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
33 |
+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
34 |
+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
35 |
+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
36 |
+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
37 |
+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
38 |
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
39 |
+
*
|
40 |
+
* @category XML
|
41 |
+
* @package XML_Serializer
|
42 |
+
* @author Stephan Schmidt <schst@php.net>
|
43 |
+
* @copyright 2003-2008 Stephan Schmidt <schst@php.net>
|
44 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
45 |
+
* @version CVS: $Id: Unserializer.php,v 1.41 2008/08/25 00:07:16 ashnazg Exp $
|
46 |
+
* @link http://pear.php.net/package/XML_Serializer
|
47 |
+
* @see XML_Unserializer
|
48 |
+
*/
|
49 |
+
|
50 |
+
/**
|
51 |
+
* uses PEAR error managemt
|
52 |
+
*/
|
53 |
+
require_once 'PEAR.php';
|
54 |
+
|
55 |
+
/**
|
56 |
+
* uses XML_Parser to unserialize document
|
57 |
+
*/
|
58 |
+
require_once 'XML/Parser.php';
|
59 |
+
|
60 |
+
/**
|
61 |
+
* option: Convert nested tags to array or object
|
62 |
+
*
|
63 |
+
* Possible values:
|
64 |
+
* - array
|
65 |
+
* - object
|
66 |
+
* - associative array to define this option per tag name
|
67 |
+
*/
|
68 |
+
define('XML_UNSERIALIZER_OPTION_COMPLEXTYPE', 'complexType');
|
69 |
+
|
70 |
+
/**
|
71 |
+
* option: Name of the attribute that stores the original key
|
72 |
+
*
|
73 |
+
* Possible values:
|
74 |
+
* - any string
|
75 |
+
*/
|
76 |
+
define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
|
77 |
+
|
78 |
+
/**
|
79 |
+
* option: Name of the attribute that stores the type
|
80 |
+
*
|
81 |
+
* Possible values:
|
82 |
+
* - any string
|
83 |
+
*/
|
84 |
+
define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
|
85 |
+
|
86 |
+
/**
|
87 |
+
* option: Name of the attribute that stores the class name
|
88 |
+
*
|
89 |
+
* Possible values:
|
90 |
+
* - any string
|
91 |
+
*/
|
92 |
+
define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
|
93 |
+
|
94 |
+
/**
|
95 |
+
* option: Whether to use the tag name as a class name
|
96 |
+
*
|
97 |
+
* Possible values:
|
98 |
+
* - true or false
|
99 |
+
*/
|
100 |
+
define('XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME', 'tagAsClass');
|
101 |
+
|
102 |
+
/**
|
103 |
+
* option: Name of the default class
|
104 |
+
*
|
105 |
+
* Possible values:
|
106 |
+
* - any string
|
107 |
+
*/
|
108 |
+
define('XML_UNSERIALIZER_OPTION_DEFAULT_CLASS', 'defaultClass');
|
109 |
+
|
110 |
+
/**
|
111 |
+
* option: Whether to parse attributes
|
112 |
+
*
|
113 |
+
* Possible values:
|
114 |
+
* - true or false
|
115 |
+
*/
|
116 |
+
define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE', 'parseAttributes');
|
117 |
+
|
118 |
+
/**
|
119 |
+
* option: Key of the array to store attributes (if any)
|
120 |
+
*
|
121 |
+
* Possible values:
|
122 |
+
* - any string
|
123 |
+
* - false (disabled)
|
124 |
+
*/
|
125 |
+
define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY', 'attributesArray');
|
126 |
+
|
127 |
+
/**
|
128 |
+
* option: string to prepend attribute name (if any)
|
129 |
+
*
|
130 |
+
* Possible values:
|
131 |
+
* - any string
|
132 |
+
* - false (disabled)
|
133 |
+
*/
|
134 |
+
define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND', 'prependAttributes');
|
135 |
+
|
136 |
+
/**
|
137 |
+
* option: key to store the content,
|
138 |
+
* if XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE is used
|
139 |
+
*
|
140 |
+
* Possible values:
|
141 |
+
* - any string
|
142 |
+
*/
|
143 |
+
define('XML_UNSERIALIZER_OPTION_CONTENT_KEY', 'contentName');
|
144 |
+
|
145 |
+
/**
|
146 |
+
* option: map tag names
|
147 |
+
*
|
148 |
+
* Possible values:
|
149 |
+
* - associative array
|
150 |
+
*/
|
151 |
+
define('XML_UNSERIALIZER_OPTION_TAG_MAP', 'tagMap');
|
152 |
+
|
153 |
+
/**
|
154 |
+
* option: list of tags that will always be enumerated
|
155 |
+
*
|
156 |
+
* Possible values:
|
157 |
+
* - indexed array
|
158 |
+
*/
|
159 |
+
define('XML_UNSERIALIZER_OPTION_FORCE_ENUM', 'forceEnum');
|
160 |
+
|
161 |
+
/**
|
162 |
+
* option: Encoding of the XML document
|
163 |
+
*
|
164 |
+
* Possible values:
|
165 |
+
* - UTF-8
|
166 |
+
* - ISO-8859-1
|
167 |
+
*/
|
168 |
+
define('XML_UNSERIALIZER_OPTION_ENCODING_SOURCE', 'encoding');
|
169 |
+
|
170 |
+
/**
|
171 |
+
* option: Desired target encoding of the data
|
172 |
+
*
|
173 |
+
* Possible values:
|
174 |
+
* - UTF-8
|
175 |
+
* - ISO-8859-1
|
176 |
+
*/
|
177 |
+
define('XML_UNSERIALIZER_OPTION_ENCODING_TARGET', 'targetEncoding');
|
178 |
+
|
179 |
+
/**
|
180 |
+
* option: Callback that will be applied to textual data
|
181 |
+
*
|
182 |
+
* Possible values:
|
183 |
+
* - any valid PHP callback
|
184 |
+
*/
|
185 |
+
define('XML_UNSERIALIZER_OPTION_DECODE_FUNC', 'decodeFunction');
|
186 |
+
|
187 |
+
/**
|
188 |
+
* option: whether to return the result of the unserialization from unserialize()
|
189 |
+
*
|
190 |
+
* Possible values:
|
191 |
+
* - true
|
192 |
+
* - false (default)
|
193 |
+
*/
|
194 |
+
define('XML_UNSERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
|
195 |
+
|
196 |
+
/**
|
197 |
+
* option: set the whitespace behaviour
|
198 |
+
*
|
199 |
+
* Possible values:
|
200 |
+
* - XML_UNSERIALIZER_WHITESPACE_KEEP
|
201 |
+
* - XML_UNSERIALIZER_WHITESPACE_TRIM
|
202 |
+
* - XML_UNSERIALIZER_WHITESPACE_NORMALIZE
|
203 |
+
*/
|
204 |
+
define('XML_UNSERIALIZER_OPTION_WHITESPACE', 'whitespace');
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Keep all whitespace
|
208 |
+
*/
|
209 |
+
define('XML_UNSERIALIZER_WHITESPACE_KEEP', 'keep');
|
210 |
+
|
211 |
+
/**
|
212 |
+
* remove whitespace from start and end of the data
|
213 |
+
*/
|
214 |
+
define('XML_UNSERIALIZER_WHITESPACE_TRIM', 'trim');
|
215 |
+
|
216 |
+
/**
|
217 |
+
* normalize whitespace
|
218 |
+
*/
|
219 |
+
define('XML_UNSERIALIZER_WHITESPACE_NORMALIZE', 'normalize');
|
220 |
+
|
221 |
+
/**
|
222 |
+
* option: whether to ovverride all options that have been set before
|
223 |
+
*
|
224 |
+
* Possible values:
|
225 |
+
* - true
|
226 |
+
* - false (default)
|
227 |
+
*/
|
228 |
+
define('XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS', 'overrideOptions');
|
229 |
+
|
230 |
+
/**
|
231 |
+
* option: list of tags, that will not be used as keys
|
232 |
+
*/
|
233 |
+
define('XML_UNSERIALIZER_OPTION_IGNORE_KEYS', 'ignoreKeys');
|
234 |
+
|
235 |
+
/**
|
236 |
+
* option: whether to use type guessing for scalar values
|
237 |
+
*/
|
238 |
+
define('XML_UNSERIALIZER_OPTION_GUESS_TYPES', 'guessTypes');
|
239 |
+
|
240 |
+
/**
|
241 |
+
* error code for no serialization done
|
242 |
+
*/
|
243 |
+
define('XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION', 151);
|
244 |
+
|
245 |
+
/**
|
246 |
+
* XML_Unserializer
|
247 |
+
*
|
248 |
+
* class to unserialize XML documents that have been created with
|
249 |
+
* XML_Serializer. To unserialize an XML document you have to add
|
250 |
+
* type hints to the XML_Serializer options.
|
251 |
+
*
|
252 |
+
* If no type hints are available, XML_Unserializer will guess how
|
253 |
+
* the tags should be treated, that means complex structures will be
|
254 |
+
* arrays and tags with only CData in them will be strings.
|
255 |
+
*
|
256 |
+
* <code>
|
257 |
+
* require_once 'XML/Unserializer.php';
|
258 |
+
*
|
259 |
+
* // be careful to always use the ampersand in front of the new operator
|
260 |
+
* $unserializer = &new XML_Unserializer();
|
261 |
+
*
|
262 |
+
* $unserializer->unserialize($xml);
|
263 |
+
*
|
264 |
+
* $data = $unserializer->getUnserializedData();
|
265 |
+
* <code>
|
266 |
+
*
|
267 |
+
* @category XML
|
268 |
+
* @package XML_Serializer
|
269 |
+
* @author Stephan Schmidt <schst@php.net>
|
270 |
+
* @copyright 2003-2008 Stephan Schmidt <schst@php.net>
|
271 |
+
* @license http://opensource.org/licenses/bsd-license New BSD License
|
272 |
+
* @version Release: 0.19.2
|
273 |
+
* @link http://pear.php.net/package/XML_Serializer
|
274 |
+
* @see XML_Serializer
|
275 |
+
*/
|
276 |
+
class XML_Unserializer extends PEAR
|
277 |
+
{
|
278 |
+
/**
|
279 |
+
* list of all available options
|
280 |
+
*
|
281 |
+
* @access private
|
282 |
+
* @var array
|
283 |
+
*/
|
284 |
+
var $_knownOptions = array(
|
285 |
+
XML_UNSERIALIZER_OPTION_COMPLEXTYPE,
|
286 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY,
|
287 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE,
|
288 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS,
|
289 |
+
XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME,
|
290 |
+
XML_UNSERIALIZER_OPTION_DEFAULT_CLASS,
|
291 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE,
|
292 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY,
|
293 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND,
|
294 |
+
XML_UNSERIALIZER_OPTION_CONTENT_KEY,
|
295 |
+
XML_UNSERIALIZER_OPTION_TAG_MAP,
|
296 |
+
XML_UNSERIALIZER_OPTION_FORCE_ENUM,
|
297 |
+
XML_UNSERIALIZER_OPTION_ENCODING_SOURCE,
|
298 |
+
XML_UNSERIALIZER_OPTION_ENCODING_TARGET,
|
299 |
+
XML_UNSERIALIZER_OPTION_DECODE_FUNC,
|
300 |
+
XML_UNSERIALIZER_OPTION_RETURN_RESULT,
|
301 |
+
XML_UNSERIALIZER_OPTION_WHITESPACE,
|
302 |
+
XML_UNSERIALIZER_OPTION_IGNORE_KEYS,
|
303 |
+
XML_UNSERIALIZER_OPTION_GUESS_TYPES
|
304 |
+
);
|
305 |
+
/**
|
306 |
+
* default options for the serialization
|
307 |
+
*
|
308 |
+
* @access private
|
309 |
+
* @var array
|
310 |
+
*/
|
311 |
+
var $_defaultOptions = array(
|
312 |
+
// complex types will be converted to arrays, if no type hint is given
|
313 |
+
XML_UNSERIALIZER_OPTION_COMPLEXTYPE => 'array',
|
314 |
+
|
315 |
+
// get array key/property name from this attribute
|
316 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey',
|
317 |
+
|
318 |
+
// get type from this attribute
|
319 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type',
|
320 |
+
|
321 |
+
// get class from this attribute (if not given, use tag name)
|
322 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class',
|
323 |
+
|
324 |
+
// use the tagname as the classname
|
325 |
+
XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME => true,
|
326 |
+
|
327 |
+
// name of the class that is used to create objects
|
328 |
+
XML_UNSERIALIZER_OPTION_DEFAULT_CLASS => 'stdClass',
|
329 |
+
|
330 |
+
// parse the attributes of the tag into an array
|
331 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE => false,
|
332 |
+
|
333 |
+
// parse them into sperate array (specify name of array here)
|
334 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY => false,
|
335 |
+
|
336 |
+
// prepend attribute names with this string
|
337 |
+
XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND => '',
|
338 |
+
|
339 |
+
// put cdata found in a tag that has been converted
|
340 |
+
// to a complex type in this key
|
341 |
+
XML_UNSERIALIZER_OPTION_CONTENT_KEY => '_content',
|
342 |
+
|
343 |
+
// use this to map tagnames
|
344 |
+
XML_UNSERIALIZER_OPTION_TAG_MAP => array(),
|
345 |
+
|
346 |
+
// these tags will always be an indexed array
|
347 |
+
XML_UNSERIALIZER_OPTION_FORCE_ENUM => array(),
|
348 |
+
|
349 |
+
// specify the encoding character of the document to parse
|
350 |
+
XML_UNSERIALIZER_OPTION_ENCODING_SOURCE => null,
|
351 |
+
|
352 |
+
// specify the target encoding
|
353 |
+
XML_UNSERIALIZER_OPTION_ENCODING_TARGET => null,
|
354 |
+
|
355 |
+
// function used to decode data
|
356 |
+
XML_UNSERIALIZER_OPTION_DECODE_FUNC => null,
|
357 |
+
|
358 |
+
// unserialize() returns the result of the unserialization instead of true
|
359 |
+
XML_UNSERIALIZER_OPTION_RETURN_RESULT => false,
|
360 |
+
|
361 |
+
// remove whitespace around data
|
362 |
+
XML_UNSERIALIZER_OPTION_WHITESPACE => XML_UNSERIALIZER_WHITESPACE_TRIM,
|
363 |
+
|
364 |
+
// List of tags that will automatically be added to the parent,
|
365 |
+
// instead of adding a new key
|
366 |
+
XML_UNSERIALIZER_OPTION_IGNORE_KEYS => array(),
|
367 |
+
|
368 |
+
// Whether to use type guessing
|
369 |
+
XML_UNSERIALIZER_OPTION_GUESS_TYPES => false
|
370 |
+
);
|
371 |
+
|
372 |
+
/**
|
373 |
+
* current options for the serialization
|
374 |
+
*
|
375 |
+
* @access public
|
376 |
+
* @var array
|
377 |
+
*/
|
378 |
+
var $options = array();
|
379 |
+
|
380 |
+
/**
|
381 |
+
* unserialized data
|
382 |
+
*
|
383 |
+
* @access private
|
384 |
+
* @var string
|
385 |
+
*/
|
386 |
+
var $_unserializedData = null;
|
387 |
+
|
388 |
+
/**
|
389 |
+
* name of the root tag
|
390 |
+
*
|
391 |
+
* @access private
|
392 |
+
* @var string
|
393 |
+
*/
|
394 |
+
var $_root = null;
|
395 |
+
|
396 |
+
/**
|
397 |
+
* stack for all data that is found
|
398 |
+
*
|
399 |
+
* @access private
|
400 |
+
* @var array
|
401 |
+
*/
|
402 |
+
var $_dataStack = array();
|
403 |
+
|
404 |
+
/**
|
405 |
+
* stack for all values that are generated
|
406 |
+
*
|
407 |
+
* @access private
|
408 |
+
* @var array
|
409 |
+
*/
|
410 |
+
var $_valStack = array();
|
411 |
+
|
412 |
+
/**
|
413 |
+
* current tag depth
|
414 |
+
*
|
415 |
+
* @access private
|
416 |
+
* @var int
|
417 |
+
*/
|
418 |
+
var $_depth = 0;
|
419 |
+
|
420 |
+
/**
|
421 |
+
* XML_Parser instance
|
422 |
+
*
|
423 |
+
* @access private
|
424 |
+
* @var object XML_Parser
|
425 |
+
*/
|
426 |
+
var $_parser = null;
|
427 |
+
|
428 |
+
/**
|
429 |
+
* constructor
|
430 |
+
*
|
431 |
+
* @param mixed $options array containing options for the unserialization
|
432 |
+
*
|
433 |
+
* @access public
|
434 |
+
*/
|
435 |
+
function XML_Unserializer($options = null)
|
436 |
+
{
|
437 |
+
if (is_array($options)) {
|
438 |
+
$this->options = array_merge($this->_defaultOptions, $options);
|
439 |
+
} else {
|
440 |
+
$this->options = $this->_defaultOptions;
|
441 |
+
}
|
442 |
+
}
|
443 |
+
|
444 |
+
/**
|
445 |
+
* return API version
|
446 |
+
*
|
447 |
+
* @access public
|
448 |
+
* @return string $version API version
|
449 |
+
* @static
|
450 |
+
*/
|
451 |
+
function apiVersion()
|
452 |
+
{
|
453 |
+
return '0.19.2';
|
454 |
+
}
|
455 |
+
|
456 |
+
/**
|
457 |
+
* reset all options to default options
|
458 |
+
*
|
459 |
+
* @return void
|
460 |
+
* @access public
|
461 |
+
* @see setOption(), XML_Unserializer(), setOptions()
|
462 |
+
*/
|
463 |
+
function resetOptions()
|
464 |
+
{
|
465 |
+
$this->options = $this->_defaultOptions;
|
466 |
+
}
|
467 |
+
|
468 |
+
/**
|
469 |
+
* set an option
|
470 |
+
*
|
471 |
+
* You can use this method if you do not want
|
472 |
+
* to set all options in the constructor
|
473 |
+
*
|
474 |
+
* @param string $name name of option
|
475 |
+
* @param mixed $value value of option
|
476 |
+
*
|
477 |
+
* @return void
|
478 |
+
* @access public
|
479 |
+
* @see resetOption(), XML_Unserializer(), setOptions()
|
480 |
+
*/
|
481 |
+
function setOption($name, $value)
|
482 |
+
{
|
483 |
+
$this->options[$name] = $value;
|
484 |
+
}
|
485 |
+
|
486 |
+
/**
|
487 |
+
* sets several options at once
|
488 |
+
*
|
489 |
+
* You can use this method if you do not want
|
490 |
+
* to set all options in the constructor
|
491 |
+
*
|
492 |
+
* @param array $options options array
|
493 |
+
*
|
494 |
+
* @return void
|
495 |
+
* @access public
|
496 |
+
* @see resetOption(), XML_Unserializer(), setOption()
|
497 |
+
*/
|
498 |
+
function setOptions($options)
|
499 |
+
{
|
500 |
+
$this->options = array_merge($this->options, $options);
|
501 |
+
}
|
502 |
+
|
503 |
+
/**
|
504 |
+
* unserialize data
|
505 |
+
*
|
506 |
+
* @param mixed $data data to unserialize (string, filename or resource)
|
507 |
+
* @param boolean $isFile data should be treated as a file
|
508 |
+
* @param array $options options that will override
|
509 |
+
* the global options for this call
|
510 |
+
*
|
511 |
+
* @return boolean $success
|
512 |
+
* @access public
|
513 |
+
*/
|
514 |
+
function unserialize($data, $isFile = false, $options = null)
|
515 |
+
{
|
516 |
+
$this->_unserializedData = null;
|
517 |
+
$this->_root = null;
|
518 |
+
|
519 |
+
// if options have been specified, use them instead
|
520 |
+
// of the previously defined ones
|
521 |
+
if (is_array($options)) {
|
522 |
+
$optionsBak = $this->options;
|
523 |
+
if (isset($options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS])
|
524 |
+
&& $options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS] == true
|
525 |
+
) {
|
526 |
+
$this->options = array_merge($this->_defaultOptions, $options);
|
527 |
+
} else {
|
528 |
+
$this->options = array_merge($this->options, $options);
|
529 |
+
}
|
530 |
+
} else {
|
531 |
+
$optionsBak = null;
|
532 |
+
}
|
533 |
+
|
534 |
+
$this->_valStack = array();
|
535 |
+
$this->_dataStack = array();
|
536 |
+
$this->_depth = 0;
|
537 |
+
|
538 |
+
$this->_createParser();
|
539 |
+
|
540 |
+
if (is_string($data)) {
|
541 |
+
if ($isFile) {
|
542 |
+
$result = $this->_parser->setInputFile($data);
|
543 |
+
if (PEAR::isError($result)) {
|
544 |
+
return $result;
|
545 |
+
}
|
546 |
+
$result = $this->_parser->parse();
|
547 |
+
} else {
|
548 |
+
$result = $this->_parser->parseString($data, true);
|
549 |
+
}
|
550 |
+
} else {
|
551 |
+
$this->_parser->setInput($data);
|
552 |
+
$result = $this->_parser->parse();
|
553 |
+
}
|
554 |
+
|
555 |
+
if ($this->options[XML_UNSERIALIZER_OPTION_RETURN_RESULT] === true) {
|
556 |
+
$return = $this->_unserializedData;
|
557 |
+
} else {
|
558 |
+
$return = true;
|
559 |
+
}
|
560 |
+
|
561 |
+
if ($optionsBak !== null) {
|
562 |
+
$this->options = $optionsBak;
|
563 |
+
}
|
564 |
+
|
565 |
+
if (PEAR::isError($result)) {
|
566 |
+
return $result;
|
567 |
+
}
|
568 |
+
|
569 |
+
return $return;
|
570 |
+
}
|
571 |
+
|
572 |
+
/**
|
573 |
+
* get the result of the serialization
|
574 |
+
*
|
575 |
+
* @access public
|
576 |
+
* @return string $serializedData
|
577 |
+
*/
|
578 |
+
function getUnserializedData()
|
579 |
+
{
|
580 |
+
if ($this->_root === null) {
|
581 |
+
return $this->raiseError('No unserialized data available. '
|
582 |
+
. 'Use XML_Unserializer::unserialize() first.',
|
583 |
+
XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
|
584 |
+
}
|
585 |
+
return $this->_unserializedData;
|
586 |
+
}
|
587 |
+
|
588 |
+
/**
|
589 |
+
* get the name of the root tag
|
590 |
+
*
|
591 |
+
* @access public
|
592 |
+
* @return string $rootName
|
593 |
+
*/
|
594 |
+
function getRootName()
|
595 |
+
{
|
596 |
+
if ($this->_root === null) {
|
597 |
+
return $this->raiseError('No unserialized data available. '
|
598 |
+
. 'Use XML_Unserializer::unserialize() first.',
|
599 |
+
XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
|
600 |
+
}
|
601 |
+
return $this->_root;
|
602 |
+
}
|
603 |
+
|
604 |
+
/**
|
605 |
+
* Start element handler for XML parser
|
606 |
+
*
|
607 |
+
* @param object $parser XML parser object
|
608 |
+
* @param string $element XML element
|
609 |
+
* @param array $attribs attributes of XML tag
|
610 |
+
*
|
611 |
+
* @return void
|
612 |
+
* @access private
|
613 |
+
*/
|
614 |
+
function startHandler($parser, $element, $attribs)
|
615 |
+
{
|
616 |
+
if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]])
|
617 |
+
) {
|
618 |
+
$type = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]];
|
619 |
+
|
620 |
+
$guessType = false;
|
621 |
+
} else {
|
622 |
+
$type = 'string';
|
623 |
+
if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
|
624 |
+
$guessType = true;
|
625 |
+
} else {
|
626 |
+
$guessType = false;
|
627 |
+
}
|
628 |
+
}
|
629 |
+
|
630 |
+
if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
|
631 |
+
$attribs = array_map($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC],
|
632 |
+
$attribs);
|
633 |
+
}
|
634 |
+
|
635 |
+
$this->_depth++;
|
636 |
+
$this->_dataStack[$this->_depth] = null;
|
637 |
+
|
638 |
+
if (is_array($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP])
|
639 |
+
&& isset($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element])
|
640 |
+
) {
|
641 |
+
$element = $this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element];
|
642 |
+
}
|
643 |
+
|
644 |
+
$val = array(
|
645 |
+
'name' => $element,
|
646 |
+
'value' => null,
|
647 |
+
'type' => $type,
|
648 |
+
'guessType' => $guessType,
|
649 |
+
'childrenKeys' => array(),
|
650 |
+
'aggregKeys' => array()
|
651 |
+
);
|
652 |
+
|
653 |
+
if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE] == true
|
654 |
+
&& (count($attribs) > 0)
|
655 |
+
) {
|
656 |
+
$val['children'] = array();
|
657 |
+
$val['type'] = $this->_getComplexType($element);
|
658 |
+
$val['class'] = $element;
|
659 |
+
|
660 |
+
if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
|
661 |
+
$attribs = $this->_guessAndSetTypes($attribs);
|
662 |
+
}
|
663 |
+
if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY] != false
|
664 |
+
) {
|
665 |
+
$val['children'][$this->
|
666 |
+
options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY]] = $attribs;
|
667 |
+
} else {
|
668 |
+
foreach ($attribs as $attrib => $value) {
|
669 |
+
$val['children'][$this->
|
670 |
+
options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND]
|
671 |
+
. $attrib] = $value;
|
672 |
+
}
|
673 |
+
}
|
674 |
+
}
|
675 |
+
|
676 |
+
$keyAttr = false;
|
677 |
+
|
678 |
+
if (is_string($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
|
679 |
+
$keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY];
|
680 |
+
} elseif (is_array($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
|
681 |
+
if (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
|
682 |
+
[$element])
|
683 |
+
) {
|
684 |
+
$keyAttr =
|
685 |
+
$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element];
|
686 |
+
} elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
|
687 |
+
['#default'])
|
688 |
+
) {
|
689 |
+
$keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
|
690 |
+
['#default'];
|
691 |
+
} elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
|
692 |
+
['__default'])
|
693 |
+
) {
|
694 |
+
// keep this for BC
|
695 |
+
$keyAttr =
|
696 |
+
$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
|
697 |
+
['__default'];
|
698 |
+
}
|
699 |
+
}
|
700 |
+
|
701 |
+
if ($keyAttr !== false && isset($attribs[$keyAttr])) {
|
702 |
+
$val['name'] = $attribs[$keyAttr];
|
703 |
+
}
|
704 |
+
|
705 |
+
if (isset($attribs[$this->
|
706 |
+
options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]])
|
707 |
+
) {
|
708 |
+
$val['class'] =
|
709 |
+
$attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]];
|
710 |
+
}
|
711 |
+
|
712 |
+
array_push($this->_valStack, $val);
|
713 |
+
}
|
714 |
+
|
715 |
+
/**
|
716 |
+
* Try to guess the type of several values and
|
717 |
+
* set them accordingly
|
718 |
+
*
|
719 |
+
* @param array $array array containing the values
|
720 |
+
*
|
721 |
+
* @return array array, containing the values with their correct types
|
722 |
+
* @access private
|
723 |
+
*/
|
724 |
+
function _guessAndSetTypes($array)
|
725 |
+
{
|
726 |
+
foreach ($array as $key => $value) {
|
727 |
+
$array[$key] = $this->_guessAndSetType($value);
|
728 |
+
}
|
729 |
+
return $array;
|
730 |
+
}
|
731 |
+
|
732 |
+
/**
|
733 |
+
* Try to guess the type of a value and
|
734 |
+
* set it accordingly
|
735 |
+
*
|
736 |
+
* @param string $value character data
|
737 |
+
*
|
738 |
+
* @return mixed value with the best matching type
|
739 |
+
* @access private
|
740 |
+
*/
|
741 |
+
function _guessAndSetType($value)
|
742 |
+
{
|
743 |
+
if ($value === 'true') {
|
744 |
+
return true;
|
745 |
+
}
|
746 |
+
if ($value === 'false') {
|
747 |
+
return false;
|
748 |
+
}
|
749 |
+
if ($value === 'NULL') {
|
750 |
+
return null;
|
751 |
+
}
|
752 |
+
if (preg_match('/^[-+]?[0-9]{1,}$/', $value)) {
|
753 |
+
return intval($value);
|
754 |
+
}
|
755 |
+
if (preg_match('/^[-+]?[0-9]{1,}\.[0-9]{1,}$/', $value)) {
|
756 |
+
return doubleval($value);
|
757 |
+
}
|
758 |
+
return (string)$value;
|
759 |
+
}
|
760 |
+
|
761 |
+
/**
|
762 |
+
* End element handler for XML parser
|
763 |
+
*
|
764 |
+
* @param object $parser XML parser object
|
765 |
+
* @param string $element element
|
766 |
+
*
|
767 |
+
* @return void
|
768 |
+
* @access private
|
769 |
+
*/
|
770 |
+
function endHandler($parser, $element)
|
771 |
+
{
|
772 |
+
$value = array_pop($this->_valStack);
|
773 |
+
switch ($this->options[XML_UNSERIALIZER_OPTION_WHITESPACE]) {
|
774 |
+
case XML_UNSERIALIZER_WHITESPACE_KEEP:
|
775 |
+
$data = $this->_dataStack[$this->_depth];
|
776 |
+
break;
|
777 |
+
case XML_UNSERIALIZER_WHITESPACE_NORMALIZE:
|
778 |
+
$data = trim(preg_replace('/\s\s+/m', ' ',
|
779 |
+
$this->_dataStack[$this->_depth]));
|
780 |
+
break;
|
781 |
+
case XML_UNSERIALIZER_WHITESPACE_TRIM:
|
782 |
+
default:
|
783 |
+
$data = trim($this->_dataStack[$this->_depth]);
|
784 |
+
break;
|
785 |
+
}
|
786 |
+
|
787 |
+
// adjust type of the value
|
788 |
+
switch(strtolower($value['type'])) {
|
789 |
+
|
790 |
+
// unserialize an object
|
791 |
+
case 'object':
|
792 |
+
if (isset($value['class'])) {
|
793 |
+
$classname = $value['class'];
|
794 |
+
} else {
|
795 |
+
$classname = '';
|
796 |
+
}
|
797 |
+
// instantiate the class
|
798 |
+
if ($this->options[XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME] === true
|
799 |
+
&& class_exists($classname)
|
800 |
+
) {
|
801 |
+
$value['value'] = &new $classname;
|
802 |
+
} else {
|
803 |
+
$value['value'] =
|
804 |
+
&new $this->options[XML_UNSERIALIZER_OPTION_DEFAULT_CLASS];
|
805 |
+
}
|
806 |
+
if (trim($data) !== '') {
|
807 |
+
if ($value['guessType'] === true) {
|
808 |
+
$data = $this->_guessAndSetType($data);
|
809 |
+
}
|
810 |
+
$value['children'][$this->
|
811 |
+
options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
|
812 |
+
}
|
813 |
+
|
814 |
+
// set properties
|
815 |
+
foreach ($value['children'] as $prop => $propVal) {
|
816 |
+
// check whether there is a special method to set this property
|
817 |
+
$setMethod = 'set'.$prop;
|
818 |
+
if (method_exists($value['value'], $setMethod)) {
|
819 |
+
call_user_func(array(&$value['value'], $setMethod), $propVal);
|
820 |
+
} else {
|
821 |
+
$value['value']->$prop = $propVal;
|
822 |
+
}
|
823 |
+
}
|
824 |
+
// check for magic function
|
825 |
+
if (method_exists($value['value'], '__wakeup')) {
|
826 |
+
$value['value']->__wakeup();
|
827 |
+
}
|
828 |
+
break;
|
829 |
+
|
830 |
+
// unserialize an array
|
831 |
+
case 'array':
|
832 |
+
if (trim($data) !== '') {
|
833 |
+
if ($value['guessType'] === true) {
|
834 |
+
$data = $this->_guessAndSetType($data);
|
835 |
+
}
|
836 |
+
$value['children'][$this->
|
837 |
+
options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
|
838 |
+
}
|
839 |
+
if (isset($value['children'])) {
|
840 |
+
$value['value'] = $value['children'];
|
841 |
+
} else {
|
842 |
+
$value['value'] = array();
|
843 |
+
}
|
844 |
+
break;
|
845 |
+
|
846 |
+
// unserialize a null value
|
847 |
+
case 'null':
|
848 |
+
$data = null;
|
849 |
+
break;
|
850 |
+
|
851 |
+
// unserialize a resource => this is not possible :-(
|
852 |
+
case 'resource':
|
853 |
+
$value['value'] = $data;
|
854 |
+
break;
|
855 |
+
|
856 |
+
// unserialize any scalar value
|
857 |
+
default:
|
858 |
+
if ($value['guessType'] === true) {
|
859 |
+
$data = $this->_guessAndSetType($data);
|
860 |
+
} else {
|
861 |
+
settype($data, $value['type']);
|
862 |
+
}
|
863 |
+
|
864 |
+
$value['value'] = $data;
|
865 |
+
break;
|
866 |
+
}
|
867 |
+
$parent = array_pop($this->_valStack);
|
868 |
+
if ($parent === null) {
|
869 |
+
$this->_unserializedData = &$value['value'];
|
870 |
+
$this->_root = &$value['name'];
|
871 |
+
return true;
|
872 |
+
} else {
|
873 |
+
// parent has to be an array
|
874 |
+
if (!isset($parent['children']) || !is_array($parent['children'])) {
|
875 |
+
$parent['children'] = array();
|
876 |
+
if (!in_array($parent['type'], array('array', 'object'))) {
|
877 |
+
$parent['type'] = $this->_getComplexType($parent['name']);
|
878 |
+
if ($parent['type'] == 'object') {
|
879 |
+
$parent['class'] = $parent['name'];
|
880 |
+
}
|
881 |
+
}
|
882 |
+
}
|
883 |
+
|
884 |
+
if (in_array($element,
|
885 |
+
$this->options[XML_UNSERIALIZER_OPTION_IGNORE_KEYS])
|
886 |
+
) {
|
887 |
+
$ignoreKey = true;
|
888 |
+
} else {
|
889 |
+
$ignoreKey = false;
|
890 |
+
}
|
891 |
+
|
892 |
+
if (!empty($value['name']) && $ignoreKey === false) {
|
893 |
+
// there already has been a tag with this name
|
894 |
+
if (in_array($value['name'], $parent['childrenKeys'])
|
895 |
+
|| in_array($value['name'],
|
896 |
+
$this->options[XML_UNSERIALIZER_OPTION_FORCE_ENUM])
|
897 |
+
) {
|
898 |
+
// no aggregate has been created for this tag
|
899 |
+
if (!in_array($value['name'], $parent['aggregKeys'])) {
|
900 |
+
if (isset($parent['children'][$value['name']])) {
|
901 |
+
$parent['children'][$value['name']] =
|
902 |
+
array($parent['children'][$value['name']]);
|
903 |
+
} else {
|
904 |
+
$parent['children'][$value['name']] = array();
|
905 |
+
}
|
906 |
+
array_push($parent['aggregKeys'], $value['name']);
|
907 |
+
}
|
908 |
+
array_push($parent['children'][$value['name']], $value['value']);
|
909 |
+
} else {
|
910 |
+
$parent['children'][$value['name']] = &$value['value'];
|
911 |
+
array_push($parent['childrenKeys'], $value['name']);
|
912 |
+
}
|
913 |
+
} else {
|
914 |
+
array_push($parent['children'], $value['value']);
|
915 |
+
}
|
916 |
+
array_push($this->_valStack, $parent);
|
917 |
+
}
|
918 |
+
|
919 |
+
$this->_depth--;
|
920 |
+
}
|
921 |
+
|
922 |
+
/**
|
923 |
+
* Handler for character data
|
924 |
+
*
|
925 |
+
* @param object $parser XML parser object
|
926 |
+
* @param string $cdata CDATA
|
927 |
+
*
|
928 |
+
* @return void
|
929 |
+
* @access private
|
930 |
+
*/
|
931 |
+
function cdataHandler($parser, $cdata)
|
932 |
+
{
|
933 |
+
if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
|
934 |
+
$cdata = call_user_func($this->
|
935 |
+
options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $cdata);
|
936 |
+
}
|
937 |
+
$this->_dataStack[$this->_depth] .= $cdata;
|
938 |
+
}
|
939 |
+
|
940 |
+
/**
|
941 |
+
* get the complex type, that should be used for a specified tag
|
942 |
+
*
|
943 |
+
* @param string $tagname name of the tag
|
944 |
+
*
|
945 |
+
* @return string complex type ('array' or 'object')
|
946 |
+
* @access private
|
947 |
+
*/
|
948 |
+
function _getComplexType($tagname)
|
949 |
+
{
|
950 |
+
if (is_string($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE])) {
|
951 |
+
return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE];
|
952 |
+
}
|
953 |
+
if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname])) {
|
954 |
+
return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname];
|
955 |
+
}
|
956 |
+
if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'])) {
|
957 |
+
return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'];
|
958 |
+
}
|
959 |
+
return 'array';
|
960 |
+
}
|
961 |
+
|
962 |
+
/**
|
963 |
+
* create the XML_Parser instance
|
964 |
+
*
|
965 |
+
* @return boolean
|
966 |
+
* @access private
|
967 |
+
*/
|
968 |
+
function _createParser()
|
969 |
+
{
|
970 |
+
if (is_object($this->_parser)) {
|
971 |
+
$this->_parser->free();
|
972 |
+
unset($this->_parser);
|
973 |
+
}
|
974 |
+
$this->_parser = &new XML_Parser($this->
|
975 |
+
options[XML_UNSERIALIZER_OPTION_ENCODING_SOURCE],
|
976 |
+
'event', $this->options[XML_UNSERIALIZER_OPTION_ENCODING_TARGET]);
|
977 |
+
|
978 |
+
$this->_parser->folding = false;
|
979 |
+
$this->_parser->setHandlerObj($this);
|
980 |
+
return true;
|
981 |
+
}
|
982 |
+
}
|
983 |
+
?>
|
package.xml
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<package>
|
3 |
+
<name>Lib_PEAR</name>
|
4 |
+
<version>1.3.1</version>
|
5 |
+
<stability>stable</stability>
|
6 |
+
<license>Mixed</license>
|
7 |
+
<channel>community</channel>
|
8 |
+
<extends/>
|
9 |
+
<summary>PEAR Library</summary>
|
10 |
+
<description>PEAR Library</description>
|
11 |
+
<notes>1.3.1</notes>
|
12 |
+
<authors><author><name>Magento Core Team</name><user>auto-converted</user><email>core@magentocommerce.com</email></author></authors>
|
13 |
+
<date>2009-04-18</date>
|
14 |
+
<time>01:26:32</time>
|
15 |
+
<contents><target name="magelib"><dir name="PEAR"><dir name="HTTP"><dir name="Request"><file name="Listener.php" hash="77b53a700acc70a55713efd0642bc28a"/></dir><file name="HTTP.php" hash="b6408ededd0ec0d585b84951b7054810"/><file name="Request.php" hash="e409fdbe1518e11e16943f5185eccc2b"/></dir><dir name="Mail"><file name="mime.php" hash="93a00275edb0d9e94364ae11175741d1"/><file name="mimeDecode.php" hash="0e3cc74c0631a0e99e861caab50d763b"/><file name="mimePart.php" hash="53aedbcc104a9bd15cf96e5511b6498b"/><file name="xmail.dtd" hash="194810c478066eaeb28f51116b88e25a"/><file name="xmail.xsl" hash="61cea06fb6b4bd3a4b5e2d37384e14a9"/></dir><dir name="Net"><file name="Socket.php" hash="ea6a2a5f9ea70bc5ef25f03b0234cc08"/><file name="URL.php" hash="e7042d4176244ed99fe4b9c8e8cc6fb5"/></dir><dir name="PEAR"><file name="PEAR.php" hash="bd7b708d096f50fc873a2079fe03fd04"/></dir><dir name="SOAP"><dir name="Transport"><file name="HTTP.php" hash="7f3b3f8680fc1a337a6992056e6d41f4"/></dir><file name="Base.php" hash="7320b423fcdfb397c6a99cb7c585ac62"/><file name="Client.php" hash="1dd525ee520007e2d7651231c0dfefe3"/><file name="Fault.php" hash="34b9f405ebde848809b27ca2b16f1095"/><file name="Parser.php" hash="5c01a59b754194453a04fe24adb43ca4"/><file name="Transport.php" hash="0429fcddac603adc76c34c413705bd86"/><file name="Value.php" hash="026bce92aa9f922f0d8b410ff0f7ae3b"/><file name="WSDL.php" hash="67394dd9431b6970eac9a32df6171f2a"/></dir><dir name="XML"><dir name="Parser"><file name="Simple.php" hash="d11a9e750faecbfba6cc0c7a9eee5dcb"/></dir><file name="Parser.php" hash="f457617bc7ab0002f7374e9cca379681"/><file name="Serializer.php" hash="3fc9cbf6dff9b95372d05cb63854984d"/><file name="Unserializer.php" hash="937ba82a91ee40fa5a34911e70efaf3a"/></dir><file name="PEAR.php" hash="bd7b708d096f50fc873a2079fe03fd04"/></dir></target></contents>
|
16 |
+
<compatible/>
|
17 |
+
<dependencies/>
|
18 |
+
</package>
|