friendica/vendor/pear-pear.php.net/PEAR/PEAR/PackageFile.php

492 lines
16 KiB
PHP

<?php
/**
* PEAR_PackageFile, package.xml parsing utility class
*
* PHP versions 4 and 5
*
* @category pear
* @package PEAR
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @link http://pear.php.net/package/PEAR
* @since File available since Release 1.4.0a1
*/
/**
* needed for PEAR_VALIDATE_* constants
*/
require_once 'PEAR/Validate.php';
/**
* Error code if the package.xml <package> tag does not contain a valid version
*/
define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1);
/**
* Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions,
* currently
*/
define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2);
/**
* Abstraction for the package.xml package description file
*
* @category pear
* @package PEAR
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version Release: 1.10.5
* @link http://pear.php.net/package/PEAR
* @since Class available since Release 1.4.0a1
*/
class PEAR_PackageFile
{
/**
* @var PEAR_Config
*/
var $_config;
var $_debug;
var $_logger = false;
/**
* @var boolean
*/
var $_rawReturn = false;
/**
* helper for extracting Archive_Tar errors
* @var array
* @access private
*/
var $_extractErrors = array();
/**
*
* @param PEAR_Config $config
* @param ? $debug
* @param string @tmpdir Optional temporary directory for uncompressing
* files
*/
function __construct(&$config, $debug = false)
{
$this->_config = $config;
$this->_debug = $debug;
}
/**
* Turn off validation - return a parsed package.xml without checking it
*
* This is used by the package-validate command
*/
function rawReturn()
{
$this->_rawReturn = true;
}
function setLogger(&$l)
{
$this->_logger = &$l;
}
/**
* Create a PEAR_PackageFile_Parser_v* of a given version.
* @param int $version
* @return PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1
*/
function &parserFactory($version)
{
if (!in_array($version{0}, array('1', '2'))) {
$a = false;
return $a;
}
include_once 'PEAR/PackageFile/Parser/v' . $version{0} . '.php';
$version = $version{0};
$class = "PEAR_PackageFile_Parser_v$version";
$a = new $class;
return $a;
}
/**
* For simpler unit-testing
* @return string
*/
function getClassPrefix()
{
return 'PEAR_PackageFile_v';
}
/**
* Create a PEAR_PackageFile_v* of a given version.
* @param int $version
* @return PEAR_PackageFile_v1|PEAR_PackageFile_v1
*/
function &factory($version)
{
if (!in_array($version{0}, array('1', '2'))) {
$a = false;
return $a;
}
include_once 'PEAR/PackageFile/v' . $version{0} . '.php';
$version = $version{0};
$class = $this->getClassPrefix() . $version;
$a = new $class;
return $a;
}
/**
* Create a PEAR_PackageFile_v* from its toArray() method
*
* WARNING: no validation is performed, the array is assumed to be valid,
* always parse from xml if you want validation.
* @param array $arr
* @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2
* @uses factory() to construct the returned object.
*/
function &fromArray($arr)
{
if (isset($arr['xsdversion'])) {
$obj = &$this->factory($arr['xsdversion']);
if ($this->_logger) {
$obj->setLogger($this->_logger);
}
$obj->setConfig($this->_config);
$obj->fromArray($arr);
return $obj;
}
if (isset($arr['package']['attribs']['version'])) {
$obj = &$this->factory($arr['package']['attribs']['version']);
} else {
$obj = &$this->factory('1.0');
}
if ($this->_logger) {
$obj->setLogger($this->_logger);
}
$obj->setConfig($this->_config);
$obj->fromArray($arr);
return $obj;
}
/**
* Create a PEAR_PackageFile_v* from an XML string.
* @access public
* @param string $data contents of package.xml file
* @param int $state package state (one of PEAR_VALIDATE_* constants)
* @param string $file full path to the package.xml file (and the files
* it references)
* @param string $archive optional name of the archive that the XML was
* extracted from, if any
* @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
* @uses parserFactory() to construct a parser to load the package.
*/
function &fromXmlString($data, $state, $file, $archive = false)
{
if (preg_match('/<package[^>]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) {
if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) {
return PEAR::raiseError('package.xml version "' . $packageversion[1] .
'" is not supported, only 1.0, 2.0, and 2.1 are supported.');
}
$object = &$this->parserFactory($packageversion[1]);
if ($this->_logger) {
$object->setLogger($this->_logger);
}
$object->setConfig($this->_config);
$pf = $object->parse($data, $file, $archive);
if (PEAR::isError($pf)) {
return $pf;
}
if ($this->_rawReturn) {
return $pf;
}
if (!$pf->validate($state)) {;
if ($this->_config->get('verbose') > 0
&& $this->_logger && $pf->getValidationWarnings(false)
) {
foreach ($pf->getValidationWarnings(false) as $warning) {
$this->_logger->log(0, 'ERROR: ' . $warning['message']);
}
}
$a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
2, null, null, $pf->getValidationWarnings());
return $a;
}
if ($this->_logger && $pf->getValidationWarnings(false)) {
foreach ($pf->getValidationWarnings() as $warning) {
$this->_logger->log(0, 'WARNING: ' . $warning['message']);
}
}
if (method_exists($pf, 'flattenFilelist')) {
$pf->flattenFilelist(); // for v2
}
return $pf;
} elseif (preg_match('/<package[^>]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) {
$a = PEAR::raiseError('package.xml file "' . $file .
'" has unsupported package.xml <package> version "' . $packageversion[1] . '"');
return $a;
} else {
if (!class_exists('PEAR_ErrorStack')) {
require_once 'PEAR/ErrorStack.php';
}
PEAR_ErrorStack::staticPush('PEAR_PackageFile',
PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION,
'warning', array('xml' => $data), 'package.xml "' . $file .
'" has no package.xml <package> version');
$object = &$this->parserFactory('1.0');
$object->setConfig($this->_config);
$pf = $object->parse($data, $file, $archive);
if (PEAR::isError($pf)) {
return $pf;
}
if ($this->_rawReturn) {
return $pf;
}
if (!$pf->validate($state)) {
$a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
2, null, null, $pf->getValidationWarnings());
return $a;
}
if ($this->_logger && $pf->getValidationWarnings(false)) {
foreach ($pf->getValidationWarnings() as $warning) {
$this->_logger->log(0, 'WARNING: ' . $warning['message']);
}
}
if (method_exists($pf, 'flattenFilelist')) {
$pf->flattenFilelist(); // for v2
}
return $pf;
}
}
/**
* Register a temporary file or directory. When the destructor is
* executed, all registered temporary files and directories are
* removed.
*
* @param string $file name of file or directory
* @return void
*/
function addTempFile($file)
{
$GLOBALS['_PEAR_Common_tempfiles'][] = $file;
}
/**
* Create a PEAR_PackageFile_v* from a compressed Tar or Tgz file.
* @access public
* @param string contents of package.xml file
* @param int package state (one of PEAR_VALIDATE_* constants)
* @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
* @using Archive_Tar to extract the files
* @using fromPackageFile() to load the package after the package.xml
* file is extracted.
*/
function &fromTgzFile($file, $state)
{
if (!class_exists('Archive_Tar')) {
require_once 'Archive/Tar.php';
}
$tar = new Archive_Tar($file);
if ($this->_debug <= 1) {
$tar->pushErrorHandling(PEAR_ERROR_RETURN);
}
$content = $tar->listContent();
if ($this->_debug <= 1) {
$tar->popErrorHandling();
}
if (!is_array($content)) {
if (is_string($file) && strlen($file < 255) &&
(!file_exists($file) || !@is_file($file))) {
$ret = PEAR::raiseError("could not open file \"$file\"");
return $ret;
}
$file = realpath($file);
$ret = PEAR::raiseError("Could not get contents of package \"$file\"".
'. Invalid tgz file.');
return $ret;
}
if (!count($content) && !@is_file($file)) {
$ret = PEAR::raiseError("could not open file \"$file\"");
return $ret;
}
$xml = null;
$origfile = $file;
foreach ($content as $file) {
$name = $file['filename'];
if ($name == 'package2.xml') { // allow a .tgz to distribute both versions
$xml = $name;
break;
}
if ($name == 'package.xml') {
$xml = $name;
break;
} elseif (preg_match('/package.xml$/', $name, $match)) {
$xml = $name;
break;
}
}
$tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear');
if ($tmpdir === false) {
$ret = PEAR::raiseError("there was a problem with getting the configured temp directory");
return $ret;
}
PEAR_PackageFile::addTempFile($tmpdir);
$this->_extractErrors();
PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors'));
if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
$extra = implode("\n", $this->_extractErrors());
if ($extra) {
$extra = ' ' . $extra;
}
PEAR::staticPopErrorHandling();
$ret = PEAR::raiseError('could not extract the package.xml file from "' .
$origfile . '"' . $extra);
return $ret;
}
PEAR::staticPopErrorHandling();
$ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile);
return $ret;
}
/**
* helper callback for extracting Archive_Tar errors
*
* @param PEAR_Error|null $err
* @return array
* @access private
*/
function _extractErrors($err = null)
{
static $errors = array();
if ($err === null) {
$e = $errors;
$errors = array();
return $e;
}
$errors[] = $err->getMessage();
}
/**
* Create a PEAR_PackageFile_v* from a package.xml file.
*
* @access public
* @param string $descfile name of package xml file
* @param int $state package state (one of PEAR_VALIDATE_* constants)
* @param string|false $archive name of the archive this package.xml came
* from, if any
* @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
* @uses PEAR_PackageFile::fromXmlString to create the oject after the
* XML is loaded from the package.xml file.
*/
function &fromPackageFile($descfile, $state, $archive = false)
{
$fp = false;
if (is_string($descfile) && strlen($descfile) < 255 &&
(
!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile)
|| (!$fp = @fopen($descfile, 'r'))
)
) {
$a = PEAR::raiseError("Unable to open $descfile");
return $a;
}
// read the whole thing so we only get one cdata callback
// for each block of cdata
fclose($fp);
$data = file_get_contents($descfile);
$ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive);
return $ret;
}
/**
* Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file.
*
* This method is able to extract information about a package from a .tgz
* archive or from a XML package definition file.
*
* @access public
* @param string $info file name
* @param int $state package state (one of PEAR_VALIDATE_* constants)
* @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
* @uses fromPackageFile() if the file appears to be XML
* @uses fromTgzFile() to load all non-XML files
*/
function &fromAnyFile($info, $state)
{
if (is_dir($info)) {
$dir_name = realpath($info);
if (file_exists($dir_name . '/package.xml')) {
$info = PEAR_PackageFile::fromPackageFile($dir_name . '/package.xml', $state);
} elseif (file_exists($dir_name . '/package2.xml')) {
$info = PEAR_PackageFile::fromPackageFile($dir_name . '/package2.xml', $state);
} else {
$info = PEAR::raiseError("No package definition found in '$info' directory");
}
return $info;
}
$fp = false;
if (is_string($info) && strlen($info) < 255 &&
(file_exists($info) || ($fp = @fopen($info, 'r')))
) {
if ($fp) {
fclose($fp);
}
$tmp = substr($info, -4);
if ($tmp == '.xml') {
$info = &PEAR_PackageFile::fromPackageFile($info, $state);
} elseif ($tmp == '.tar' || $tmp == '.tgz') {
$info = &PEAR_PackageFile::fromTgzFile($info, $state);
} else {
$fp = fopen($info, 'r');
$test = fread($fp, 5);
fclose($fp);
if ($test == '<?xml') {
$info = &PEAR_PackageFile::fromPackageFile($info, $state);
} else {
$info = &PEAR_PackageFile::fromTgzFile($info, $state);
}
}
return $info;
}
$info = PEAR::raiseError("Cannot open '$info' for parsing");
return $info;
}
}