1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095 |
- <?php
- //
- // +----------------------------------------------------------------------+
- // | PHP Version 4 |
- // +----------------------------------------------------------------------+
- // | Copyright (c) 1997-2003 The PHP Group |
- // +----------------------------------------------------------------------+
- // | This source file is subject to version 3.0 of the PHP license, |
- // | that is bundled with this package in the file LICENSE, and is |
- // | available through the world-wide-web at the following url: |
- // | http://www.php.net/license/3_0.txt. |
- // | If you did not receive a copy of the PHP license and are unable to |
- // | obtain it through the world-wide-web, please send a note to |
- // | [email protected] so we can mail you a copy immediately. |
- // +----------------------------------------------------------------------+
- // | Authors: Stig Bakken <[email protected]> |
- // | Tomas V.V.Cox <[email protected]> |
- // +----------------------------------------------------------------------+
- //
- // $Id: Common.php,v 1.126.2.2 2004/12/27 07:04:19 cellog Exp $
- require_once 'PEAR.php';
- require_once 'Archive/Tar.php';
- require_once 'System.php';
- require_once 'PEAR/Config.php';
- // {{{ constants and globals
- /**
- * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
- */
- define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
- define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
- define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '$/');
- // this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
- define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
- define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '$/i');
- // XXX far from perfect :-)
- define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^(' . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?$/');
- /**
- * List of temporary files and directories registered by
- * PEAR_Common::addTempFile().
- * @var array
- */
- $GLOBALS['_PEAR_Common_tempfiles'] = array();
- /**
- * Valid maintainer roles
- * @var array
- */
- $GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
- /**
- * Valid release states
- * @var array
- */
- $GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
- /**
- * Valid dependency types
- * @var array
- */
- $GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
- /**
- * Valid dependency relations
- * @var array
- */
- $GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
- /**
- * Valid file roles
- * @var array
- */
- $GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
- /**
- * Valid replacement types
- * @var array
- */
- $GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
- /**
- * Valid "provide" types
- * @var array
- */
- $GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
- /**
- * Valid "provide" types
- * @var array
- */
- $GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
- // }}}
- /**
- * Class providing common functionality for PEAR administration classes.
- * @deprecated This class will disappear, and its components will be spread
- * into smaller classes, like the AT&T breakup
- */
- class PEAR_Common extends PEAR
- {
- // {{{ properties
- /** stack of elements, gives some sort of XML context */
- var $element_stack = array();
- /** name of currently parsed XML element */
- var $current_element;
- /** array of attributes of the currently parsed XML element */
- var $current_attributes = array();
- /** assoc with information about a package */
- var $pkginfo = array();
- /**
- * User Interface object (PEAR_Frontend_* class). If null,
- * the log() method uses print.
- * @var object
- */
- var $ui = null;
- /**
- * Configuration object (PEAR_Config).
- * @var object
- */
- var $config = null;
- var $current_path = null;
- /**
- * PEAR_SourceAnalyzer instance
- * @var object
- */
- var $source_analyzer = null;
- /**
- * Flag variable used to mark a valid package file
- * @var boolean
- * @access private
- */
- var $_validPackageFile;
- // }}}
- // {{{ constructor
- /**
- * PEAR_Common constructor
- *
- * @access public
- */
- function PEAR_Common()
- {
- parent::PEAR();
- $this->config = &PEAR_Config::singleton();
- $this->debug = $this->config->get('verbose');
- }
- // }}}
- // {{{ destructor
- /**
- * PEAR_Common destructor
- *
- * @access private
- */
- function _PEAR_Common()
- {
- // doesn't work due to bug #14744
- //$tempfiles = $this->_tempfiles;
- $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
- while ($file = array_shift($tempfiles)) {
- if (@is_dir($file)) {
- System::rm(array('-rf', $file));
- } elseif (file_exists($file)) {
- unlink($file);
- }
- }
- }
- // }}}
- // {{{ addTempFile()
- /**
- * 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
- *
- * @access public
- */
- function addTempFile($file)
- {
- $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
- }
- // }}}
- // {{{ mkDirHier()
- /**
- * Wrapper to System::mkDir(), creates a directory as well as
- * any necessary parent directories.
- *
- * @param string $dir directory name
- *
- * @return bool TRUE on success, or a PEAR error
- *
- * @access public
- */
- function mkDirHier($dir)
- {
- $this->log(2, "+ create dir $dir");
- return System::mkDir(array('-p', $dir));
- }
- // }}}
- // {{{ log()
- /**
- * Logging method.
- *
- * @param int $level log level (0 is quiet, higher is noisier)
- * @param string $msg message to write to the log
- *
- * @return void
- *
- * @access public
- */
- function log($level, $msg, $append_crlf = true)
- {
- if ($this->debug >= $level) {
- if (is_object($this->ui)) {
- $this->ui->log($msg, $append_crlf);
- } else {
- print "$msg\n";
- }
- }
- }
- // }}}
- // {{{ mkTempDir()
- /**
- * Create and register a temporary directory.
- *
- * @param string $tmpdir (optional) Directory to use as tmpdir.
- * Will use system defaults (for example
- * /tmp or c:\windows\temp) if not specified
- *
- * @return string name of created directory
- *
- * @access public
- */
- function mkTempDir($tmpdir = '')
- {
- if ($tmpdir) {
- $topt = array('-t', $tmpdir);
- } else {
- $topt = array();
- }
- $topt = array_merge($topt, array('-d', 'pear'));
- if (!$tmpdir = System::mktemp($topt)) {
- return false;
- }
- $this->addTempFile($tmpdir);
- return $tmpdir;
- }
- // }}}
- // {{{ setFrontendObject()
- /**
- * Set object that represents the frontend to be used.
- *
- * @param object Reference of the frontend object
- * @return void
- * @access public
- */
- function setFrontendObject(&$ui)
- {
- $this->ui = &$ui;
- }
- // }}}
- // {{{ _unIndent()
- /**
- * Unindent given string (?)
- *
- * @param string $str The string that has to be unindented.
- * @return string
- * @access private
- */
- function _unIndent($str)
- {
- // remove leading newlines
- $str = preg_replace('/^[\r\n]+/', '', $str);
- // find whitespace at the beginning of the first line
- $indent_len = strspn($str, " \t");
- $indent = substr($str, 0, $indent_len);
- $data = '';
- // remove the same amount of whitespace from following lines
- foreach (explode("\n", $str) as $line) {
- if (substr($line, 0, $indent_len) == $indent) {
- $data .= substr($line, $indent_len) . "\n";
- }
- }
- return $data;
- }
- // }}}
- // {{{ _element_start()
- /**
- * XML parser callback for starting elements. Used while package
- * format version is not yet known.
- *
- * @param resource $xp XML parser resource
- * @param string $name name of starting element
- * @param array $attribs element attributes, name => value
- *
- * @return void
- *
- * @access private
- */
- function _element_start($xp, $name, $attribs)
- {
- array_push($this->element_stack, $name);
- $this->current_element = $name;
- $spos = sizeof($this->element_stack) - 2;
- $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
- $this->current_attributes = $attribs;
- switch ($name) {
- case 'package': {
- $this->_validPackageFile = true;
- if (isset($attribs['version'])) {
- $vs = preg_replace('/[^0-9a-z]/', '_', $attribs['version']);
- } else {
- $vs = '1_0';
- }
- $elem_start = '_element_start_'. $vs;
- $elem_end = '_element_end_'. $vs;
- $cdata = '_pkginfo_cdata_'. $vs;
- if (!method_exists($this, $elem_start) ||
- !method_exists($this, $elem_end) ||
- !method_exists($this, $cdata)) {
- $this->raiseError("No handlers for package.xml version $attribs[version]");
- return;
- }
- xml_set_element_handler($xp, $elem_start, $elem_end);
- xml_set_character_data_handler($xp, $cdata);
- break;
- }
- }
- }
- // }}}
- // {{{ _element_end()
- /**
- * XML parser callback for ending elements. Used while package
- * format version is not yet known.
- *
- * @param resource $xp XML parser resource
- * @param string $name name of ending element
- *
- * @return void
- *
- * @access private
- */
- function _element_end($xp, $name)
- {
- }
- // }}}
- // Support for package DTD v1.0:
- // {{{ _element_start_1_0()
- /**
- * XML parser callback for ending elements. Used for version 1.0
- * packages.
- *
- * @param resource $xp XML parser resource
- * @param string $name name of ending element
- *
- * @return void
- *
- * @access private
- */
- function _element_start_1_0($xp, $name, $attribs)
- {
- array_push($this->element_stack, $name);
- $this->current_element = $name;
- $spos = sizeof($this->element_stack) - 2;
- $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
- $this->current_attributes = $attribs;
- $this->cdata = '';
- switch ($name) {
- case 'dir':
- if ($this->in_changelog) {
- break;
- }
- if ($attribs['name'] != '/') {
- $this->dir_names[] = $attribs['name'];
- }
- if (isset($attribs['baseinstalldir'])) {
- $this->dir_install = $attribs['baseinstalldir'];
- }
- if (isset($attribs['role'])) {
- $this->dir_role = $attribs['role'];
- }
- break;
- case 'file':
- if ($this->in_changelog) {
- break;
- }
- if (isset($attribs['name'])) {
- $path = '';
- if (count($this->dir_names)) {
- foreach ($this->dir_names as $dir) {
- $path .= $dir . DIRECTORY_SEPARATOR;
- }
- }
- $path .= $attribs['name'];
- unset($attribs['name']);
- $this->current_path = $path;
- $this->filelist[$path] = $attribs;
- // Set the baseinstalldir only if the file don't have this attrib
- if (!isset($this->filelist[$path]['baseinstalldir']) &&
- isset($this->dir_install))
- {
- $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
- }
- // Set the Role
- if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
- $this->filelist[$path]['role'] = $this->dir_role;
- }
- }
- break;
- case 'replace':
- if (!$this->in_changelog) {
- $this->filelist[$this->current_path]['replacements'][] = $attribs;
- }
- break;
- case 'maintainers':
- $this->pkginfo['maintainers'] = array();
- $this->m_i = 0; // maintainers array index
- break;
- case 'maintainer':
- // compatibility check
- if (!isset($this->pkginfo['maintainers'])) {
- $this->pkginfo['maintainers'] = array();
- $this->m_i = 0;
- }
- $this->pkginfo['maintainers'][$this->m_i] = array();
- $this->current_maintainer =& $this->pkginfo['maintainers'][$this->m_i];
- break;
- case 'changelog':
- $this->pkginfo['changelog'] = array();
- $this->c_i = 0; // changelog array index
- $this->in_changelog = true;
- break;
- case 'release':
- if ($this->in_changelog) {
- $this->pkginfo['changelog'][$this->c_i] = array();
- $this->current_release = &$this->pkginfo['changelog'][$this->c_i];
- } else {
- $this->current_release = &$this->pkginfo;
- }
- break;
- case 'deps':
- if (!$this->in_changelog) {
- $this->pkginfo['release_deps'] = array();
- }
- break;
- case 'dep':
- // dependencies array index
- if (!$this->in_changelog) {
- $this->d_i++;
- $this->pkginfo['release_deps'][$this->d_i] = $attribs;
- }
- break;
- case 'configureoptions':
- if (!$this->in_changelog) {
- $this->pkginfo['configure_options'] = array();
- }
- break;
- case 'configureoption':
- if (!$this->in_changelog) {
- $this->pkginfo['configure_options'][] = $attribs;
- }
- break;
- case 'provides':
- if (empty($attribs['type']) || empty($attribs['name'])) {
- break;
- }
- $attribs['explicit'] = true;
- $this->pkginfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
- break;
- }
- }
- // }}}
- // {{{ _element_end_1_0()
- /**
- * XML parser callback for ending elements. Used for version 1.0
- * packages.
- *
- * @param resource $xp XML parser resource
- * @param string $name name of ending element
- *
- * @return void
- *
- * @access private
- */
- function _element_end_1_0($xp, $name)
- {
- $data = trim($this->cdata);
- switch ($name) {
- case 'name':
- switch ($this->prev_element) {
- case 'package':
- // XXX should we check the package name here?
- $this->pkginfo['package'] = ereg_replace('[^a-zA-Z0-9._]', '_', $data);
- break;
- case 'maintainer':
- $this->current_maintainer['name'] = $data;
- break;
- }
- break;
- case 'summary':
- $this->pkginfo['summary'] = $data;
- break;
- case 'description':
- $data = $this->_unIndent($this->cdata);
- $this->pkginfo['description'] = $data;
- break;
- case 'user':
- $this->current_maintainer['handle'] = $data;
- break;
- case 'email':
- $this->current_maintainer['email'] = $data;
- break;
- case 'role':
- $this->current_maintainer['role'] = $data;
- break;
- case 'version':
- $data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data);
- if ($this->in_changelog) {
- $this->current_release['version'] = $data;
- } else {
- $this->pkginfo['version'] = $data;
- }
- break;
- case 'date':
- if ($this->in_changelog) {
- $this->current_release['release_date'] = $data;
- } else {
- $this->pkginfo['release_date'] = $data;
- }
- break;
- case 'notes':
- // try to "de-indent" release notes in case someone
- // has been over-indenting their xml ;-)
- $data = $this->_unIndent($this->cdata);
- if ($this->in_changelog) {
- $this->current_release['release_notes'] = $data;
- } else {
- $this->pkginfo['release_notes'] = $data;
- }
- break;
- case 'warnings':
- if ($this->in_changelog) {
- $this->current_release['release_warnings'] = $data;
- } else {
- $this->pkginfo['release_warnings'] = $data;
- }
- break;
- case 'state':
- if ($this->in_changelog) {
- $this->current_release['release_state'] = $data;
- } else {
- $this->pkginfo['release_state'] = $data;
- }
- break;
- case 'license':
- if ($this->in_changelog) {
- $this->current_release['release_license'] = $data;
- } else {
- $this->pkginfo['release_license'] = $data;
- }
- break;
- case 'dep':
- if ($data && !$this->in_changelog) {
- $this->pkginfo['release_deps'][$this->d_i]['name'] = $data;
- }
- break;
- case 'dir':
- if ($this->in_changelog) {
- break;
- }
- array_pop($this->dir_names);
- break;
- case 'file':
- if ($this->in_changelog) {
- break;
- }
- if ($data) {
- $path = '';
- if (count($this->dir_names)) {
- foreach ($this->dir_names as $dir) {
- $path .= $dir . DIRECTORY_SEPARATOR;
- }
- }
- $path .= $data;
- $this->filelist[$path] = $this->current_attributes;
- // Set the baseinstalldir only if the file don't have this attrib
- if (!isset($this->filelist[$path]['baseinstalldir']) &&
- isset($this->dir_install))
- {
- $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
- }
- // Set the Role
- if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
- $this->filelist[$path]['role'] = $this->dir_role;
- }
- }
- break;
- case 'maintainer':
- if (empty($this->pkginfo['maintainers'][$this->m_i]['role'])) {
- $this->pkginfo['maintainers'][$this->m_i]['role'] = 'lead';
- }
- $this->m_i++;
- break;
- case 'release':
- if ($this->in_changelog) {
- $this->c_i++;
- }
- break;
- case 'changelog':
- $this->in_changelog = false;
- break;
- }
- array_pop($this->element_stack);
- $spos = sizeof($this->element_stack) - 1;
- $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
- $this->cdata = '';
- }
- // }}}
- // {{{ _pkginfo_cdata_1_0()
- /**
- * XML parser callback for character data. Used for version 1.0
- * packages.
- *
- * @param resource $xp XML parser resource
- * @param string $name character data
- *
- * @return void
- *
- * @access private
- */
- function _pkginfo_cdata_1_0($xp, $data)
- {
- if (isset($this->cdata)) {
- $this->cdata .= $data;
- }
- }
- // }}}
- // {{{ infoFromTgzFile()
- /**
- * Returns information about a package file. Expects the name of
- * a gzipped tar file as input.
- *
- * @param string $file name of .tgz file
- *
- * @return array array with package information
- *
- * @access public
- *
- */
- function infoFromTgzFile($file)
- {
- if (!@is_file($file)) {
- return $this->raiseError("could not open file \"$file\"");
- }
- $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)) {
- $file = realpath($file);
- return $this->raiseError("Could not get contents of package \"$file\"".
- '. Invalid tgz file.');
- }
- $xml = null;
- foreach ($content as $file) {
- $name = $file['filename'];
- if ($name == 'package.xml') {
- $xml = $name;
- break;
- } elseif (ereg('package.xml$', $name, $match)) {
- $xml = $match[0];
- break;
- }
- }
- $tmpdir = System::mkTemp(array('-d', 'pear'));
- $this->addTempFile($tmpdir);
- if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
- return $this->raiseError('could not extract the package.xml file');
- }
- return $this->infoFromDescriptionFile("$tmpdir/$xml");
- }
- // }}}
- // {{{ infoFromDescriptionFile()
- /**
- * Returns information about a package file. Expects the name of
- * a package xml file as input.
- *
- * @param string $descfile name of package xml file
- *
- * @return array array with package information
- *
- * @access public
- *
- */
- function infoFromDescriptionFile($descfile)
- {
- if (!@is_file($descfile) || !is_readable($descfile) ||
- (!$fp = @fopen($descfile, 'r'))) {
- return $this->raiseError("Unable to open $descfile");
- }
- // read the whole thing so we only get one cdata callback
- // for each block of cdata
- $data = fread($fp, filesize($descfile));
- return $this->infoFromString($data);
- }
- // }}}
- // {{{ infoFromString()
- /**
- * Returns information about a package file. Expects the contents
- * of a package xml file as input.
- *
- * @param string $data name of package xml file
- *
- * @return array array with package information
- *
- * @access public
- *
- */
- function infoFromString($data)
- {
- require_once('PEAR/Dependency.php');
- if (PEAR_Dependency::checkExtension($error, 'xml')) {
- return $this->raiseError($error);
- }
- $xp = @xml_parser_create();
- if (!$xp) {
- return $this->raiseError('Unable to create XML parser');
- }
- xml_set_object($xp, $this);
- xml_set_element_handler($xp, '_element_start', '_element_end');
- xml_set_character_data_handler($xp, '_pkginfo_cdata');
- xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
- $this->element_stack = array();
- $this->pkginfo = array('provides' => array());
- $this->current_element = false;
- unset($this->dir_install);
- $this->pkginfo['filelist'] = array();
- $this->filelist =& $this->pkginfo['filelist'];
- $this->dir_names = array();
- $this->in_changelog = false;
- $this->d_i = 0;
- $this->cdata = '';
- $this->_validPackageFile = false;
- if (!xml_parse($xp, $data, 1)) {
- $code = xml_get_error_code($xp);
- $msg = sprintf("XML error: %s at line %d",
- xml_error_string($code),
- xml_get_current_line_number($xp));
- xml_parser_free($xp);
- return $this->raiseError($msg, $code);
- }
- xml_parser_free($xp);
- if (!$this->_validPackageFile) {
- return $this->raiseError('Invalid Package File, no <package> tag');
- }
- foreach ($this->pkginfo as $k => $v) {
- if (!is_array($v)) {
- $this->pkginfo[$k] = trim($v);
- }
- }
- return $this->pkginfo;
- }
- // }}}
- // {{{ infoFromAny()
- /**
- * Returns package information from different sources
- *
- * 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 Filename of the source ('package.xml', '<package>.tgz')
- * @return string
- */
- function infoFromAny($info)
- {
- if (is_string($info) && file_exists($info)) {
- $tmp = substr($info, -4);
- if ($tmp == '.xml') {
- $info = $this->infoFromDescriptionFile($info);
- } elseif ($tmp == '.tar' || $tmp == '.tgz') {
- $info = $this->infoFromTgzFile($info);
- } else {
- $fp = fopen($info, "r");
- $test = fread($fp, 5);
- fclose($fp);
- if ($test == "<?xml") {
- $info = $this->infoFromDescriptionFile($info);
- } else {
- $info = $this->infoFromTgzFile($info);
- }
- }
- if (PEAR::isError($info)) {
- return $this->raiseError($info);
- }
- }
- return $info;
- }
- // }}}
- // {{{ xmlFromInfo()
- /**
- * Return an XML document based on the package info (as returned
- * by the PEAR_Common::infoFrom* methods).
- *
- * @param array $pkginfo package info
- *
- * @return string XML data
- *
- * @access public
- */
- function xmlFromInfo($pkginfo)
- {
- static $maint_map = array(
- "handle" => "user",
- "name" => "name",
- "email" => "email",
- "role" => "role",
- );
- $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
- $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
- $ret .= "<package version=\"1.0\">
- <name>$pkginfo[package]</name>
- <summary>".htmlspecialchars($pkginfo['summary'])."</summary>
- <description>".htmlspecialchars($pkginfo['description'])."</description>
- <maintainers>
- ";
- foreach ($pkginfo['maintainers'] as $maint) {
- $ret .= " <maintainer>\n";
- foreach ($maint_map as $idx => $elm) {
- $ret .= " <$elm>";
- $ret .= htmlspecialchars($maint[$idx]);
- $ret .= "</$elm>\n";
- }
- $ret .= " </maintainer>\n";
- }
- $ret .= " </maintainers>\n";
- $ret .= $this->_makeReleaseXml($pkginfo);
- if (@sizeof($pkginfo['changelog']) > 0) {
- $ret .= " <changelog>\n";
- foreach ($pkginfo['changelog'] as $oldrelease) {
- $ret .= $this->_makeReleaseXml($oldrelease, true);
- }
- $ret .= " </changelog>\n";
- }
- $ret .= "</package>\n";
- return $ret;
- }
- // }}}
- // {{{ _makeReleaseXml()
- /**
- * Generate part of an XML description with release information.
- *
- * @param array $pkginfo array with release information
- * @param bool $changelog whether the result will be in a changelog element
- *
- * @return string XML data
- *
- * @access private
- */
- function _makeReleaseXml($pkginfo, $changelog = false)
- {
- // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
- $indent = $changelog ? " " : "";
- $ret = "$indent <release>\n";
- if (!empty($pkginfo['version'])) {
- $ret .= "$indent <version>$pkginfo[version]</version>\n";
- }
- if (!empty($pkginfo['release_date'])) {
- $ret .= "$indent <date>$pkginfo[release_date]</date>\n";
- }
- if (!empty($pkginfo['release_license'])) {
- $ret .= "$indent <license>$pkginfo[release_license]</license>\n";
- }
- if (!empty($pkginfo['release_state'])) {
- $ret .= "$indent <state>$pkginfo[release_state]</state>\n";
- }
- if (!empty($pkginfo['release_notes'])) {
- $ret .= "$indent <notes>".htmlspecialchars($pkginfo['release_notes'])."</notes>\n";
- }
- if (!empty($pkginfo['release_warnings'])) {
- $ret .= "$indent <warnings>".htmlspecialchars($pkginfo['release_warnings'])."</warnings>\n";
- }
- if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
- $ret .= "$indent <deps>\n";
- foreach ($pkginfo['release_deps'] as $dep) {
- $ret .= "$indent <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
- if (isset($dep['version'])) {
- $ret .= " version=\"$dep[version]\"";
- }
- if (isset($dep['optional'])) {
- $ret .= " optional=\"$dep[optional]\"";
- }
- if (isset($dep['name'])) {
- $ret .= ">$dep[name]</dep>\n";
- } else {
- $ret .= "/>\n";
- }
- }
- $ret .= "$indent </deps>\n";
- }
- if (isset($pkginfo['configure_options'])) {
- $ret .= "$indent <configureoptions>\n";
- foreach ($pkginfo['configure_options'] as $c) {
- $ret .= "$indent <configureoption name=\"".
- htmlspecialchars($c['name']) . "\"";
- if (isset($c['default'])) {
- $ret .= " default=\"" . htmlspecialchars($c['default']) . "\"";
- }
- $ret .= " prompt=\"" . htmlspecialchars($c['prompt']) . "\"";
- $ret .= "/>\n";
- }
- $ret .= "$indent </configureoptions>\n";
- }
- if (isset($pkginfo['provides'])) {
- foreach ($pkginfo['provides'] as $key => $what) {
- $ret .= "$indent <provides type=\"$what[type]\" ";
- $ret .= "name=\"$what[name]\" ";
- if (isset($what['extends'])) {
- $ret .= "extends=\"$what[extends]\" ";
- }
- $ret .= "/>\n";
- }
- }
- if (isset($pkginfo['filelist'])) {
- $ret .= "$indent <filelist>\n";
- foreach ($pkginfo['filelist'] as $file => $fa) {
- @$ret .= "$indent <file role=\"$fa[role]\"";
- if (isset($fa['baseinstalldir'])) {
- $ret .= ' baseinstalldir="' .
- htmlspecialchars($fa['baseinstalldir']) . '"';
- }
- if (isset($fa['md5sum'])) {
- $ret .= " md5sum=\"$fa[md5sum]\"";
- }
- if (isset($fa['platform'])) {
- $ret .= " platform=\"$fa[platform]\"";
- }
- if (!empty($fa['install-as'])) {
- $ret .= ' install-as="' .
- htmlspecialchars($fa['install-as']) . '"';
- }
- $ret .= ' name="' . htmlspecialchars($file) . '"';
- if (empty($fa['replacements'])) {
- $ret .= "/>\n";
- } else {
- $ret .= ">\n";
- foreach ($fa['replacements'] as $r) {
- $ret .= "$indent <replace";
- foreach ($r as $k => $v) {
- $ret .= " $k=\"" . htmlspecialchars($v) .'"';
- }
- $ret .= "/>\n";
- }
- @$ret .= "$indent </file>\n";
- }
- }
- $ret .= "$indent </filelist>\n";
- }
- $ret .= "$indent </release>\n";
- return $ret;
- }
- // }}}
- // {{{ validatePackageInfo()
- /**
- * Validate XML package definition file.
- *
- * @param string $info Filename of the package archive or of the
- * package definition file
- * @param array $errors Array that will contain the errors
- * @param array $warnings Array that will contain the warnings
- * @param string $dir_prefix (optional) directory where source files
- * may be found, or empty if they are not available
- * @access public
- * @return boolean
- */
- function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
- {
- if (PEAR::isError($info = $this->infoFromAny($info))) {
- return $this->raiseError($info);
- }
- if (!is_array($info)) {
- return false;
- }
- $errors = array();
- $warnings = array();
- if (!isset($info['package'])) {
- $errors[] = 'missing package name';
- } elseif (!$this->validPackageName($info['package'])) {
- $errors[] = 'invalid package name';
- }
- $this->_packageName = $pn = $info['package'];
- if (empty($info['summary'])) {
- $errors[] = 'missing summary';
- } elseif (strpos(trim($info['summary']), "\n") !== false) {
- $warnings[] = 'summary should be on a single line';
- }
- if (empty($info['description'])) {
- $errors[] = 'missing description';
- }
- if (empty($info['release_license'])) {
- $errors[] = 'missing license';
- }
- if (!isset($info['version'])) {
- $errors[] = 'missing version';
- } elseif (!$this->validPackageVersion($info['version'])) {
- $errors[] = 'invalid package release version';
- }
- if (empty($info['release_state'])) {
- $errors[] = 'missing release state';
- } elseif (!in_array($info['release_state'], PEAR_Common::getReleaseStates())) {
- $errors[] = "invalid release state `$info[release_state]', should be one of: "
- . implode(' ', PEAR_Common::getReleaseStates());
- }
- if (empty($info['release_date'])) {
- $errors[] = 'missing release date';
- } elseif (!preg_match('/^\d{4}-\d\d-\d\d$/', $info['release_date'])) {
- $errors[] = "invalid release date `$info[release_date]', format is YYYY-MM-DD";
- }
- if (empty($info['release_notes'])) {
- $errors[] = "missing release notes";
- }
- if (empty($info['maintainers'])) {
- $errors[] = 'no maintainer(s)';
- } else {
- $i = 1;
- foreach ($info['maintainers'] as $m) {
- if (empty($m['handle'])) {
- $errors[] = "maintainer $i: missing handle";
- }
- if (empty($m['role'])) {
- $errors[] = "maintainer $i: missing role";
- } elseif (!in_array($m['role'], PEAR_Common::getUserRoles())) {
- $errors[] = "maintainer $i: invalid role `$m[role]', should be one of: "
- . implode(' ', PEAR_Common::getUserRoles());
- }
- if (empty($m['name'])) {
- $errors[] = "maintainer $i: missing name";
- }
- if (empty($m['email'])) {
- $errors[] = "maintainer $i: missing email";
- }
- $i++;
- }
- }
- if (!empty($info['release_deps'])) {
- $i = 1;
- foreach ($info['release_deps'] as $d) {
- if (empty($d['type'])) {
- $errors[] = "dependency $i: missing type";
- } elseif (!in_array($d['type'], PEAR_Common::getDependencyTypes())) {
- $errors[] = "dependency $i: invalid type '$d[type]', should be one of: " .
- implode(' ', PEAR_Common::getDependencyTypes());
- }
- if (empty($d['rel'])) {
- $errors[] = "dependency $i: missing relation";
- } elseif (!in_array($d['rel'], PEAR_Common::getDependencyRelations())) {
- $errors[] = "dependency $i: invalid relation '$d[rel]', should be one of: "
- . implode(' ', PEAR_Common::getDependencyRelations());
- }
- if (!empty($d['optional'])) {
- if (!in_array($d['optional'], array('yes', 'no'))) {
- $errors[] = "dependency $i: invalid relation optional attribute '$d[optional]', should be one of: yes no";
- } else {
- if (($d['rel'] == 'not' || $d['rel'] == 'ne') && $d['optional'] == 'yes') {
- $errors[] = "dependency $i: 'not' and 'ne' dependencies cannot be " .
- "optional";
- }
- }
- }
- if ($d['rel'] != 'not' && $d['rel'] != 'has' && empty($d['version'])) {
- $warnings[] = "dependency $i: missing version";
- } elseif (($d['rel'] == 'not' || $d['rel'] == 'has') && !empty($d['version'])) {
- $warnings[] = "dependency $i: version ignored for `$d[rel]' dependencies";
- }
- if ($d['rel'] == 'not' && !empty($d['version'])) {
- $warnings[] = "dependency $i: 'not' defines a total conflict, to exclude " .
- "specific versions, use 'ne'";
- }
- if ($d['type'] == 'php' && !empty($d['name'])) {
- $warnings[] = "dependency $i: name ignored for php type dependencies";
- } elseif ($d['type'] != 'php' && empty($d['name'])) {
- $errors[] = "dependency $i: missing name";
- }
- if ($d['type'] == 'php' && $d['rel'] == 'not') {
- $errors[] = "dependency $i: PHP dependencies cannot use 'not' " .
- "rel, use 'ne' to exclude versions";
- }
- $i++;
- }
- }
- if (!empty($info['configure_options'])) {
- $i = 1;
- foreach ($info['configure_options'] as $c) {
- if (empty($c['name'])) {
- $errors[] = "configure option $i: missing name";
- }
- if (empty($c['prompt'])) {
- $errors[] = "configure option $i: missing prompt";
- }
- $i++;
- }
- }
- if (empty($info['filelist'])) {
- $errors[] = 'no files';
- } else {
- foreach ($info['filelist'] as $file => $fa) {
- if (empty($fa['role'])) {
- $errors[] = "file $file: missing role";
- continue;
- } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
- $errors[] = "file $file: invalid role, should be one of: "
- . implode(' ', PEAR_Common::getFileRoles());
- }
- if ($fa['role'] == 'php' && $dir_prefix) {
- $this->log(1, "Analyzing $file");
- $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
- if ($srcinfo) {
- $this->buildProvidesArray($srcinfo);
- }
- }
- // (ssb) Any checks we can do for baseinstalldir?
- // (cox) Perhaps checks that either the target dir and
- // baseInstall doesn't cointain "../../"
- }
- }
- $this->_packageName = $pn = $info['package'];
- $pnl = strlen($pn);
- foreach ((array)$this->pkginfo['provides'] as $key => $what) {
- if (isset($what['explicit'])) {
- // skip conformance checks if the provides entry is
- // specified in the package.xml file
- continue;
- }
- extract($what);
- if ($type == 'class') {
- if (!strncasecmp($name, $pn, $pnl)) {
- continue;
- }
- $warnings[] = "in $file: class \"$name\" not prefixed with package name \"$pn\"";
- } elseif ($type == 'function') {
- if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
- continue;
- }
- $warnings[] = "in $file: function \"$name\" not prefixed with package name \"$pn\"";
- }
- }
- return true;
- }
- // }}}
- // {{{ buildProvidesArray()
- /**
- * Build a "provides" array from data returned by
- * analyzeSourceCode(). The format of the built array is like
- * this:
- *
- * array(
- * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
- * ...
- * )
- *
- *
- * @param array $srcinfo array with information about a source file
- * as returned by the analyzeSourceCode() method.
- *
- * @return void
- *
- * @access public
- *
- */
- function buildProvidesArray($srcinfo)
- {
- $file = basename($srcinfo['source_file']);
- $pn = '';
- if (isset($this->_packageName)) {
- $pn = $this->_packageName;
- }
- $pnl = strlen($pn);
- foreach ($srcinfo['declared_classes'] as $class) {
- $key = "class;$class";
- if (isset($this->pkginfo['provides'][$key])) {
- continue;
- }
- $this->pkginfo['provides'][$key] =
- array('file'=> $file, 'type' => 'class', 'name' => $class);
- if (isset($srcinfo['inheritance'][$class])) {
- $this->pkginfo['provides'][$key]['extends'] =
- $srcinfo['inheritance'][$class];
- }
- }
- foreach ($srcinfo['declared_methods'] as $class => $methods) {
- foreach ($methods as $method) {
- $function = "$class::$method";
- $key = "function;$function";
- if ($method{0} == '_' || !strcasecmp($method, $class) ||
- isset($this->pkginfo['provides'][$key])) {
- continue;
- }
- $this->pkginfo['provides'][$key] =
- array('file'=> $file, 'type' => 'function', 'name' => $function);
- }
- }
- foreach ($srcinfo['declared_functions'] as $function) {
- $key = "function;$function";
- if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
- continue;
- }
- if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
- $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
- }
- $this->pkginfo['provides'][$key] =
- array('file'=> $file, 'type' => 'function', 'name' => $function);
- }
- }
- // }}}
- // {{{ analyzeSourceCode()
- /**
- * Analyze the source code of the given PHP file
- *
- * @param string Filename of the PHP file
- * @return mixed
- * @access public
- */
- function analyzeSourceCode($file)
- {
- if (!function_exists("token_get_all")) {
- return false;
- }
- if (!defined('T_DOC_COMMENT')) {
- define('T_DOC_COMMENT', T_COMMENT);
- }
- if (!defined('T_INTERFACE')) {
- define('T_INTERFACE', -1);
- }
- if (!defined('T_IMPLEMENTS')) {
- define('T_IMPLEMENTS', -1);
- }
- if (!$fp = @fopen($file, "r")) {
- return false;
- }
- $contents = fread($fp, filesize($file));
- $tokens = token_get_all($contents);
- /*
- for ($i = 0; $i < sizeof($tokens); $i++) {
- @list($token, $data) = $tokens[$i];
- if (is_string($token)) {
- var_dump($token);
- } else {
- print token_name($token) . ' ';
- var_dump(rtrim($data));
- }
- }
- */
- $look_for = 0;
- $paren_level = 0;
- $bracket_level = 0;
- $brace_level = 0;
- $lastphpdoc = '';
- $current_class = '';
- $current_interface = '';
- $current_class_level = -1;
- $current_function = '';
- $current_function_level = -1;
- $declared_classes = array();
- $declared_interfaces = array();
- $declared_functions = array();
- $declared_methods = array();
- $used_classes = array();
- $used_functions = array();
- $extends = array();
- $implements = array();
- $nodeps = array();
- $inquote = false;
- $interface = false;
- for ($i = 0; $i < sizeof($tokens); $i++) {
- if (is_array($tokens[$i])) {
- list($token, $data) = $tokens[$i];
- } else {
- $token = $tokens[$i];
- $data = '';
- }
- if ($inquote) {
- if ($token != '"') {
- continue;
- } else {
- $inquote = false;
- }
- }
- switch ($token) {
- case T_WHITESPACE:
- continue;
- case ';':
- if ($interface) {
- $current_function = '';
- $current_function_level = -1;
- }
- break;
- case '"':
- $inquote = true;
- break;
- case T_CURLY_OPEN:
- case T_DOLLAR_OPEN_CURLY_BRACES:
- case '{': $brace_level++; continue 2;
- case '}':
- $brace_level--;
- if ($current_class_level == $brace_level) {
- $current_class = '';
- $current_class_level = -1;
- }
- if ($current_function_level == $brace_level) {
- $current_function = '';
- $current_function_level = -1;
- }
- continue 2;
- case '[': $bracket_level++; continue 2;
- case ']': $bracket_level--; continue 2;
- case '(': $paren_level++; continue 2;
- case ')': $paren_level--; continue 2;
- case T_INTERFACE:
- $interface = true;
- case T_CLASS:
- if (($current_class_level != -1) || ($current_function_level != -1)) {
- PEAR::raiseError("Parser error: Invalid PHP file $file",
- PEAR_COMMON_ERROR_INVALIDPHP);
- return false;
- }
- case T_FUNCTION:
- case T_NEW:
- case T_EXTENDS:
- case T_IMPLEMENTS:
- $look_for = $token;
- continue 2;
- case T_STRING:
- if (version_compare(zend_version(), '2.0', '<')) {
- if (in_array(strtolower($data),
- array('public', 'private', 'protected', 'abstract',
- 'interface', 'implements', 'clone', 'throw')
- )) {
- PEAR::raiseError('Error: PHP5 packages must be packaged by php 5 PEAR');
- return false;
- }
- }
- if ($look_for == T_CLASS) {
- $current_class = $data;
- $current_class_level = $brace_level;
- $declared_classes[] = $current_class;
- } elseif ($look_for == T_INTERFACE) {
- $current_interface = $data;
- $current_class_level = $brace_level;
- $declared_interfaces[] = $current_interface;
- } elseif ($look_for == T_IMPLEMENTS) {
- $implements[$current_class] = $data;
- } elseif ($look_for == T_EXTENDS) {
- $extends[$current_class] = $data;
- } elseif ($look_for == T_FUNCTION) {
- if ($current_class) {
- $current_function = "$current_class::$data";
- $declared_methods[$current_class][] = $data;
- } elseif ($current_interface) {
- $current_function = "$current_interface::$data";
- $declared_methods[$current_interface][] = $data;
- } else {
- $current_function = $data;
- $declared_functions[] = $current_function;
- }
- $current_function_level = $brace_level;
- $m = array();
- } elseif ($look_for == T_NEW) {
- $used_classes[$data] = true;
- }
- $look_for = 0;
- continue 2;
- case T_VARIABLE:
- $look_for = 0;
- continue 2;
- case T_DOC_COMMENT:
- case T_COMMENT:
- if (preg_match('!^/\*\*\s!', $data)) {
- $lastphpdoc = $data;
- if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
- $nodeps = array_merge($nodeps, $m[1]);
- }
- }
- continue 2;
- case T_DOUBLE_COLON:
- if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
- PEAR::raiseError("Parser error: Invalid PHP file $file",
- PEAR_COMMON_ERROR_INVALIDPHP);
- return false;
- }
- $class = $tokens[$i - 1][1];
- if (strtolower($class) != 'parent') {
- $used_classes[$class] = true;
- }
- continue 2;
- }
- }
- return array(
- "source_file" => $file,
- "declared_classes" => $declared_classes,
- "declared_interfaces" => $declared_interfaces,
- "declared_methods" => $declared_methods,
- "declared_functions" => $declared_functions,
- "used_classes" => array_diff(array_keys($used_classes), $nodeps),
- "inheritance" => $extends,
- "implements" => $implements,
- );
- }
- // }}}
- // {{{ betterStates()
- /**
- * Return an array containing all of the states that are more stable than
- * or equal to the passed in state
- *
- * @param string Release state
- * @param boolean Determines whether to include $state in the list
- * @return false|array False if $state is not a valid release state
- */
- function betterStates($state, $include = false)
- {
- static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
- $i = array_search($state, $states);
- if ($i === false) {
- return false;
- }
- if ($include) {
- $i--;
- }
- return array_slice($states, $i + 1);
- }
- // }}}
- // {{{ detectDependencies()
- function detectDependencies($any, $status_callback = null)
- {
- if (!function_exists("token_get_all")) {
- return false;
- }
- if (PEAR::isError($info = $this->infoFromAny($any))) {
- return $this->raiseError($info);
- }
- if (!is_array($info)) {
- return false;
- }
- $deps = array();
- $used_c = $decl_c = $decl_f = $decl_m = array();
- foreach ($info['filelist'] as $file => $fa) {
- $tmp = $this->analyzeSourceCode($file);
- $used_c = @array_merge($used_c, $tmp['used_classes']);
- $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
- $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
- $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
- $inheri = @array_merge($inheri, $tmp['inheritance']);
- }
- $used_c = array_unique($used_c);
- $decl_c = array_unique($decl_c);
- $undecl_c = array_diff($used_c, $decl_c);
- return array('used_classes' => $used_c,
- 'declared_classes' => $decl_c,
- 'declared_methods' => $decl_m,
- 'declared_functions' => $decl_f,
- 'undeclared_classes' => $undecl_c,
- 'inheritance' => $inheri,
- );
- }
- // }}}
- // {{{ getUserRoles()
- /**
- * Get the valid roles for a PEAR package maintainer
- *
- * @return array
- * @static
- */
- function getUserRoles()
- {
- return $GLOBALS['_PEAR_Common_maintainer_roles'];
- }
- // }}}
- // {{{ getReleaseStates()
- /**
- * Get the valid package release states of packages
- *
- * @return array
- * @static
- */
- function getReleaseStates()
- {
- return $GLOBALS['_PEAR_Common_release_states'];
- }
- // }}}
- // {{{ getDependencyTypes()
- /**
- * Get the implemented dependency types (php, ext, pkg etc.)
- *
- * @return array
- * @static
- */
- function getDependencyTypes()
- {
- return $GLOBALS['_PEAR_Common_dependency_types'];
- }
- // }}}
- // {{{ getDependencyRelations()
- /**
- * Get the implemented dependency relations (has, lt, ge etc.)
- *
- * @return array
- * @static
- */
- function getDependencyRelations()
- {
- return $GLOBALS['_PEAR_Common_dependency_relations'];
- }
- // }}}
- // {{{ getFileRoles()
- /**
- * Get the implemented file roles
- *
- * @return array
- * @static
- */
- function getFileRoles()
- {
- return $GLOBALS['_PEAR_Common_file_roles'];
- }
- // }}}
- // {{{ getReplacementTypes()
- /**
- * Get the implemented file replacement types in
- *
- * @return array
- * @static
- */
- function getReplacementTypes()
- {
- return $GLOBALS['_PEAR_Common_replacement_types'];
- }
- // }}}
- // {{{ getProvideTypes()
- /**
- * Get the implemented file replacement types in
- *
- * @return array
- * @static
- */
- function getProvideTypes()
- {
- return $GLOBALS['_PEAR_Common_provide_types'];
- }
- // }}}
- // {{{ getScriptPhases()
- /**
- * Get the implemented file replacement types in
- *
- * @return array
- * @static
- */
- function getScriptPhases()
- {
- return $GLOBALS['_PEAR_Common_script_phases'];
- }
- // }}}
- // {{{ validPackageName()
- /**
- * Test whether a string contains a valid package name.
- *
- * @param string $name the package name to test
- *
- * @return bool
- *
- * @access public
- */
- function validPackageName($name)
- {
- return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
- }
- // }}}
- // {{{ validPackageVersion()
- /**
- * Test whether a string contains a valid package version.
- *
- * @param string $ver the package version to test
- *
- * @return bool
- *
- * @access public
- */
- function validPackageVersion($ver)
- {
- return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
- }
- // }}}
- // {{{ downloadHttp()
- /**
- * Download a file through HTTP. Considers suggested file name in
- * Content-disposition: header and can run a callback function for
- * different events. The callback will be called with two
- * parameters: the callback type, and parameters. The implemented
- * callback types are:
- *
- * 'setup' called at the very beginning, parameter is a UI object
- * that should be used for all output
- * 'message' the parameter is a string with an informational message
- * 'saveas' may be used to save with a different file name, the
- * parameter is the filename that is about to be used.
- * If a 'saveas' callback returns a non-empty string,
- * that file name will be used as the filename instead.
- * Note that $save_dir will not be affected by this, only
- * the basename of the file.
- * 'start' download is starting, parameter is number of bytes
- * that are expected, or -1 if unknown
- * 'bytesread' parameter is the number of bytes read so far
- * 'done' download is complete, parameter is the total number
- * of bytes read
- * 'connfailed' if the TCP connection fails, this callback is called
- * with array(host,port,errno,errmsg)
- * 'writefailed' if writing to disk fails, this callback is called
- * with array(destfile,errmsg)
- *
- * If an HTTP proxy has been configured (http_proxy PEAR_Config
- * setting), the proxy will be used.
- *
- * @param string $url the URL to download
- * @param object $ui PEAR_Frontend_* instance
- * @param object $config PEAR_Config instance
- * @param string $save_dir (optional) directory to save file in
- * @param mixed $callback (optional) function/method to call for status
- * updates
- *
- * @return string Returns the full path of the downloaded file or a PEAR
- * error on failure. If the error is caused by
- * socket-related errors, the error object will
- * have the fsockopen error code available through
- * getCode().
- *
- * @access public
- */
- function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
- {
- if ($callback) {
- call_user_func($callback, 'setup', array(&$ui));
- }
- if (preg_match('!^http://([^/:?#]*)(:(\d+))?(/.*)!', $url, $matches)) {
- list(,$host,,$port,$path) = $matches;
- }
- if (isset($this)) {
- $config = &$this->config;
- } else {
- $config = &PEAR_Config::singleton();
- }
- $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
- if ($proxy = parse_url($config->get('http_proxy'))) {
- $proxy_host = @$proxy['host'];
- $proxy_port = @$proxy['port'];
- $proxy_user = @$proxy['user'];
- $proxy_pass = @$proxy['pass'];
- if ($proxy_port == '') {
- $proxy_port = 8080;
- }
- if ($callback) {
- call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
- }
- }
- if (empty($port)) {
- $port = 80;
- }
- if ($proxy_host != '') {
- $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
- if (!$fp) {
- if ($callback) {
- call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
- $errno, $errstr));
- }
- return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
- }
- $request = "GET $url HTTP/1.0\r\n";
- } else {
- $fp = @fsockopen($host, $port, $errno, $errstr);
- if (!$fp) {
- if ($callback) {
- call_user_func($callback, 'connfailed', array($host, $port,
- $errno, $errstr));
- }
- return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
- }
- $request = "GET $path HTTP/1.0\r\n";
- }
- $request .= "Host: $host:$port\r\n".
- "User-Agent: PHP/".PHP_VERSION."\r\n";
- if ($proxy_host != '' && $proxy_user != '') {
- $request .= 'Proxy-Authorization: Basic ' .
- base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
- }
- $request .= "\r\n";
- fwrite($fp, $request);
- $headers = array();
- while (trim($line = fgets($fp, 1024))) {
- if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {
- $headers[strtolower($matches[1])] = trim($matches[2]);
- } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
- if ($matches[1] != 200) {
- return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)");
- }
- }
- }
- if (isset($headers['content-disposition']) &&
- preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|$)/', $headers['content-disposition'], $matches)) {
- $save_as = basename($matches[1]);
- } else {
- $save_as = basename($url);
- }
- if ($callback) {
- $tmp = call_user_func($callback, 'saveas', $save_as);
- if ($tmp) {
- $save_as = $tmp;
- }
- }
- $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
- if (!$wp = @fopen($dest_file, 'wb')) {
- fclose($fp);
- if ($callback) {
- call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
- }
- return PEAR::raiseError("could not open $dest_file for writing");
- }
- if (isset($headers['content-length'])) {
- $length = $headers['content-length'];
- } else {
- $length = -1;
- }
- $bytes = 0;
- if ($callback) {
- call_user_func($callback, 'start', array(basename($dest_file), $length));
- }
- while ($data = @fread($fp, 1024)) {
- $bytes += strlen($data);
- if ($callback) {
- call_user_func($callback, 'bytesread', $bytes);
- }
- if (!@fwrite($wp, $data)) {
- fclose($fp);
- if ($callback) {
- call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
- }
- return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
- }
- }
- fclose($fp);
- fclose($wp);
- if ($callback) {
- call_user_func($callback, 'done', $bytes);
- }
- return $dest_file;
- }
- // }}}
- // {{{ sortPkgDeps()
- /**
- * Sort a list of arrays of array(downloaded packagefilename) by dependency.
- *
- * It also removes duplicate dependencies
- * @param array
- * @param boolean Sort packages in reverse order if true
- * @return array array of array(packagefilename, package.xml contents)
- */
- function sortPkgDeps(&$packages, $uninstall = false)
- {
- $ret = array();
- if ($uninstall) {
- foreach($packages as $packageinfo) {
- $ret[] = array('info' => $packageinfo);
- }
- } else {
- foreach($packages as $packagefile) {
- if (!is_array($packagefile)) {
- $ret[] = array('file' => $packagefile,
- 'info' => $a = $this->infoFromAny($packagefile),
- 'pkg' => $a['package']);
- } else {
- $ret[] = $packagefile;
- }
- }
- }
- $checkdupes = array();
- $newret = array();
- foreach($ret as $i => $p) {
- if (!isset($checkdupes[$p['info']['package']])) {
- $checkdupes[$p['info']['package']][] = $i;
- $newret[] = $p;
- }
- }
- $this->_packageSortTree = $this->_getPkgDepTree($newret);
- $func = $uninstall ? '_sortPkgDepsRev' : '_sortPkgDeps';
- usort($newret, array(&$this, $func));
- $this->_packageSortTree = null;
- $packages = $newret;
- }
- // }}}
- // {{{ _sortPkgDeps()
- /**
- * Compare two package's package.xml, and sort
- * so that dependencies are installed first
- *
- * This is a crude compare, real dependency checking is done on install.
- * The only purpose this serves is to make the command-line
- * order-independent (you can list a dependent package first, and
- * installation occurs in the order required)
- * @access private
- */
- function _sortPkgDeps($p1, $p2)
- {
- $p1name = $p1['info']['package'];
- $p2name = $p2['info']['package'];
- $p1deps = $this->_getPkgDeps($p1);
- $p2deps = $this->_getPkgDeps($p2);
- if (!count($p1deps) && !count($p2deps)) {
- return 0; // order makes no difference
- }
- if (!count($p1deps)) {
- return -1; // package 2 has dependencies, package 1 doesn't
- }
- if (!count($p2deps)) {
- return 1; // package 1 has dependencies, package 2 doesn't
- }
- // both have dependencies
- if (in_array($p1name, $p2deps)) {
- return -1; // put package 1 first: package 2 depends on package 1
- }
- if (in_array($p2name, $p1deps)) {
- return 1; // put package 2 first: package 1 depends on package 2
- }
- if ($this->_removedDependency($p1name, $p2name)) {
- return -1; // put package 1 first: package 2 depends on packages that depend on package 1
- }
- if ($this->_removedDependency($p2name, $p1name)) {
- return 1; // put package 2 first: package 1 depends on packages that depend on package 2
- }
- // doesn't really matter if neither depends on the other
- return 0;
- }
- // }}}
- // {{{ _sortPkgDepsRev()
- /**
- * Compare two package's package.xml, and sort
- * so that dependencies are uninstalled last
- *
- * This is a crude compare, real dependency checking is done on uninstall.
- * The only purpose this serves is to make the command-line
- * order-independent (you can list a dependency first, and
- * uninstallation occurs in the order required)
- * @access private
- */
- function _sortPkgDepsRev($p1, $p2)
- {
- $p1name = $p1['info']['package'];
- $p2name = $p2['info']['package'];
- $p1deps = $this->_getRevPkgDeps($p1);
- $p2deps = $this->_getRevPkgDeps($p2);
- if (!count($p1deps) && !count($p2deps)) {
- return 0; // order makes no difference
- }
- if (!count($p1deps)) {
- return 1; // package 2 has dependencies, package 1 doesn't
- }
- if (!count($p2deps)) {
- return -1; // package 2 has dependencies, package 1 doesn't
- }
- // both have dependencies
- if (in_array($p1name, $p2deps)) {
- return 1; // put package 1 last
- }
- if (in_array($p2name, $p1deps)) {
- return -1; // put package 2 last
- }
- if ($this->_removedDependency($p1name, $p2name)) {
- return 1; // put package 1 last: package 2 depends on packages that depend on package 1
- }
- if ($this->_removedDependency($p2name, $p1name)) {
- return -1; // put package 2 last: package 1 depends on packages that depend on package 2
- }
- // doesn't really matter if neither depends on the other
- return 0;
- }
- // }}}
- // {{{ _getPkgDeps()
- /**
- * get an array of package dependency names
- * @param array
- * @return array
- * @access private
- */
- function _getPkgDeps($p)
- {
- if (!isset($p['info']['releases'])) {
- return $this->_getRevPkgDeps($p);
- }
- $rel = array_shift($p['info']['releases']);
- if (!isset($rel['deps'])) {
- return array();
- }
- $ret = array();
- foreach($rel['deps'] as $dep) {
- if ($dep['type'] == 'pkg') {
- $ret[] = $dep['name'];
- }
- }
- return $ret;
- }
- // }}}
- // {{{ _getPkgDeps()
- /**
- * get an array representation of the package dependency tree
- * @return array
- * @access private
- */
- function _getPkgDepTree($packages)
- {
- $tree = array();
- foreach ($packages as $p) {
- $package = $p['info']['package'];
- $deps = $this->_getPkgDeps($p);
- $tree[$package] = $deps;
- }
- return $tree;
- }
- // }}}
- // {{{ _removedDependency($p1, $p2)
- /**
- * get an array of package dependency names for uninstall
- * @param string package 1 name
- * @param string package 2 name
- * @return bool
- * @access private
- */
- function _removedDependency($p1, $p2)
- {
- if (empty($this->_packageSortTree[$p2])) {
- return false;
- }
- if (!in_array($p1, $this->_packageSortTree[$p2])) {
- foreach ($this->_packageSortTree[$p2] as $potential) {
- if ($this->_removedDependency($p1, $potential)) {
- return true;
- }
- }
- return false;
- }
- return true;
- }
- // }}}
- // {{{ _getRevPkgDeps()
- /**
- * get an array of package dependency names for uninstall
- * @param array
- * @return array
- * @access private
- */
- function _getRevPkgDeps($p)
- {
- if (!isset($p['info']['release_deps'])) {
- return array();
- }
- $ret = array();
- foreach($p['info']['release_deps'] as $dep) {
- if ($dep['type'] == 'pkg') {
- $ret[] = $dep['name'];
- }
- }
- return $ret;
- }
- // }}}
- }
- ?>
|