Редакция 69 | Содержимое файла | Сравнить с предыдущей | Последнее изменение | Открыть журнал | RSS
Редакция | Автор | № строки | Строка |
---|---|---|---|
69 | alex-w | 1 | <?php |
2 | /** |
||
3 | * PEAR_PackageFile, package.xml parsing utility class |
||
4 | * |
||
5 | * PHP versions 4 and 5 |
||
6 | * |
||
7 | * LICENSE: This source file is subject to version 3.0 of the PHP license |
||
8 | * that is available through the world-wide-web at the following URI: |
||
9 | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
||
10 | * the PHP License and are unable to obtain it through the web, please |
||
11 | * send a note to license@php.net so we can mail you a copy immediately. |
||
12 | * |
||
13 | * @category pear |
||
14 | * @package PEAR |
||
15 | * @author Greg Beaver <cellog@php.net> |
||
16 | * @copyright 1997-2008 The PHP Group |
||
17 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
18 | * @version CVS: $Id: PackageFile.php,v 1.41 2008/01/03 20:26:36 cellog Exp $ |
||
19 | * @link http://pear.php.net/package/PEAR |
||
20 | * @since File available since Release 1.4.0a1 |
||
21 | */ |
||
22 | |||
23 | /** |
||
24 | * needed for PEAR_VALIDATE_* constants |
||
25 | */ |
||
26 | require_once 'PEAR/Validate.php'; |
||
27 | /** |
||
28 | * Error code if the package.xml <package> tag does not contain a valid version |
||
29 | */ |
||
30 | define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1); |
||
31 | /** |
||
32 | * Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions, |
||
33 | * currently |
||
34 | */ |
||
35 | define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2); |
||
36 | /** |
||
37 | * Abstraction for the package.xml package description file |
||
38 | * |
||
39 | * @category pear |
||
40 | * @package PEAR |
||
41 | * @author Greg Beaver <cellog@php.net> |
||
42 | * @copyright 1997-2008 The PHP Group |
||
43 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
44 | * @version Release: 1.7.2 |
||
45 | * @link http://pear.php.net/package/PEAR |
||
46 | * @since Class available since Release 1.4.0a1 |
||
47 | */ |
||
48 | class PEAR_PackageFile |
||
49 | { |
||
50 | /** |
||
51 | * @var PEAR_Config |
||
52 | */ |
||
53 | var $_config; |
||
54 | var $_debug; |
||
55 | /** |
||
56 | * Temp directory for uncompressing tgz files. |
||
57 | * @var string|false |
||
58 | */ |
||
59 | var $_tmpdir; |
||
60 | var $_logger = false; |
||
61 | /** |
||
62 | * @var boolean |
||
63 | */ |
||
64 | var $_rawReturn = false; |
||
65 | |||
66 | /** |
||
67 | * |
||
68 | * @param PEAR_Config $config |
||
69 | * @param ? $debug |
||
70 | * @param string @tmpdir Optional temporary directory for uncompressing |
||
71 | * files |
||
72 | */ |
||
73 | function PEAR_PackageFile(&$config, $debug = false, $tmpdir = false) |
||
74 | { |
||
75 | $this->_config = $config; |
||
76 | $this->_debug = $debug; |
||
77 | $this->_tmpdir = $tmpdir; |
||
78 | } |
||
79 | |||
80 | /** |
||
81 | * Turn off validation - return a parsed package.xml without checking it |
||
82 | * |
||
83 | * This is used by the package-validate command |
||
84 | */ |
||
85 | function rawReturn() |
||
86 | { |
||
87 | $this->_rawReturn = true; |
||
88 | } |
||
89 | |||
90 | function setLogger(&$l) |
||
91 | { |
||
92 | $this->_logger = &$l; |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Create a PEAR_PackageFile_Parser_v* of a given version. |
||
97 | * @param int $version |
||
98 | * @return PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1 |
||
99 | */ |
||
100 | function &parserFactory($version) |
||
101 | { |
||
102 | if (!in_array($version{0}, array('1', '2'))) { |
||
103 | $a = false; |
||
104 | return $a; |
||
105 | } |
||
106 | include_once 'PEAR/PackageFile/Parser/v' . $version{0} . '.php'; |
||
107 | $version = $version{0}; |
||
108 | $class = "PEAR_PackageFile_Parser_v$version"; |
||
109 | $a = new $class; |
||
110 | return $a; |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * For simpler unit-testing |
||
115 | * @return string |
||
116 | */ |
||
117 | function getClassPrefix() |
||
118 | { |
||
119 | return 'PEAR_PackageFile_v'; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * Create a PEAR_PackageFile_v* of a given version. |
||
124 | * @param int $version |
||
125 | * @return PEAR_PackageFile_v1|PEAR_PackageFile_v1 |
||
126 | */ |
||
127 | function &factory($version) |
||
128 | { |
||
129 | if (!in_array($version{0}, array('1', '2'))) { |
||
130 | $a = false; |
||
131 | return $a; |
||
132 | } |
||
133 | include_once 'PEAR/PackageFile/v' . $version{0} . '.php'; |
||
134 | $version = $version{0}; |
||
135 | $class = $this->getClassPrefix() . $version; |
||
136 | $a = new $class; |
||
137 | return $a; |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Create a PEAR_PackageFile_v* from its toArray() method |
||
142 | * |
||
143 | * WARNING: no validation is performed, the array is assumed to be valid, |
||
144 | * always parse from xml if you want validation. |
||
145 | * @param array $arr |
||
146 | * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2 |
||
147 | * @uses factory() to construct the returned object. |
||
148 | */ |
||
149 | function &fromArray($arr) |
||
150 | { |
||
151 | if (isset($arr['xsdversion'])) { |
||
152 | $obj = &$this->factory($arr['xsdversion']); |
||
153 | if ($this->_logger) { |
||
154 | $obj->setLogger($this->_logger); |
||
155 | } |
||
156 | $obj->setConfig($this->_config); |
||
157 | $obj->fromArray($arr); |
||
158 | return $obj; |
||
159 | } else { |
||
160 | if (isset($arr['package']['attribs']['version'])) { |
||
161 | $obj = &$this->factory($arr['package']['attribs']['version']); |
||
162 | } else { |
||
163 | $obj = &$this->factory('1.0'); |
||
164 | } |
||
165 | if ($this->_logger) { |
||
166 | $obj->setLogger($this->_logger); |
||
167 | } |
||
168 | $obj->setConfig($this->_config); |
||
169 | $obj->fromArray($arr); |
||
170 | return $obj; |
||
171 | } |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Create a PEAR_PackageFile_v* from an XML string. |
||
176 | * @access public |
||
177 | * @param string $data contents of package.xml file |
||
178 | * @param int $state package state (one of PEAR_VALIDATE_* constants) |
||
179 | * @param string $file full path to the package.xml file (and the files |
||
180 | * it references) |
||
181 | * @param string $archive optional name of the archive that the XML was |
||
182 | * extracted from, if any |
||
183 | * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 |
||
184 | * @uses parserFactory() to construct a parser to load the package. |
||
185 | */ |
||
186 | function &fromXmlString($data, $state, $file, $archive = false) |
||
187 | { |
||
188 | if (preg_match('/<package[^>]+version="([0-9]+\.[0-9]+)"/', $data, $packageversion)) { |
||
189 | if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) { |
||
190 | return PEAR::raiseError('package.xml version "' . $packageversion[1] . |
||
191 | '" is not supported, only 1.0, 2.0, and 2.1 are supported.'); |
||
192 | } |
||
193 | $object = &$this->parserFactory($packageversion[1]); |
||
194 | if ($this->_logger) { |
||
195 | $object->setLogger($this->_logger); |
||
196 | } |
||
197 | $object->setConfig($this->_config); |
||
198 | $pf = $object->parse($data, $file, $archive); |
||
199 | if (PEAR::isError($pf)) { |
||
200 | return $pf; |
||
201 | } |
||
202 | if ($this->_rawReturn) { |
||
203 | return $pf; |
||
204 | } |
||
205 | if ($pf->validate($state)) { |
||
206 | if ($this->_logger) { |
||
207 | if ($pf->getValidationWarnings(false)) { |
||
208 | foreach ($pf->getValidationWarnings() as $warning) { |
||
209 | $this->_logger->log(0, 'WARNING: ' . $warning['message']); |
||
210 | } |
||
211 | } |
||
212 | } |
||
213 | if (method_exists($pf, 'flattenFilelist')) { |
||
214 | $pf->flattenFilelist(); // for v2 |
||
215 | } |
||
216 | return $pf; |
||
217 | } else { |
||
218 | if ($this->_config->get('verbose') > 0) { |
||
219 | if ($this->_logger) { |
||
220 | if ($pf->getValidationWarnings(false)) { |
||
221 | foreach ($pf->getValidationWarnings(false) as $warning) { |
||
222 | $this->_logger->log(0, 'ERROR: ' . $warning['message']); |
||
223 | } |
||
224 | } |
||
225 | } |
||
226 | } |
||
227 | $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', |
||
228 | 2, null, null, $pf->getValidationWarnings()); |
||
229 | return $a; |
||
230 | } |
||
231 | } elseif (preg_match('/<package[^>]+version="([^"]+)"/', $data, $packageversion)) { |
||
232 | $a = PEAR::raiseError('package.xml file "' . $file . |
||
233 | '" has unsupported package.xml <package> version "' . $packageversion[1] . '"'); |
||
234 | return $a; |
||
235 | } else { |
||
236 | if (!class_exists('PEAR_ErrorStack')) { |
||
237 | require_once 'PEAR/ErrorStack.php'; |
||
238 | } |
||
239 | PEAR_ErrorStack::staticPush('PEAR_PackageFile', |
||
240 | PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION, |
||
241 | 'warning', array('xml' => $data), 'package.xml "' . $file . |
||
242 | '" has no package.xml <package> version'); |
||
243 | $object = &$this->parserFactory('1.0'); |
||
244 | $object->setConfig($this->_config); |
||
245 | $pf = $object->parse($data, $file, $archive); |
||
246 | if (PEAR::isError($pf)) { |
||
247 | return $pf; |
||
248 | } |
||
249 | if ($this->_rawReturn) { |
||
250 | return $pf; |
||
251 | } |
||
252 | if ($pf->validate($state)) { |
||
253 | if ($this->_logger) { |
||
254 | if ($pf->getValidationWarnings(false)) { |
||
255 | foreach ($pf->getValidationWarnings() as $warning) { |
||
256 | $this->_logger->log(0, 'WARNING: ' . $warning['message']); |
||
257 | } |
||
258 | } |
||
259 | } |
||
260 | if (method_exists($pf, 'flattenFilelist')) { |
||
261 | $pf->flattenFilelist(); // for v2 |
||
262 | } |
||
263 | return $pf; |
||
264 | } else { |
||
265 | $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', |
||
266 | 2, null, null, $pf->getValidationWarnings()); |
||
267 | return $a; |
||
268 | } |
||
269 | } |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * Register a temporary file or directory. When the destructor is |
||
274 | * executed, all registered temporary files and directories are |
||
275 | * removed. |
||
276 | * |
||
277 | * @param string $file name of file or directory |
||
278 | * @return void |
||
279 | */ |
||
280 | function addTempFile($file) |
||
281 | { |
||
282 | $GLOBALS['_PEAR_Common_tempfiles'][] = $file; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Create a PEAR_PackageFile_v* from a compresed Tar or Tgz file. |
||
287 | * @access public |
||
288 | * @param string contents of package.xml file |
||
289 | * @param int package state (one of PEAR_VALIDATE_* constants) |
||
290 | * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 |
||
291 | * @using Archive_Tar to extract the files |
||
292 | * @using fromPackageFile() to load the package after the package.xml |
||
293 | * file is extracted. |
||
294 | */ |
||
295 | function &fromTgzFile($file, $state) |
||
296 | { |
||
297 | if (!class_exists('Archive_Tar')) { |
||
298 | require_once 'Archive/Tar.php'; |
||
299 | } |
||
300 | $tar = new Archive_Tar($file); |
||
301 | if ($this->_debug <= 1) { |
||
302 | $tar->pushErrorHandling(PEAR_ERROR_RETURN); |
||
303 | } |
||
304 | $content = $tar->listContent(); |
||
305 | if ($this->_debug <= 1) { |
||
306 | $tar->popErrorHandling(); |
||
307 | } |
||
308 | if (!is_array($content)) { |
||
309 | if (is_string($file) && strlen($file < 255) && |
||
310 | (!file_exists($file) || !@is_file($file))) { |
||
311 | $ret = PEAR::raiseError("could not open file \"$file\""); |
||
312 | return $ret; |
||
313 | } |
||
314 | $file = realpath($file); |
||
315 | $ret = PEAR::raiseError("Could not get contents of package \"$file\"". |
||
316 | '. Invalid tgz file.'); |
||
317 | return $ret; |
||
318 | } else { |
||
319 | if (!count($content) && !@is_file($file)) { |
||
320 | $ret = PEAR::raiseError("could not open file \"$file\""); |
||
321 | return $ret; |
||
322 | } |
||
323 | } |
||
324 | $xml = null; |
||
325 | $origfile = $file; |
||
326 | foreach ($content as $file) { |
||
327 | $name = $file['filename']; |
||
328 | if ($name == 'package2.xml') { // allow a .tgz to distribute both versions |
||
329 | $xml = $name; |
||
330 | break; |
||
331 | } |
||
332 | if ($name == 'package.xml') { |
||
333 | $xml = $name; |
||
334 | break; |
||
335 | } elseif (ereg('package.xml$', $name, $match)) { |
||
336 | $xml = $name; |
||
337 | break; |
||
338 | } |
||
339 | } |
||
340 | if ($this->_tmpdir) { |
||
341 | $tmpdir = $this->_tmpdir; |
||
342 | } else { |
||
343 | $tmpdir = System::mkTemp(array('-d', 'pear')); |
||
344 | PEAR_PackageFile::addTempFile($tmpdir); |
||
345 | } |
||
346 | $this->_extractErrors(); |
||
347 | PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors')); |
||
348 | if (!$xml || !$tar->extractList(array($xml), $tmpdir)) { |
||
349 | $extra = implode("\n", $this->_extractErrors()); |
||
350 | if ($extra) { |
||
351 | $extra = ' ' . $extra; |
||
352 | } |
||
353 | PEAR::staticPopErrorHandling(); |
||
354 | $ret = PEAR::raiseError('could not extract the package.xml file from "' . |
||
355 | $origfile . '"' . $extra); |
||
356 | return $ret; |
||
357 | } |
||
358 | PEAR::staticPopErrorHandling(); |
||
359 | $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile); |
||
360 | return $ret; |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * helper for extracting Archive_Tar errors |
||
365 | * @var array |
||
366 | * @access private |
||
367 | */ |
||
368 | var $_extractErrors = array(); |
||
369 | |||
370 | /** |
||
371 | * helper callback for extracting Archive_Tar errors |
||
372 | * |
||
373 | * @param PEAR_Error|null $err |
||
374 | * @return array |
||
375 | * @access private |
||
376 | */ |
||
377 | function _extractErrors($err = null) |
||
378 | { |
||
379 | static $errors = array(); |
||
380 | if ($err === null) { |
||
381 | $e = $errors; |
||
382 | $errors = array(); |
||
383 | return $e; |
||
384 | } |
||
385 | $errors[] = $err->getMessage(); |
||
386 | } |
||
387 | |||
388 | /** |
||
389 | * Create a PEAR_PackageFile_v* from a package.xml file. |
||
390 | * |
||
391 | * @access public |
||
392 | * @param string $descfile name of package xml file |
||
393 | * @param int $state package state (one of PEAR_VALIDATE_* constants) |
||
394 | * @param string|false $archive name of the archive this package.xml came |
||
395 | * from, if any |
||
396 | * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 |
||
397 | * @uses PEAR_PackageFile::fromXmlString to create the oject after the |
||
398 | * XML is loaded from the package.xml file. |
||
399 | */ |
||
400 | function &fromPackageFile($descfile, $state, $archive = false) |
||
401 | { |
||
402 | if (is_string($descfile) && strlen($descfile) < 255 && |
||
403 | (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) || |
||
404 | (!$fp = @fopen($descfile, 'r')))) { |
||
405 | $a = PEAR::raiseError("Unable to open $descfile"); |
||
406 | return $a; |
||
407 | } |
||
408 | |||
409 | // read the whole thing so we only get one cdata callback |
||
410 | // for each block of cdata |
||
411 | fclose($fp); |
||
412 | $data = file_get_contents($descfile); |
||
413 | $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive); |
||
414 | return $ret; |
||
415 | } |
||
416 | |||
417 | |||
418 | /** |
||
419 | * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file. |
||
420 | * |
||
421 | * This method is able to extract information about a package from a .tgz |
||
422 | * archive or from a XML package definition file. |
||
423 | * |
||
424 | * @access public |
||
425 | * @param string $info file name |
||
426 | * @param int $state package state (one of PEAR_VALIDATE_* constants) |
||
427 | * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 |
||
428 | * @uses fromPackageFile() if the file appears to be XML |
||
429 | * @uses fromTgzFile() to load all non-XML files |
||
430 | */ |
||
431 | function &fromAnyFile($info, $state) |
||
432 | { |
||
433 | if (is_dir($info)) { |
||
434 | $dir_name = realpath($info); |
||
435 | if (file_exists($dir_name . '/package.xml')) { |
||
436 | $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package.xml', $state); |
||
437 | } elseif (file_exists($dir_name . '/package2.xml')) { |
||
438 | $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package2.xml', $state); |
||
439 | } else { |
||
440 | $info = PEAR::raiseError("No package definition found in '$info' directory"); |
||
441 | } |
||
442 | return $info; |
||
443 | } |
||
444 | |||
445 | $fp = false; |
||
446 | if (is_string($info) && strlen($info) < 255 && |
||
447 | (file_exists($info) || ($fp = @fopen($info, 'r')))) { |
||
448 | if ($fp) { |
||
449 | fclose($fp); |
||
450 | } |
||
451 | $tmp = substr($info, -4); |
||
452 | if ($tmp == '.xml') { |
||
453 | $info = &PEAR_PackageFile::fromPackageFile($info, $state); |
||
454 | } elseif ($tmp == '.tar' || $tmp == '.tgz') { |
||
455 | $info = &PEAR_PackageFile::fromTgzFile($info, $state); |
||
456 | } else { |
||
457 | $fp = fopen($info, "r"); |
||
458 | $test = fread($fp, 5); |
||
459 | fclose($fp); |
||
460 | if ($test == "<?xml") { |
||
461 | $info = &PEAR_PackageFile::fromPackageFile($info, $state); |
||
462 | } else { |
||
463 | $info = &PEAR_PackageFile::fromTgzFile($info, $state); |
||
464 | } |
||
465 | } |
||
466 | } else { |
||
467 | $info = PEAR::raiseError("Cannot open '$info' for parsing"); |
||
468 | return $info; |
||
469 | } |
||
470 | return $info; |
||
471 | } |
||
472 | } |
||
473 | |||
474 | ?> |