v2.php 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050
  1. <?php
  2. /**
  3. * PEAR_PackageFile_v2, package.xml version 2.0
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * @category pear
  8. * @package PEAR
  9. * @author Greg Beaver <cellog@php.net>
  10. * @copyright 1997-2009 The Authors
  11. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  12. * @version CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
  13. * @link http://pear.php.net/package/PEAR
  14. * @since File available since Release 1.4.0a1
  15. */
  16. /**
  17. * For error handling
  18. */
  19. require_once 'PEAR/ErrorStack.php';
  20. /**
  21. * @category pear
  22. * @package PEAR
  23. * @author Greg Beaver <cellog@php.net>
  24. * @copyright 1997-2009 The Authors
  25. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  26. * @version Release: 1.9.4
  27. * @link http://pear.php.net/package/PEAR
  28. * @since Class available since Release 1.4.0a1
  29. */
  30. class PEAR_PackageFile_v2
  31. {
  32. /**
  33. * Parsed package information
  34. * @var array
  35. * @access private
  36. */
  37. var $_packageInfo = array();
  38. /**
  39. * path to package .tgz or false if this is a local/extracted package.xml
  40. * @var string|false
  41. * @access private
  42. */
  43. var $_archiveFile;
  44. /**
  45. * path to package .xml or false if this is an abstract parsed-from-string xml
  46. * @var string|false
  47. * @access private
  48. */
  49. var $_packageFile;
  50. /**
  51. * This is used by file analysis routines to log progress information
  52. * @var PEAR_Common
  53. * @access protected
  54. */
  55. var $_logger;
  56. /**
  57. * This is set to the highest validation level that has been validated
  58. *
  59. * If the package.xml is invalid or unknown, this is set to 0. If
  60. * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL. If
  61. * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
  62. * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING. This allows validation
  63. * "caching" to occur, which is particularly important for package validation, so
  64. * that PHP files are not validated twice
  65. * @var int
  66. * @access private
  67. */
  68. var $_isValid = 0;
  69. /**
  70. * True if the filelist has been validated
  71. * @param bool
  72. */
  73. var $_filesValid = false;
  74. /**
  75. * @var PEAR_Registry
  76. * @access protected
  77. */
  78. var $_registry;
  79. /**
  80. * @var PEAR_Config
  81. * @access protected
  82. */
  83. var $_config;
  84. /**
  85. * Optional Dependency group requested for installation
  86. * @var string
  87. * @access private
  88. */
  89. var $_requestedGroup = false;
  90. /**
  91. * @var PEAR_ErrorStack
  92. * @access protected
  93. */
  94. var $_stack;
  95. /**
  96. * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
  97. */
  98. var $_tasksNs;
  99. /**
  100. * Determines whether this packagefile was initialized only with partial package info
  101. *
  102. * If this package file was constructed via parsing REST, it will only contain
  103. *
  104. * - package name
  105. * - channel name
  106. * - dependencies
  107. * @var boolean
  108. * @access private
  109. */
  110. var $_incomplete = true;
  111. /**
  112. * @var PEAR_PackageFile_v2_Validator
  113. */
  114. var $_v2Validator;
  115. /**
  116. * The constructor merely sets up the private error stack
  117. */
  118. function PEAR_PackageFile_v2()
  119. {
  120. $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
  121. $this->_isValid = false;
  122. }
  123. /**
  124. * To make unit-testing easier
  125. * @param PEAR_Frontend_*
  126. * @param array options
  127. * @param PEAR_Config
  128. * @return PEAR_Downloader
  129. * @access protected
  130. */
  131. function &getPEARDownloader(&$i, $o, &$c)
  132. {
  133. $z = &new PEAR_Downloader($i, $o, $c);
  134. return $z;
  135. }
  136. /**
  137. * To make unit-testing easier
  138. * @param PEAR_Config
  139. * @param array options
  140. * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
  141. * @param int PEAR_VALIDATE_* constant
  142. * @return PEAR_Dependency2
  143. * @access protected
  144. */
  145. function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
  146. {
  147. if (!class_exists('PEAR_Dependency2')) {
  148. require_once 'PEAR/Dependency2.php';
  149. }
  150. $z = &new PEAR_Dependency2($c, $o, $p, $s);
  151. return $z;
  152. }
  153. function getInstalledBinary()
  154. {
  155. return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
  156. false;
  157. }
  158. /**
  159. * Installation of source package has failed, attempt to download and install the
  160. * binary version of this package.
  161. * @param PEAR_Installer
  162. * @return array|false
  163. */
  164. function installBinary(&$installer)
  165. {
  166. if (!OS_WINDOWS) {
  167. $a = false;
  168. return $a;
  169. }
  170. if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
  171. $releasetype = $this->getPackageType() . 'release';
  172. if (!is_array($installer->getInstallPackages())) {
  173. $a = false;
  174. return $a;
  175. }
  176. foreach ($installer->getInstallPackages() as $p) {
  177. if ($p->isExtension($this->_packageInfo['providesextension'])) {
  178. if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
  179. $a = false;
  180. return $a; // the user probably downloaded it separately
  181. }
  182. }
  183. }
  184. if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
  185. $installer->log(0, 'Attempting to download binary version of extension "' .
  186. $this->_packageInfo['providesextension'] . '"');
  187. $params = $this->_packageInfo[$releasetype]['binarypackage'];
  188. if (!is_array($params) || !isset($params[0])) {
  189. $params = array($params);
  190. }
  191. if (isset($this->_packageInfo['channel'])) {
  192. foreach ($params as $i => $param) {
  193. $params[$i] = array('channel' => $this->_packageInfo['channel'],
  194. 'package' => $param, 'version' => $this->getVersion());
  195. }
  196. }
  197. $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
  198. $installer->config);
  199. $verbose = $dl->config->get('verbose');
  200. $dl->config->set('verbose', -1);
  201. foreach ($params as $param) {
  202. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  203. $ret = $dl->download(array($param));
  204. PEAR::popErrorHandling();
  205. if (is_array($ret) && count($ret)) {
  206. break;
  207. }
  208. }
  209. $dl->config->set('verbose', $verbose);
  210. if (is_array($ret)) {
  211. if (count($ret) == 1) {
  212. $pf = $ret[0]->getPackageFile();
  213. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  214. $err = $installer->install($ret[0]);
  215. PEAR::popErrorHandling();
  216. if (is_array($err)) {
  217. $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
  218. // "install" self, so all dependencies will work transparently
  219. $this->_registry->addPackage2($this);
  220. $installer->log(0, 'Download and install of binary extension "' .
  221. $this->_registry->parsedPackageNameToString(
  222. array('channel' => $pf->getChannel(),
  223. 'package' => $pf->getPackage()), true) . '" successful');
  224. $a = array($ret[0], $err);
  225. return $a;
  226. }
  227. $installer->log(0, 'Download and install of binary extension "' .
  228. $this->_registry->parsedPackageNameToString(
  229. array('channel' => $pf->getChannel(),
  230. 'package' => $pf->getPackage()), true) . '" failed');
  231. }
  232. }
  233. }
  234. }
  235. $a = false;
  236. return $a;
  237. }
  238. /**
  239. * @return string|false Extension name
  240. */
  241. function getProvidesExtension()
  242. {
  243. if (in_array($this->getPackageType(),
  244. array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
  245. if (isset($this->_packageInfo['providesextension'])) {
  246. return $this->_packageInfo['providesextension'];
  247. }
  248. }
  249. return false;
  250. }
  251. /**
  252. * @param string Extension name
  253. * @return bool
  254. */
  255. function isExtension($extension)
  256. {
  257. if (in_array($this->getPackageType(),
  258. array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
  259. return $this->_packageInfo['providesextension'] == $extension;
  260. }
  261. return false;
  262. }
  263. /**
  264. * Tests whether every part of the package.xml 1.0 is represented in
  265. * this package.xml 2.0
  266. * @param PEAR_PackageFile_v1
  267. * @return bool
  268. */
  269. function isEquivalent($pf1)
  270. {
  271. if (!$pf1) {
  272. return true;
  273. }
  274. if ($this->getPackageType() == 'bundle') {
  275. return false;
  276. }
  277. $this->_stack->getErrors(true);
  278. if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
  279. return false;
  280. }
  281. $pass = true;
  282. if ($pf1->getPackage() != $this->getPackage()) {
  283. $this->_differentPackage($pf1->getPackage());
  284. $pass = false;
  285. }
  286. if ($pf1->getVersion() != $this->getVersion()) {
  287. $this->_differentVersion($pf1->getVersion());
  288. $pass = false;
  289. }
  290. if (trim($pf1->getSummary()) != $this->getSummary()) {
  291. $this->_differentSummary($pf1->getSummary());
  292. $pass = false;
  293. }
  294. if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
  295. preg_replace('/\s+/', '', $this->getDescription())) {
  296. $this->_differentDescription($pf1->getDescription());
  297. $pass = false;
  298. }
  299. if ($pf1->getState() != $this->getState()) {
  300. $this->_differentState($pf1->getState());
  301. $pass = false;
  302. }
  303. if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
  304. preg_replace('/\s+/', '', $pf1->getNotes()))) {
  305. $this->_differentNotes($pf1->getNotes());
  306. $pass = false;
  307. }
  308. $mymaintainers = $this->getMaintainers();
  309. $yourmaintainers = $pf1->getMaintainers();
  310. for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
  311. $reset = false;
  312. for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
  313. if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
  314. if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
  315. $this->_differentRole($mymaintainers[$i2]['handle'],
  316. $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
  317. $pass = false;
  318. }
  319. if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
  320. $this->_differentEmail($mymaintainers[$i2]['handle'],
  321. $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
  322. $pass = false;
  323. }
  324. if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
  325. $this->_differentName($mymaintainers[$i2]['handle'],
  326. $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
  327. $pass = false;
  328. }
  329. unset($mymaintainers[$i2]);
  330. $mymaintainers = array_values($mymaintainers);
  331. unset($yourmaintainers[$i1]);
  332. $yourmaintainers = array_values($yourmaintainers);
  333. $reset = true;
  334. break;
  335. }
  336. }
  337. if ($reset) {
  338. $i1 = -1;
  339. }
  340. }
  341. $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
  342. $filelist = $this->getFilelist();
  343. foreach ($pf1->getFilelist() as $file => $atts) {
  344. if (!isset($filelist[$file])) {
  345. $this->_missingFile($file);
  346. $pass = false;
  347. }
  348. }
  349. return $pass;
  350. }
  351. function _differentPackage($package)
  352. {
  353. $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
  354. 'self' => $this->getPackage()),
  355. 'package.xml 1.0 package "%package%" does not match "%self%"');
  356. }
  357. function _differentVersion($version)
  358. {
  359. $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
  360. 'self' => $this->getVersion()),
  361. 'package.xml 1.0 version "%version%" does not match "%self%"');
  362. }
  363. function _differentState($state)
  364. {
  365. $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
  366. 'self' => $this->getState()),
  367. 'package.xml 1.0 state "%state%" does not match "%self%"');
  368. }
  369. function _differentRole($handle, $role, $selfrole)
  370. {
  371. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  372. 'role' => $role, 'self' => $selfrole),
  373. 'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
  374. }
  375. function _differentEmail($handle, $email, $selfemail)
  376. {
  377. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  378. 'email' => $email, 'self' => $selfemail),
  379. 'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
  380. }
  381. function _differentName($handle, $name, $selfname)
  382. {
  383. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  384. 'name' => $name, 'self' => $selfname),
  385. 'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
  386. }
  387. function _unmatchedMaintainers($my, $yours)
  388. {
  389. if ($my) {
  390. array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
  391. $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
  392. 'package.xml 2.0 has unmatched extra maintainers "%handles%"');
  393. }
  394. if ($yours) {
  395. array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
  396. $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
  397. 'package.xml 1.0 has unmatched extra maintainers "%handles%"');
  398. }
  399. }
  400. function _differentNotes($notes)
  401. {
  402. $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
  403. $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
  404. substr($this->getNotes(), 0, 24) . '...';
  405. $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
  406. 'self' => $truncmynotes),
  407. 'package.xml 1.0 release notes "%notes%" do not match "%self%"');
  408. }
  409. function _differentSummary($summary)
  410. {
  411. $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
  412. $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
  413. substr($this->getsummary(), 0, 24) . '...';
  414. $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
  415. 'self' => $truncmysummary),
  416. 'package.xml 1.0 summary "%summary%" does not match "%self%"');
  417. }
  418. function _differentDescription($description)
  419. {
  420. $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
  421. $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
  422. substr($this->getdescription(), 0, 24) . '...');
  423. $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
  424. 'self' => $truncmydescription),
  425. 'package.xml 1.0 description "%description%" does not match "%self%"');
  426. }
  427. function _missingFile($file)
  428. {
  429. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  430. 'package.xml 1.0 file "%file%" is not present in <contents>');
  431. }
  432. /**
  433. * WARNING - do not use this function unless you know what you're doing
  434. */
  435. function setRawState($state)
  436. {
  437. if (!isset($this->_packageInfo['stability'])) {
  438. $this->_packageInfo['stability'] = array();
  439. }
  440. $this->_packageInfo['stability']['release'] = $state;
  441. }
  442. /**
  443. * WARNING - do not use this function unless you know what you're doing
  444. */
  445. function setRawCompatible($compatible)
  446. {
  447. $this->_packageInfo['compatible'] = $compatible;
  448. }
  449. /**
  450. * WARNING - do not use this function unless you know what you're doing
  451. */
  452. function setRawPackage($package)
  453. {
  454. $this->_packageInfo['name'] = $package;
  455. }
  456. /**
  457. * WARNING - do not use this function unless you know what you're doing
  458. */
  459. function setRawChannel($channel)
  460. {
  461. $this->_packageInfo['channel'] = $channel;
  462. }
  463. function setRequestedGroup($group)
  464. {
  465. $this->_requestedGroup = $group;
  466. }
  467. function getRequestedGroup()
  468. {
  469. if (isset($this->_requestedGroup)) {
  470. return $this->_requestedGroup;
  471. }
  472. return false;
  473. }
  474. /**
  475. * For saving in the registry.
  476. *
  477. * Set the last version that was installed
  478. * @param string
  479. */
  480. function setLastInstalledVersion($version)
  481. {
  482. $this->_packageInfo['_lastversion'] = $version;
  483. }
  484. /**
  485. * @return string|false
  486. */
  487. function getLastInstalledVersion()
  488. {
  489. if (isset($this->_packageInfo['_lastversion'])) {
  490. return $this->_packageInfo['_lastversion'];
  491. }
  492. return false;
  493. }
  494. /**
  495. * Determines whether this package.xml has post-install scripts or not
  496. * @return array|false
  497. */
  498. function listPostinstallScripts()
  499. {
  500. $filelist = $this->getFilelist();
  501. $contents = $this->getContents();
  502. $contents = $contents['dir']['file'];
  503. if (!is_array($contents) || !isset($contents[0])) {
  504. $contents = array($contents);
  505. }
  506. $taskfiles = array();
  507. foreach ($contents as $file) {
  508. $atts = $file['attribs'];
  509. unset($file['attribs']);
  510. if (count($file)) {
  511. $taskfiles[$atts['name']] = $file;
  512. }
  513. }
  514. $common = new PEAR_Common;
  515. $common->debug = $this->_config->get('verbose');
  516. $this->_scripts = array();
  517. $ret = array();
  518. foreach ($taskfiles as $name => $tasks) {
  519. if (!isset($filelist[$name])) {
  520. // ignored files will not be in the filelist
  521. continue;
  522. }
  523. $atts = $filelist[$name];
  524. foreach ($tasks as $tag => $raw) {
  525. $task = $this->getTask($tag);
  526. $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
  527. if ($task->isScript()) {
  528. $ret[] = $filelist[$name]['installed_as'];
  529. }
  530. }
  531. }
  532. if (count($ret)) {
  533. return $ret;
  534. }
  535. return false;
  536. }
  537. /**
  538. * Initialize post-install scripts for running
  539. *
  540. * This method can be used to detect post-install scripts, as the return value
  541. * indicates whether any exist
  542. * @return bool
  543. */
  544. function initPostinstallScripts()
  545. {
  546. $filelist = $this->getFilelist();
  547. $contents = $this->getContents();
  548. $contents = $contents['dir']['file'];
  549. if (!is_array($contents) || !isset($contents[0])) {
  550. $contents = array($contents);
  551. }
  552. $taskfiles = array();
  553. foreach ($contents as $file) {
  554. $atts = $file['attribs'];
  555. unset($file['attribs']);
  556. if (count($file)) {
  557. $taskfiles[$atts['name']] = $file;
  558. }
  559. }
  560. $common = new PEAR_Common;
  561. $common->debug = $this->_config->get('verbose');
  562. $this->_scripts = array();
  563. foreach ($taskfiles as $name => $tasks) {
  564. if (!isset($filelist[$name])) {
  565. // file was not installed due to installconditions
  566. continue;
  567. }
  568. $atts = $filelist[$name];
  569. foreach ($tasks as $tag => $raw) {
  570. $taskname = $this->getTask($tag);
  571. $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
  572. if (!$task->isScript()) {
  573. continue; // scripts are only handled after installation
  574. }
  575. $lastversion = isset($this->_packageInfo['_lastversion']) ?
  576. $this->_packageInfo['_lastversion'] : null;
  577. $task->init($raw, $atts, $lastversion);
  578. $res = $task->startSession($this, $atts['installed_as']);
  579. if (!$res) {
  580. continue; // skip this file
  581. }
  582. if (PEAR::isError($res)) {
  583. return $res;
  584. }
  585. $assign = &$task;
  586. $this->_scripts[] = &$assign;
  587. }
  588. }
  589. if (count($this->_scripts)) {
  590. return true;
  591. }
  592. return false;
  593. }
  594. function runPostinstallScripts()
  595. {
  596. if ($this->initPostinstallScripts()) {
  597. $ui = &PEAR_Frontend::singleton();
  598. if ($ui) {
  599. $ui->runPostinstallScripts($this->_scripts, $this);
  600. }
  601. }
  602. }
  603. /**
  604. * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
  605. * <file> tags.
  606. */
  607. function flattenFilelist()
  608. {
  609. if (isset($this->_packageInfo['bundle'])) {
  610. return;
  611. }
  612. $filelist = array();
  613. if (isset($this->_packageInfo['contents']['dir']['dir'])) {
  614. $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
  615. if (!isset($filelist[1])) {
  616. $filelist = $filelist[0];
  617. }
  618. $this->_packageInfo['contents']['dir']['file'] = $filelist;
  619. unset($this->_packageInfo['contents']['dir']['dir']);
  620. } else {
  621. // else already flattened but check for baseinstalldir propagation
  622. if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
  623. if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
  624. foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
  625. if (isset($file['attribs']['baseinstalldir'])) {
  626. continue;
  627. }
  628. $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
  629. = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
  630. }
  631. } else {
  632. if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
  633. $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
  634. = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
  635. }
  636. }
  637. }
  638. }
  639. }
  640. /**
  641. * @param array the final flattened file list
  642. * @param array the current directory being processed
  643. * @param string|false any recursively inherited baeinstalldir attribute
  644. * @param string private recursion variable
  645. * @return array
  646. * @access protected
  647. */
  648. function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
  649. {
  650. if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
  651. $baseinstall = $dir['attribs']['baseinstalldir'];
  652. }
  653. if (isset($dir['dir'])) {
  654. if (!isset($dir['dir'][0])) {
  655. $dir['dir'] = array($dir['dir']);
  656. }
  657. foreach ($dir['dir'] as $subdir) {
  658. if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
  659. $name = '*unknown*';
  660. } else {
  661. $name = $subdir['attribs']['name'];
  662. }
  663. $newpath = empty($path) ? $name :
  664. $path . '/' . $name;
  665. $this->_getFlattenedFilelist($files, $subdir,
  666. $baseinstall, $newpath);
  667. }
  668. }
  669. if (isset($dir['file'])) {
  670. if (!isset($dir['file'][0])) {
  671. $dir['file'] = array($dir['file']);
  672. }
  673. foreach ($dir['file'] as $file) {
  674. $attrs = $file['attribs'];
  675. $name = $attrs['name'];
  676. if ($baseinstall && !isset($attrs['baseinstalldir'])) {
  677. $attrs['baseinstalldir'] = $baseinstall;
  678. }
  679. $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
  680. $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
  681. $attrs['name']);
  682. $file['attribs'] = $attrs;
  683. $files[] = $file;
  684. }
  685. }
  686. }
  687. function setConfig(&$config)
  688. {
  689. $this->_config = &$config;
  690. $this->_registry = &$config->getRegistry();
  691. }
  692. function setLogger(&$logger)
  693. {
  694. if (!is_object($logger) || !method_exists($logger, 'log')) {
  695. return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
  696. }
  697. $this->_logger = &$logger;
  698. }
  699. /**
  700. * WARNING - do not use this function directly unless you know what you're doing
  701. */
  702. function setDeps($deps)
  703. {
  704. $this->_packageInfo['dependencies'] = $deps;
  705. }
  706. /**
  707. * WARNING - do not use this function directly unless you know what you're doing
  708. */
  709. function setCompatible($compat)
  710. {
  711. $this->_packageInfo['compatible'] = $compat;
  712. }
  713. function setPackagefile($file, $archive = false)
  714. {
  715. $this->_packageFile = $file;
  716. $this->_archiveFile = $archive ? $archive : $file;
  717. }
  718. /**
  719. * Wrapper to {@link PEAR_ErrorStack::getErrors()}
  720. * @param boolean determines whether to purge the error stack after retrieving
  721. * @return array
  722. */
  723. function getValidationWarnings($purge = true)
  724. {
  725. return $this->_stack->getErrors($purge);
  726. }
  727. function getPackageFile()
  728. {
  729. return $this->_packageFile;
  730. }
  731. function getArchiveFile()
  732. {
  733. return $this->_archiveFile;
  734. }
  735. /**
  736. * Directly set the array that defines this packagefile
  737. *
  738. * WARNING: no validation. This should only be performed by internal methods
  739. * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
  740. * @param array
  741. */
  742. function fromArray($pinfo)
  743. {
  744. unset($pinfo['old']);
  745. unset($pinfo['xsdversion']);
  746. // If the changelog isn't an array then it was passed in as an empty tag
  747. if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) {
  748. unset($pinfo['changelog']);
  749. }
  750. $this->_incomplete = false;
  751. $this->_packageInfo = $pinfo;
  752. }
  753. function isIncomplete()
  754. {
  755. return $this->_incomplete;
  756. }
  757. /**
  758. * @return array
  759. */
  760. function toArray($forreg = false)
  761. {
  762. if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
  763. return false;
  764. }
  765. return $this->getArray($forreg);
  766. }
  767. function getArray($forReg = false)
  768. {
  769. if ($forReg) {
  770. $arr = $this->_packageInfo;
  771. $arr['old'] = array();
  772. $arr['old']['version'] = $this->getVersion();
  773. $arr['old']['release_date'] = $this->getDate();
  774. $arr['old']['release_state'] = $this->getState();
  775. $arr['old']['release_license'] = $this->getLicense();
  776. $arr['old']['release_notes'] = $this->getNotes();
  777. $arr['old']['release_deps'] = $this->getDeps();
  778. $arr['old']['maintainers'] = $this->getMaintainers();
  779. $arr['xsdversion'] = '2.0';
  780. return $arr;
  781. } else {
  782. $info = $this->_packageInfo;
  783. unset($info['dirtree']);
  784. if (isset($info['_lastversion'])) {
  785. unset($info['_lastversion']);
  786. }
  787. if (isset($info['#binarypackage'])) {
  788. unset($info['#binarypackage']);
  789. }
  790. return $info;
  791. }
  792. }
  793. function packageInfo($field)
  794. {
  795. $arr = $this->getArray(true);
  796. if ($field == 'state') {
  797. return $arr['stability']['release'];
  798. }
  799. if ($field == 'api-version') {
  800. return $arr['version']['api'];
  801. }
  802. if ($field == 'api-state') {
  803. return $arr['stability']['api'];
  804. }
  805. if (isset($arr['old'][$field])) {
  806. if (!is_string($arr['old'][$field])) {
  807. return null;
  808. }
  809. return $arr['old'][$field];
  810. }
  811. if (isset($arr[$field])) {
  812. if (!is_string($arr[$field])) {
  813. return null;
  814. }
  815. return $arr[$field];
  816. }
  817. return null;
  818. }
  819. function getName()
  820. {
  821. return $this->getPackage();
  822. }
  823. function getPackage()
  824. {
  825. if (isset($this->_packageInfo['name'])) {
  826. return $this->_packageInfo['name'];
  827. }
  828. return false;
  829. }
  830. function getChannel()
  831. {
  832. if (isset($this->_packageInfo['uri'])) {
  833. return '__uri';
  834. }
  835. if (isset($this->_packageInfo['channel'])) {
  836. return strtolower($this->_packageInfo['channel']);
  837. }
  838. return false;
  839. }
  840. function getUri()
  841. {
  842. if (isset($this->_packageInfo['uri'])) {
  843. return $this->_packageInfo['uri'];
  844. }
  845. return false;
  846. }
  847. function getExtends()
  848. {
  849. if (isset($this->_packageInfo['extends'])) {
  850. return $this->_packageInfo['extends'];
  851. }
  852. return false;
  853. }
  854. function getSummary()
  855. {
  856. if (isset($this->_packageInfo['summary'])) {
  857. return $this->_packageInfo['summary'];
  858. }
  859. return false;
  860. }
  861. function getDescription()
  862. {
  863. if (isset($this->_packageInfo['description'])) {
  864. return $this->_packageInfo['description'];
  865. }
  866. return false;
  867. }
  868. function getMaintainers($raw = false)
  869. {
  870. if (!isset($this->_packageInfo['lead'])) {
  871. return false;
  872. }
  873. if ($raw) {
  874. $ret = array('lead' => $this->_packageInfo['lead']);
  875. (isset($this->_packageInfo['developer'])) ?
  876. $ret['developer'] = $this->_packageInfo['developer'] :null;
  877. (isset($this->_packageInfo['contributor'])) ?
  878. $ret['contributor'] = $this->_packageInfo['contributor'] :null;
  879. (isset($this->_packageInfo['helper'])) ?
  880. $ret['helper'] = $this->_packageInfo['helper'] :null;
  881. return $ret;
  882. } else {
  883. $ret = array();
  884. $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
  885. array($this->_packageInfo['lead']);
  886. foreach ($leads as $lead) {
  887. $s = $lead;
  888. $s['handle'] = $s['user'];
  889. unset($s['user']);
  890. $s['role'] = 'lead';
  891. $ret[] = $s;
  892. }
  893. if (isset($this->_packageInfo['developer'])) {
  894. $leads = isset($this->_packageInfo['developer'][0]) ?
  895. $this->_packageInfo['developer'] :
  896. array($this->_packageInfo['developer']);
  897. foreach ($leads as $maintainer) {
  898. $s = $maintainer;
  899. $s['handle'] = $s['user'];
  900. unset($s['user']);
  901. $s['role'] = 'developer';
  902. $ret[] = $s;
  903. }
  904. }
  905. if (isset($this->_packageInfo['contributor'])) {
  906. $leads = isset($this->_packageInfo['contributor'][0]) ?
  907. $this->_packageInfo['contributor'] :
  908. array($this->_packageInfo['contributor']);
  909. foreach ($leads as $maintainer) {
  910. $s = $maintainer;
  911. $s['handle'] = $s['user'];
  912. unset($s['user']);
  913. $s['role'] = 'contributor';
  914. $ret[] = $s;
  915. }
  916. }
  917. if (isset($this->_packageInfo['helper'])) {
  918. $leads = isset($this->_packageInfo['helper'][0]) ?
  919. $this->_packageInfo['helper'] :
  920. array($this->_packageInfo['helper']);
  921. foreach ($leads as $maintainer) {
  922. $s = $maintainer;
  923. $s['handle'] = $s['user'];
  924. unset($s['user']);
  925. $s['role'] = 'helper';
  926. $ret[] = $s;
  927. }
  928. }
  929. return $ret;
  930. }
  931. return false;
  932. }
  933. function getLeads()
  934. {
  935. if (isset($this->_packageInfo['lead'])) {
  936. return $this->_packageInfo['lead'];
  937. }
  938. return false;
  939. }
  940. function getDevelopers()
  941. {
  942. if (isset($this->_packageInfo['developer'])) {
  943. return $this->_packageInfo['developer'];
  944. }
  945. return false;
  946. }
  947. function getContributors()
  948. {
  949. if (isset($this->_packageInfo['contributor'])) {
  950. return $this->_packageInfo['contributor'];
  951. }
  952. return false;
  953. }
  954. function getHelpers()
  955. {
  956. if (isset($this->_packageInfo['helper'])) {
  957. return $this->_packageInfo['helper'];
  958. }
  959. return false;
  960. }
  961. function setDate($date)
  962. {
  963. if (!isset($this->_packageInfo['date'])) {
  964. // ensure that the extends tag is set up in the right location
  965. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  966. array('time', 'version',
  967. 'stability', 'license', 'notes', 'contents', 'compatible',
  968. 'dependencies', 'providesextension', 'srcpackage', 'srcuri',
  969. 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
  970. 'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
  971. }
  972. $this->_packageInfo['date'] = $date;
  973. $this->_isValid = 0;
  974. }
  975. function setTime($time)
  976. {
  977. $this->_isValid = 0;
  978. if (!isset($this->_packageInfo['time'])) {
  979. // ensure that the time tag is set up in the right location
  980. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  981. array('version',
  982. 'stability', 'license', 'notes', 'contents', 'compatible',
  983. 'dependencies', 'providesextension', 'srcpackage', 'srcuri',
  984. 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
  985. 'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
  986. }
  987. $this->_packageInfo['time'] = $time;
  988. }
  989. function getDate()
  990. {
  991. if (isset($this->_packageInfo['date'])) {
  992. return $this->_packageInfo['date'];
  993. }
  994. return false;
  995. }
  996. function getTime()
  997. {
  998. if (isset($this->_packageInfo['time'])) {
  999. return $this->_packageInfo['time'];
  1000. }
  1001. return false;
  1002. }
  1003. /**
  1004. * @param package|api version category to return
  1005. */
  1006. function getVersion($key = 'release')
  1007. {
  1008. if (isset($this->_packageInfo['version'][$key])) {
  1009. return $this->_packageInfo['version'][$key];
  1010. }
  1011. return false;
  1012. }
  1013. function getStability()
  1014. {
  1015. if (isset($this->_packageInfo['stability'])) {
  1016. return $this->_packageInfo['stability'];
  1017. }
  1018. return false;
  1019. }
  1020. function getState($key = 'release')
  1021. {
  1022. if (isset($this->_packageInfo['stability'][$key])) {
  1023. return $this->_packageInfo['stability'][$key];
  1024. }
  1025. return false;
  1026. }
  1027. function getLicense($raw = false)
  1028. {
  1029. if (isset($this->_packageInfo['license'])) {
  1030. if ($raw) {
  1031. return $this->_packageInfo['license'];
  1032. }
  1033. if (is_array($this->_packageInfo['license'])) {
  1034. return $this->_packageInfo['license']['_content'];
  1035. } else {
  1036. return $this->_packageInfo['license'];
  1037. }
  1038. }
  1039. return false;
  1040. }
  1041. function getLicenseLocation()
  1042. {
  1043. if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
  1044. return false;
  1045. }
  1046. return $this->_packageInfo['license']['attribs'];
  1047. }
  1048. function getNotes()
  1049. {
  1050. if (isset($this->_packageInfo['notes'])) {
  1051. return $this->_packageInfo['notes'];
  1052. }
  1053. return false;
  1054. }
  1055. /**
  1056. * Return the <usesrole> tag contents, if any
  1057. * @return array|false
  1058. */
  1059. function getUsesrole()
  1060. {
  1061. if (isset($this->_packageInfo['usesrole'])) {
  1062. return $this->_packageInfo['usesrole'];
  1063. }
  1064. return false;
  1065. }
  1066. /**
  1067. * Return the <usestask> tag contents, if any
  1068. * @return array|false
  1069. */
  1070. function getUsestask()
  1071. {
  1072. if (isset($this->_packageInfo['usestask'])) {
  1073. return $this->_packageInfo['usestask'];
  1074. }
  1075. return false;
  1076. }
  1077. /**
  1078. * This should only be used to retrieve filenames and install attributes
  1079. */
  1080. function getFilelist($preserve = false)
  1081. {
  1082. if (isset($this->_packageInfo['filelist']) && !$preserve) {
  1083. return $this->_packageInfo['filelist'];
  1084. }
  1085. $this->flattenFilelist();
  1086. if ($contents = $this->getContents()) {
  1087. $ret = array();
  1088. if (!isset($contents['dir'])) {
  1089. return false;
  1090. }
  1091. if (!isset($contents['dir']['file'][0])) {
  1092. $contents['dir']['file'] = array($contents['dir']['file']);
  1093. }
  1094. foreach ($contents['dir']['file'] as $file) {
  1095. $name = $file['attribs']['name'];
  1096. if (!$preserve) {
  1097. $file = $file['attribs'];
  1098. }
  1099. $ret[$name] = $file;
  1100. }
  1101. if (!$preserve) {
  1102. $this->_packageInfo['filelist'] = $ret;
  1103. }
  1104. return $ret;
  1105. }
  1106. return false;
  1107. }
  1108. /**
  1109. * Return configure options array, if any
  1110. *
  1111. * @return array|false
  1112. */
  1113. function getConfigureOptions()
  1114. {
  1115. if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
  1116. return false;
  1117. }
  1118. $releases = $this->getReleases();
  1119. if (isset($releases[0])) {
  1120. $releases = $releases[0];
  1121. }
  1122. if (isset($releases['configureoption'])) {
  1123. if (!isset($releases['configureoption'][0])) {
  1124. $releases['configureoption'] = array($releases['configureoption']);
  1125. }
  1126. for ($i = 0; $i < count($releases['configureoption']); $i++) {
  1127. $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
  1128. }
  1129. return $releases['configureoption'];
  1130. }
  1131. return false;
  1132. }
  1133. /**
  1134. * This is only used at install-time, after all serialization
  1135. * is over.
  1136. */
  1137. function resetFilelist()
  1138. {
  1139. $this->_packageInfo['filelist'] = array();
  1140. }
  1141. /**
  1142. * Retrieve a list of files that should be installed on this computer
  1143. * @return array
  1144. */
  1145. function getInstallationFilelist($forfilecheck = false)
  1146. {
  1147. $contents = $this->getFilelist(true);
  1148. if (isset($contents['dir']['attribs']['baseinstalldir'])) {
  1149. $base = $contents['dir']['attribs']['baseinstalldir'];
  1150. }
  1151. if (isset($this->_packageInfo['bundle'])) {
  1152. return PEAR::raiseError(
  1153. 'Exception: bundles should be handled in download code only');
  1154. }
  1155. $release = $this->getReleases();
  1156. if ($release) {
  1157. if (!isset($release[0])) {
  1158. if (!isset($release['installconditions']) && !isset($release['filelist'])) {
  1159. if ($forfilecheck) {
  1160. return $this->getFilelist();
  1161. }
  1162. return $contents;
  1163. }
  1164. $release = array($release);
  1165. }
  1166. $depchecker = &$this->getPEARDependency2($this->_config, array(),
  1167. array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
  1168. PEAR_VALIDATE_INSTALLING);
  1169. foreach ($release as $instance) {
  1170. if (isset($instance['installconditions'])) {
  1171. $installconditions = $instance['installconditions'];
  1172. if (is_array($installconditions)) {
  1173. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  1174. foreach ($installconditions as $type => $conditions) {
  1175. if (!isset($conditions[0])) {
  1176. $conditions = array($conditions);
  1177. }
  1178. foreach ($conditions as $condition) {
  1179. $ret = $depchecker->{"validate{$type}Dependency"}($condition);
  1180. if (PEAR::isError($ret)) {
  1181. PEAR::popErrorHandling();
  1182. continue 3; // skip this release
  1183. }
  1184. }
  1185. }
  1186. PEAR::popErrorHandling();
  1187. }
  1188. }
  1189. // this is the release to use
  1190. if (isset($instance['filelist'])) {
  1191. // ignore files
  1192. if (isset($instance['filelist']['ignore'])) {
  1193. $ignore = isset($instance['filelist']['ignore'][0]) ?
  1194. $instance['filelist']['ignore'] :
  1195. array($instance['filelist']['ignore']);
  1196. foreach ($ignore as $ig) {
  1197. unset ($contents[$ig['attribs']['name']]);
  1198. }
  1199. }
  1200. // install files as this name
  1201. if (isset($instance['filelist']['install'])) {
  1202. $installas = isset($instance['filelist']['install'][0]) ?
  1203. $instance['filelist']['install'] :
  1204. array($instance['filelist']['install']);
  1205. foreach ($installas as $as) {
  1206. $contents[$as['attribs']['name']]['attribs']['install-as'] =
  1207. $as['attribs']['as'];
  1208. }
  1209. }
  1210. }
  1211. if ($forfilecheck) {
  1212. foreach ($contents as $file => $attrs) {
  1213. $contents[$file] = $attrs['attribs'];
  1214. }
  1215. }
  1216. return $contents;
  1217. }
  1218. } else { // simple release - no installconditions or install-as
  1219. if ($forfilecheck) {
  1220. return $this->getFilelist();
  1221. }
  1222. return $contents;
  1223. }
  1224. // no releases matched
  1225. return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
  1226. 'system, extensions installed, or architecture, cannot install');
  1227. }
  1228. /**
  1229. * This is only used at install-time, after all serialization
  1230. * is over.
  1231. * @param string file name
  1232. * @param string installed path
  1233. */
  1234. function setInstalledAs($file, $path)
  1235. {
  1236. if ($path) {
  1237. return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
  1238. }
  1239. unset($this->_packageInfo['filelist'][$file]['installed_as']);
  1240. }
  1241. function getInstalledLocation($file)
  1242. {
  1243. if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
  1244. return $this->_packageInfo['filelist'][$file]['installed_as'];
  1245. }
  1246. return false;
  1247. }
  1248. /**
  1249. * This is only used at install-time, after all serialization
  1250. * is over.
  1251. */
  1252. function installedFile($file, $atts)
  1253. {
  1254. if (isset($this->_packageInfo['filelist'][$file])) {
  1255. $this->_packageInfo['filelist'][$file] =
  1256. array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
  1257. } else {
  1258. $this->_packageInfo['filelist'][$file] = $atts['attribs'];
  1259. }
  1260. }
  1261. /**
  1262. * Retrieve the contents tag
  1263. */
  1264. function getContents()
  1265. {
  1266. if (isset($this->_packageInfo['contents'])) {
  1267. return $this->_packageInfo['contents'];
  1268. }
  1269. return false;
  1270. }
  1271. /**
  1272. * @param string full path to file
  1273. * @param string attribute name
  1274. * @param string attribute value
  1275. * @param int risky but fast - use this to choose a file based on its position in the list
  1276. * of files. Index is zero-based like PHP arrays.
  1277. * @return bool success of operation
  1278. */
  1279. function setFileAttribute($filename, $attr, $value, $index = false)
  1280. {
  1281. $this->_isValid = 0;
  1282. if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
  1283. $this->_filesValid = false;
  1284. }
  1285. if ($index !== false &&
  1286. isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
  1287. $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
  1288. return true;
  1289. }
  1290. if (!isset($this->_packageInfo['contents']['dir']['file'])) {
  1291. return false;
  1292. }
  1293. $files = $this->_packageInfo['contents']['dir']['file'];
  1294. if (!isset($files[0])) {
  1295. $files = array($files);
  1296. $ind = false;
  1297. } else {
  1298. $ind = true;
  1299. }
  1300. foreach ($files as $i => $file) {
  1301. if (isset($file['attribs'])) {
  1302. if ($file['attribs']['name'] == $filename) {
  1303. if ($ind) {
  1304. $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
  1305. } else {
  1306. $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
  1307. }
  1308. return true;
  1309. }
  1310. }
  1311. }
  1312. return false;
  1313. }
  1314. function setDirtree($path)
  1315. {
  1316. if (!isset($this->_packageInfo['dirtree'])) {
  1317. $this->_packageInfo['dirtree'] = array();
  1318. }
  1319. $this->_packageInfo['dirtree'][$path] = true;
  1320. }
  1321. function getDirtree()
  1322. {
  1323. if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
  1324. return $this->_packageInfo['dirtree'];
  1325. }
  1326. return false;
  1327. }
  1328. function resetDirtree()
  1329. {
  1330. unset($this->_packageInfo['dirtree']);
  1331. }
  1332. /**
  1333. * Determines whether this package claims it is compatible with the version of
  1334. * the package that has a recommended version dependency
  1335. * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
  1336. * @return boolean
  1337. */
  1338. function isCompatible($pf)
  1339. {
  1340. if (!isset($this->_packageInfo['compatible'])) {
  1341. return false;
  1342. }
  1343. if (!isset($this->_packageInfo['channel'])) {
  1344. return false;
  1345. }
  1346. $me = $pf->getVersion();
  1347. $compatible = $this->_packageInfo['compatible'];
  1348. if (!isset($compatible[0])) {
  1349. $compatible = array($compatible);
  1350. }
  1351. $found = false;
  1352. foreach ($compatible as $info) {
  1353. if (strtolower($info['name']) == strtolower($pf->getPackage())) {
  1354. if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
  1355. $found = true;
  1356. break;
  1357. }
  1358. }
  1359. }
  1360. if (!$found) {
  1361. return false;
  1362. }
  1363. if (isset($info['exclude'])) {
  1364. if (!isset($info['exclude'][0])) {
  1365. $info['exclude'] = array($info['exclude']);
  1366. }
  1367. foreach ($info['exclude'] as $exclude) {
  1368. if (version_compare($me, $exclude, '==')) {
  1369. return false;
  1370. }
  1371. }
  1372. }
  1373. if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
  1374. return true;
  1375. }
  1376. return false;
  1377. }
  1378. /**
  1379. * @return array|false
  1380. */
  1381. function getCompatible()
  1382. {
  1383. if (isset($this->_packageInfo['compatible'])) {
  1384. return $this->_packageInfo['compatible'];
  1385. }
  1386. return false;
  1387. }
  1388. function getDependencies()
  1389. {
  1390. if (isset($this->_packageInfo['dependencies'])) {
  1391. return $this->_packageInfo['dependencies'];
  1392. }
  1393. return false;
  1394. }
  1395. function isSubpackageOf($p)
  1396. {
  1397. return $p->isSubpackage($this);
  1398. }
  1399. /**
  1400. * Determines whether the passed in package is a subpackage of this package.
  1401. *
  1402. * No version checking is done, only name verification.
  1403. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  1404. * @return bool
  1405. */
  1406. function isSubpackage($p)
  1407. {
  1408. $sub = array();
  1409. if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
  1410. $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
  1411. if (!isset($sub[0])) {
  1412. $sub = array($sub);
  1413. }
  1414. }
  1415. if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
  1416. $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
  1417. if (!isset($sub1[0])) {
  1418. $sub1 = array($sub1);
  1419. }
  1420. $sub = array_merge($sub, $sub1);
  1421. }
  1422. if (isset($this->_packageInfo['dependencies']['group'])) {
  1423. $group = $this->_packageInfo['dependencies']['group'];
  1424. if (!isset($group[0])) {
  1425. $group = array($group);
  1426. }
  1427. foreach ($group as $deps) {
  1428. if (isset($deps['subpackage'])) {
  1429. $sub2 = $deps['subpackage'];
  1430. if (!isset($sub2[0])) {
  1431. $sub2 = array($sub2);
  1432. }
  1433. $sub = array_merge($sub, $sub2);
  1434. }
  1435. }
  1436. }
  1437. foreach ($sub as $dep) {
  1438. if (strtolower($dep['name']) == strtolower($p->getPackage())) {
  1439. if (isset($dep['channel'])) {
  1440. if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
  1441. return true;
  1442. }
  1443. } else {
  1444. if ($dep['uri'] == $p->getURI()) {
  1445. return true;
  1446. }
  1447. }
  1448. }
  1449. }
  1450. return false;
  1451. }
  1452. function dependsOn($package, $channel)
  1453. {
  1454. if (!($deps = $this->getDependencies())) {
  1455. return false;
  1456. }
  1457. foreach (array('package', 'subpackage') as $type) {
  1458. foreach (array('required', 'optional') as $needed) {
  1459. if (isset($deps[$needed][$type])) {
  1460. if (!isset($deps[$needed][$type][0])) {
  1461. $deps[$needed][$type] = array($deps[$needed][$type]);
  1462. }
  1463. foreach ($deps[$needed][$type] as $dep) {
  1464. $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
  1465. if (strtolower($dep['name']) == strtolower($package) &&
  1466. $depchannel == $channel) {
  1467. return true;
  1468. }
  1469. }
  1470. }
  1471. }
  1472. if (isset($deps['group'])) {
  1473. if (!isset($deps['group'][0])) {
  1474. $dep['group'] = array($deps['group']);
  1475. }
  1476. foreach ($deps['group'] as $group) {
  1477. if (isset($group[$type])) {
  1478. if (!is_array($group[$type])) {
  1479. $group[$type] = array($group[$type]);
  1480. }
  1481. foreach ($group[$type] as $dep) {
  1482. $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
  1483. if (strtolower($dep['name']) == strtolower($package) &&
  1484. $depchannel == $channel) {
  1485. return true;
  1486. }
  1487. }
  1488. }
  1489. }
  1490. }
  1491. }
  1492. return false;
  1493. }
  1494. /**
  1495. * Get the contents of a dependency group
  1496. * @param string
  1497. * @return array|false
  1498. */
  1499. function getDependencyGroup($name)
  1500. {
  1501. $name = strtolower($name);
  1502. if (!isset($this->_packageInfo['dependencies']['group'])) {
  1503. return false;
  1504. }
  1505. $groups = $this->_packageInfo['dependencies']['group'];
  1506. if (!isset($groups[0])) {
  1507. $groups = array($groups);
  1508. }
  1509. foreach ($groups as $group) {
  1510. if (strtolower($group['attribs']['name']) == $name) {
  1511. return $group;
  1512. }
  1513. }
  1514. return false;
  1515. }
  1516. /**
  1517. * Retrieve a partial package.xml 1.0 representation of dependencies
  1518. *
  1519. * a very limited representation of dependencies is returned by this method.
  1520. * The <exclude> tag for excluding certain versions of a dependency is
  1521. * completely ignored. In addition, dependency groups are ignored, with the
  1522. * assumption that all dependencies in dependency groups are also listed in
  1523. * the optional group that work with all dependency groups
  1524. * @param boolean return package.xml 2.0 <dependencies> tag
  1525. * @return array|false
  1526. */
  1527. function getDeps($raw = false, $nopearinstaller = false)
  1528. {
  1529. if (isset($this->_packageInfo['dependencies'])) {
  1530. if ($raw) {
  1531. return $this->_packageInfo['dependencies'];
  1532. }
  1533. $ret = array();
  1534. $map = array(
  1535. 'php' => 'php',
  1536. 'package' => 'pkg',
  1537. 'subpackage' => 'pkg',
  1538. 'extension' => 'ext',
  1539. 'os' => 'os',
  1540. 'pearinstaller' => 'pkg',
  1541. );
  1542. foreach (array('required', 'optional') as $type) {
  1543. $optional = ($type == 'optional') ? 'yes' : 'no';
  1544. if (!isset($this->_packageInfo['dependencies'][$type])
  1545. || empty($this->_packageInfo['dependencies'][$type])) {
  1546. continue;
  1547. }
  1548. foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
  1549. if ($dtype == 'pearinstaller' && $nopearinstaller) {
  1550. continue;
  1551. }
  1552. if (!isset($deps[0])) {
  1553. $deps = array($deps);
  1554. }
  1555. foreach ($deps as $dep) {
  1556. if (!isset($map[$dtype])) {
  1557. // no support for arch type
  1558. continue;
  1559. }
  1560. if ($dtype == 'pearinstaller') {
  1561. $dep['name'] = 'PEAR';
  1562. $dep['channel'] = 'pear.php.net';
  1563. }
  1564. $s = array('type' => $map[$dtype]);
  1565. if (isset($dep['channel'])) {
  1566. $s['channel'] = $dep['channel'];
  1567. }
  1568. if (isset($dep['uri'])) {
  1569. $s['uri'] = $dep['uri'];
  1570. }
  1571. if (isset($dep['name'])) {
  1572. $s['name'] = $dep['name'];
  1573. }
  1574. if (isset($dep['conflicts'])) {
  1575. $s['rel'] = 'not';
  1576. } else {
  1577. if (!isset($dep['min']) &&
  1578. !isset($dep['max'])) {
  1579. $s['rel'] = 'has';
  1580. $s['optional'] = $optional;
  1581. } elseif (isset($dep['min']) &&
  1582. isset($dep['max'])) {
  1583. $s['rel'] = 'ge';
  1584. $s1 = $s;
  1585. $s1['rel'] = 'le';
  1586. $s['version'] = $dep['min'];
  1587. $s1['version'] = $dep['max'];
  1588. if (isset($dep['channel'])) {
  1589. $s1['channel'] = $dep['channel'];
  1590. }
  1591. if ($dtype != 'php') {
  1592. $s['name'] = $dep['name'];
  1593. $s1['name'] = $dep['name'];
  1594. }
  1595. $s['optional'] = $optional;
  1596. $s1['optional'] = $optional;
  1597. $ret[] = $s1;
  1598. } elseif (isset($dep['min'])) {
  1599. if (isset($dep['exclude']) &&
  1600. $dep['exclude'] == $dep['min']) {
  1601. $s['rel'] = 'gt';
  1602. } else {
  1603. $s['rel'] = 'ge';
  1604. }
  1605. $s['version'] = $dep['min'];
  1606. $s['optional'] = $optional;
  1607. if ($dtype != 'php') {
  1608. $s['name'] = $dep['name'];
  1609. }
  1610. } elseif (isset($dep['max'])) {
  1611. if (isset($dep['exclude']) &&
  1612. $dep['exclude'] == $dep['max']) {
  1613. $s['rel'] = 'lt';
  1614. } else {
  1615. $s['rel'] = 'le';
  1616. }
  1617. $s['version'] = $dep['max'];
  1618. $s['optional'] = $optional;
  1619. if ($dtype != 'php') {
  1620. $s['name'] = $dep['name'];
  1621. }
  1622. }
  1623. }
  1624. $ret[] = $s;
  1625. }
  1626. }
  1627. }
  1628. if (count($ret)) {
  1629. return $ret;
  1630. }
  1631. }
  1632. return false;
  1633. }
  1634. /**
  1635. * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
  1636. */
  1637. function getPackageType()
  1638. {
  1639. if (isset($this->_packageInfo['phprelease'])) {
  1640. return 'php';
  1641. }
  1642. if (isset($this->_packageInfo['extsrcrelease'])) {
  1643. return 'extsrc';
  1644. }
  1645. if (isset($this->_packageInfo['extbinrelease'])) {
  1646. return 'extbin';
  1647. }
  1648. if (isset($this->_packageInfo['zendextsrcrelease'])) {
  1649. return 'zendextsrc';
  1650. }
  1651. if (isset($this->_packageInfo['zendextbinrelease'])) {
  1652. return 'zendextbin';
  1653. }
  1654. if (isset($this->_packageInfo['bundle'])) {
  1655. return 'bundle';
  1656. }
  1657. return false;
  1658. }
  1659. /**
  1660. * @return array|false
  1661. */
  1662. function getReleases()
  1663. {
  1664. $type = $this->getPackageType();
  1665. if ($type != 'bundle') {
  1666. $type .= 'release';
  1667. }
  1668. if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
  1669. return $this->_packageInfo[$type];
  1670. }
  1671. return false;
  1672. }
  1673. /**
  1674. * @return array
  1675. */
  1676. function getChangelog()
  1677. {
  1678. if (isset($this->_packageInfo['changelog'])) {
  1679. return $this->_packageInfo['changelog'];
  1680. }
  1681. return false;
  1682. }
  1683. function hasDeps()
  1684. {
  1685. return isset($this->_packageInfo['dependencies']);
  1686. }
  1687. function getPackagexmlVersion()
  1688. {
  1689. if (isset($this->_packageInfo['zendextsrcrelease'])) {
  1690. return '2.1';
  1691. }
  1692. if (isset($this->_packageInfo['zendextbinrelease'])) {
  1693. return '2.1';
  1694. }
  1695. return '2.0';
  1696. }
  1697. /**
  1698. * @return array|false
  1699. */
  1700. function getSourcePackage()
  1701. {
  1702. if (isset($this->_packageInfo['extbinrelease']) ||
  1703. isset($this->_packageInfo['zendextbinrelease'])) {
  1704. return array('channel' => $this->_packageInfo['srcchannel'],
  1705. 'package' => $this->_packageInfo['srcpackage']);
  1706. }
  1707. return false;
  1708. }
  1709. function getBundledPackages()
  1710. {
  1711. if (isset($this->_packageInfo['bundle'])) {
  1712. return $this->_packageInfo['contents']['bundledpackage'];
  1713. }
  1714. return false;
  1715. }
  1716. function getLastModified()
  1717. {
  1718. if (isset($this->_packageInfo['_lastmodified'])) {
  1719. return $this->_packageInfo['_lastmodified'];
  1720. }
  1721. return false;
  1722. }
  1723. /**
  1724. * Get the contents of a file listed within the package.xml
  1725. * @param string
  1726. * @return string
  1727. */
  1728. function getFileContents($file)
  1729. {
  1730. if ($this->_archiveFile == $this->_packageFile) { // unpacked
  1731. $dir = dirname($this->_packageFile);
  1732. $file = $dir . DIRECTORY_SEPARATOR . $file;
  1733. $file = str_replace(array('/', '\\'),
  1734. array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
  1735. if (file_exists($file) && is_readable($file)) {
  1736. return implode('', file($file));
  1737. }
  1738. } else { // tgz
  1739. $tar = &new Archive_Tar($this->_archiveFile);
  1740. $tar->pushErrorHandling(PEAR_ERROR_RETURN);
  1741. if ($file != 'package.xml' && $file != 'package2.xml') {
  1742. $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
  1743. }
  1744. $file = $tar->extractInString($file);
  1745. $tar->popErrorHandling();
  1746. if (PEAR::isError($file)) {
  1747. return PEAR::raiseError("Cannot locate file '$file' in archive");
  1748. }
  1749. return $file;
  1750. }
  1751. }
  1752. function &getRW()
  1753. {
  1754. if (!class_exists('PEAR_PackageFile_v2_rw')) {
  1755. require_once 'PEAR/PackageFile/v2/rw.php';
  1756. }
  1757. $a = new PEAR_PackageFile_v2_rw;
  1758. foreach (get_object_vars($this) as $name => $unused) {
  1759. if (!isset($this->$name)) {
  1760. continue;
  1761. }
  1762. if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
  1763. $name == '_stack') {
  1764. $a->$name = &$this->$name;
  1765. } else {
  1766. $a->$name = $this->$name;
  1767. }
  1768. }
  1769. return $a;
  1770. }
  1771. function &getDefaultGenerator()
  1772. {
  1773. if (!class_exists('PEAR_PackageFile_Generator_v2')) {
  1774. require_once 'PEAR/PackageFile/Generator/v2.php';
  1775. }
  1776. $a = &new PEAR_PackageFile_Generator_v2($this);
  1777. return $a;
  1778. }
  1779. function analyzeSourceCode($file, $string = false)
  1780. {
  1781. if (!isset($this->_v2Validator) ||
  1782. !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
  1783. if (!class_exists('PEAR_PackageFile_v2_Validator')) {
  1784. require_once 'PEAR/PackageFile/v2/Validator.php';
  1785. }
  1786. $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
  1787. }
  1788. return $this->_v2Validator->analyzeSourceCode($file, $string);
  1789. }
  1790. function validate($state = PEAR_VALIDATE_NORMAL)
  1791. {
  1792. if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
  1793. return false;
  1794. }
  1795. if (!isset($this->_v2Validator) ||
  1796. !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
  1797. if (!class_exists('PEAR_PackageFile_v2_Validator')) {
  1798. require_once 'PEAR/PackageFile/v2/Validator.php';
  1799. }
  1800. $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
  1801. }
  1802. if (isset($this->_packageInfo['xsdversion'])) {
  1803. unset($this->_packageInfo['xsdversion']);
  1804. }
  1805. return $this->_v2Validator->validate($this, $state);
  1806. }
  1807. function getTasksNs()
  1808. {
  1809. if (!isset($this->_tasksNs)) {
  1810. if (isset($this->_packageInfo['attribs'])) {
  1811. foreach ($this->_packageInfo['attribs'] as $name => $value) {
  1812. if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
  1813. $this->_tasksNs = str_replace('xmlns:', '', $name);
  1814. break;
  1815. }
  1816. }
  1817. }
  1818. }
  1819. return $this->_tasksNs;
  1820. }
  1821. /**
  1822. * Determine whether a task name is a valid task. Custom tasks may be defined
  1823. * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
  1824. *
  1825. * Note that this method will auto-load the task class file and test for the existence
  1826. * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
  1827. * PEAR_Task_mycustom_task
  1828. * @param string
  1829. * @return boolean
  1830. */
  1831. function getTask($task)
  1832. {
  1833. $this->getTasksNs();
  1834. // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
  1835. $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
  1836. $taskfile = str_replace(' ', '/', ucwords($task));
  1837. $task = str_replace(array(' ', '/'), '_', ucwords($task));
  1838. if (class_exists("PEAR_Task_$task")) {
  1839. return "PEAR_Task_$task";
  1840. }
  1841. $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true);
  1842. if ($fp) {
  1843. fclose($fp);
  1844. require_once "PEAR/Task/$taskfile.php";
  1845. return "PEAR_Task_$task";
  1846. }
  1847. return false;
  1848. }
  1849. /**
  1850. * Key-friendly array_splice
  1851. * @param tagname to splice a value in before
  1852. * @param mixed the value to splice in
  1853. * @param string the new tag name
  1854. */
  1855. function _ksplice($array, $key, $value, $newkey)
  1856. {
  1857. $offset = array_search($key, array_keys($array));
  1858. $after = array_slice($array, $offset);
  1859. $before = array_slice($array, 0, $offset);
  1860. $before[$newkey] = $value;
  1861. return array_merge($before, $after);
  1862. }
  1863. /**
  1864. * @param array a list of possible keys, in the order they may occur
  1865. * @param mixed contents of the new package.xml tag
  1866. * @param string tag name
  1867. * @access private
  1868. */
  1869. function _insertBefore($array, $keys, $contents, $newkey)
  1870. {
  1871. foreach ($keys as $key) {
  1872. if (isset($array[$key])) {
  1873. return $array = $this->_ksplice($array, $key, $contents, $newkey);
  1874. }
  1875. }
  1876. $array[$newkey] = $contents;
  1877. return $array;
  1878. }
  1879. /**
  1880. * @param subsection of {@link $_packageInfo}
  1881. * @param array|string tag contents
  1882. * @param array format:
  1883. * <pre>
  1884. * array(
  1885. * tagname => array(list of tag names that follow this one),
  1886. * childtagname => array(list of child tag names that follow this one),
  1887. * )
  1888. * </pre>
  1889. *
  1890. * This allows construction of nested tags
  1891. * @access private
  1892. */
  1893. function _mergeTag($manip, $contents, $order)
  1894. {
  1895. if (count($order)) {
  1896. foreach ($order as $tag => $curorder) {
  1897. if (!isset($manip[$tag])) {
  1898. // ensure that the tag is set up
  1899. $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
  1900. }
  1901. if (count($order) > 1) {
  1902. $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
  1903. return $manip;
  1904. }
  1905. }
  1906. } else {
  1907. return $manip;
  1908. }
  1909. if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
  1910. $manip[$tag][] = $contents;
  1911. } else {
  1912. if (!count($manip[$tag])) {
  1913. $manip[$tag] = $contents;
  1914. } else {
  1915. $manip[$tag] = array($manip[$tag]);
  1916. $manip[$tag][] = $contents;
  1917. }
  1918. }
  1919. return $manip;
  1920. }
  1921. }
  1922. ?>