Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / package / PackageArchive.class.php
CommitLineData
11ade432
AE
1<?php
2namespace wcf\system\package;
3use wcf\data\package\Package;
4use wcf\system\database\util\PreparedStatementConditionBuilder;
1871b8b3 5use wcf\system\package\validation\PackageValidationException;
11ade432
AE
6use wcf\system\io\Tar;
7use wcf\system\WCF;
b4cbf821 8use wcf\util\DateUtil;
11ade432 9use wcf\util\FileUtil;
11ade432
AE
10use wcf\util\XML;
11
12/**
a17de04e 13 * Represents the archive of a package.
11ade432
AE
14 *
15 * @author Marcel Werk
ca4ba303 16 * @copyright 2001-2014 WoltLab GmbH
11ade432
AE
17 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
18 * @package com.woltlab.wcf
19 * @subpackage system.package
9f959ced 20 * @category Community Framework
11ade432
AE
21 */
22class PackageArchive {
23 /**
9f959ced
MS
24 * path to package archive
25 * @var string
11ade432
AE
26 */
27 protected $archive = null;
28
29 /**
30 * package object of an existing package
0ad90fc3 31 * @var \wcf\data\package\Package
11ade432
AE
32 */
33 protected $package = null;
34
35 /**
36 * tar archive object
0ad90fc3 37 * @var \wcf\system\io\Tar
11ade432
AE
38 */
39 protected $tar = null;
40
41 /**
42 * general package information
9f959ced 43 * @var array
11ade432
AE
44 */
45 protected $packageInfo = array();
46
47 /**
48 * author information
9f959ced 49 * @var array
11ade432
AE
50 */
51 protected $authorInfo = array();
52
53 /**
54 * list of requirements
9f959ced 55 * @var array
11ade432
AE
56 */
57 protected $requirements = array();
58
59 /**
60 * list of optional packages
9f959ced 61 * @var array
11ade432
AE
62 */
63 protected $optionals = array();
64
65 /**
66 * list of excluded packages
11ade432
AE
67 * @var array
68 */
69 protected $excludedPackages = array();
70
71 /**
72 * list of instructions
11ade432 73 * @var array<array>
d726f13d 74 */
11ade432
AE
75 protected $instructions = array(
76 'install' => array(),
77 'update' => array()
78 );
79
80 /**
81 * list of php requirements
11ade432
AE
82 * @var array<array>
83 */
84 protected $phpRequirements = array();
85
86 /**
87 * default name of the package.xml file
9f959ced 88 * @var string
11ade432
AE
89 */
90 const INFO_FILE = 'package.xml';
91
92 /**
93 * Creates a new PackageArchive object.
94 *
95 * @param string $archive
96 * @param Package $package
97 */
98 public function __construct($archive, Package $package = null) {
9f959ced 99 $this->archive = $archive; // be careful: this is a string within this class,
11ade432
AE
100 // but an object in the packageStartInstallForm.class!
101 $this->package = $package;
102 }
103
ec45d5e8
AE
104 /**
105 * Sets associated package object.
106 *
0ad90fc3 107 * @param \wcf\data\package\Package $package
ec45d5e8
AE
108 */
109 public function setPackage(Package $package) {
110 $this->package = $package;
111 }
112
11ade432
AE
113 /**
114 * Returns the name of the package archive.
115 *
116 * @return string
117 */
118 public function getArchive() {
119 return $this->archive;
120 }
121
122 /**
123 * Returns the object of the package archive.
124 *
0ad90fc3 125 * @return \wcf\system\io\Tar
11ade432
AE
126 */
127 public function getTar() {
128 return $this->tar;
129 }
130
131 /**
132 * Opens the package archive and reads package information.
133 */
134 public function openArchive() {
135 // check whether archive exists and is a TAR archive
136 if (!file_exists($this->archive)) {
1871b8b3 137 throw new PackageValidationException(PackageValidationException::FILE_NOT_FOUND, array('archive' => $this->archive));
11ade432 138 }
9f959ced 139
11ade432
AE
140 // open archive and read package information
141 $this->tar = new Tar($this->archive);
142 $this->readPackageInfo();
143 }
144
145 /**
146 * Extracts information about this package (parses package.xml).
147 */
148 protected function readPackageInfo() {
149 // search package.xml in package archive
150 // throw error message if not found
151 if ($this->tar->getIndexByFilename(self::INFO_FILE) === false) {
1871b8b3 152 throw new PackageValidationException(PackageValidationException::MISSING_PACKAGE_XML, array('archive' => $this->archive));
11ade432
AE
153 }
154
ca6bca55 155 // extract package.xml, parse XML
11ade432
AE
156 // and compile an array with XML::getElementTree()
157 $xml = new XML();
158 try {
159 $xml->loadXML(self::INFO_FILE, $this->tar->extractToString(self::INFO_FILE));
160 }
6286572b 161 catch (\Exception $e) { // bugfix to avoid file caching problems
11ade432
AE
162 $xml->loadXML(self::INFO_FILE, $this->tar->extractToString(self::INFO_FILE));
163 }
164
165 // parse xml
166 $xpath = $xml->xpath();
167 $package = $xpath->query('/ns:package')->item(0);
168
169 // package name
170 $packageName = $package->getAttribute('name');
171 if (!Package::isValidPackageName($packageName)) {
172 // package name is not a valid package identifier
1871b8b3 173 throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_NAME, array('packageName' => $packageName));
11ade432
AE
174 }
175
176 $this->packageInfo['name'] = $packageName;
177
178 // get package information
179 $packageInformation = $xpath->query('./ns:packageinformation', $package)->item(0);
180 $elements = $xpath->query('child::*', $packageInformation);
181 foreach ($elements as $element) {
182 switch ($element->tagName) {
183 case 'packagename':
184 case 'packagedescription':
185 case 'readme':
186 case 'license':
187 if (!isset($this->packageInfo[$element->tagName])) $this->packageInfo[$element->tagName] = array();
188
189 $languageCode = 'default';
190 if ($element->hasAttribute('language')) {
191 $languageCode = $element->getAttribute('language');
192 }
193
194 // fix case-sensitive names
195 $name = $element->tagName;
196 if ($name == 'packagename') $name = 'packageName';
197 else if ($name == 'packagedescription') $name = 'packageDescription';
198
199 $this->packageInfo[$name][$languageCode] = $element->nodeValue;
200 break;
201
aac1247e
MS
202 case 'isapplication':
203 $this->packageInfo['isApplication'] = intval($element->nodeValue);
11ade432
AE
204 break;
205
11ade432
AE
206 case 'packageurl':
207 $this->packageInfo['packageURL'] = $element->nodeValue;
208 break;
209
210 case 'version':
b4cbf821 211 if (!Package::isValidVersion($element->nodeValue)) {
1871b8b3 212 throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_VERSION, array('packageVersion' => $element->nodeValue));
11ade432
AE
213 }
214
215 $this->packageInfo['version'] = $element->nodeValue;
216 break;
217
218 case 'date':
b4cbf821 219 DateUtil::validateDate($element->nodeValue);
11ade432 220
7611351c 221 $this->packageInfo['date'] = @strtotime($element->nodeValue);
11ade432
AE
222 break;
223 }
224 }
225
226 // get author information
227 $authorInformation = $xpath->query('./ns:authorinformation', $package)->item(0);
228 $elements = $xpath->query('child::*', $authorInformation);
229 foreach ($elements as $element) {
230 $tagName = ($element->tagName == 'authorurl') ? 'authorURL' : $element->tagName;
231 $this->authorInfo[$tagName] = $element->nodeValue;
232 }
233
234 // get required packages
235 $elements = $xpath->query('child::ns:requiredpackages/ns:requiredpackage', $package);
236 foreach ($elements as $element) {
237 if (!Package::isValidPackageName($element->nodeValue)) {
1871b8b3 238 throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_NAME, array('packageName' => $element->nodeValue));
11ade432
AE
239 }
240
241 // read attributes
242 $data = array('name' => $element->nodeValue);
243 $attributes = $xpath->query('attribute::*', $element);
244 foreach ($attributes as $attribute) {
245 $data[$attribute->name] = $attribute->value;
246 }
549376a4 247
11ade432
AE
248 $this->requirements[$element->nodeValue] = $data;
249 }
9f959ced
MS
250
251 // get optional packages
11ade432
AE
252 $elements = $xpath->query('child::ns:optionalpackages/ns:optionalpackage', $package);
253 foreach ($elements as $element) {
254 if (!Package::isValidPackageName($element->nodeValue)) {
1871b8b3 255 throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_NAME, array('packageName' => $element->nodeValue));
11ade432
AE
256 }
257
258 // read attributes
259 $data = array('name' => $element->nodeValue);
260 $attributes = $xpath->query('attribute::*', $element);
261 foreach ($attributes as $attribute) {
262 $data[$attribute->name] = $attribute->value;
263 }
549376a4 264
11ade432
AE
265 $this->optionals[] = $data;
266 }
267
268 // get excluded packages
269 $elements = $xpath->query('child::ns:excludedpackages/ns:excludedpackage', $package);
270 foreach ($elements as $element) {
271 if (!Package::isValidPackageName($element->nodeValue)) {
1871b8b3 272 throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_NAME, array('packageName' => $element->nodeValue));
11ade432
AE
273 }
274
275 // read attributes
276 $data = array('name' => $element->nodeValue);
277 $attributes = $xpath->query('attribute::*', $element);
278 foreach ($attributes as $attribute) {
279 $data[$attribute->name] = $attribute->value;
280 }
281
282 $this->excludedPackages[] = $data;
283 }
284
285 // get instructions
286 $elements = $xpath->query('./ns:instructions', $package);
287 foreach ($elements as $element) {
288 $instructionData = array();
289 $instructions = $xpath->query('./ns:instruction', $element);
290 foreach ($instructions as $instruction) {
291 $data = array();
292 $attributes = $xpath->query('attribute::*', $instruction);
293 foreach ($attributes as $attribute) {
294 $data[$attribute->name] = $attribute->value;
295 }
296
297 $instructionData[] = array(
298 'attributes' => $data,
299 'pip' => $instruction->getAttribute('type'),
300 'value' => $instruction->nodeValue
301 );
302 }
303
304 $fromVersion = $element->getAttribute('fromversion');
305 $type = $element->getAttribute('type');
306
307 if ($type == 'install') {
308 $this->instructions['install'] = $instructionData;
309 }
310 else {
ca38121b 311 $this->instructions['update'][$fromVersion] = $instructionData;
11ade432
AE
312 }
313 }
314
315 // get php requirements
430f87ec 316 /*$requirements = $xpath->query('./ns:phprequirements', $package);
11ade432
AE
317 foreach ($requirements as $requirement) {
318 $elements = $xpath->query('child::*', $requirement);
319 foreach ($elements as $element) {
320 switch ($element->tagName) {
321 case 'version':
322 $this->phpRequirements['version'] = $element->nodeValue;
9f959ced 323 break;
11ade432
AE
324
325 case 'setting':
326 $this->phpRequirements['settings'][$element->getAttribute('name')] = $element->nodeValue;
9f959ced 327 break;
11ade432
AE
328
329 case 'extension':
330 $this->phpRequirements['extensions'][] = $element->nodeValue;
9f959ced 331 break;
11ade432
AE
332
333 case 'function':
334 $this->phpRequirements['functions'][] = $element->nodeValue;
9f959ced 335 break;
11ade432
AE
336
337 case 'class':
338 $this->phpRequirements['classes'][] = $element->nodeValue;
9f959ced 339 break;
11ade432
AE
340 }
341 }
430f87ec 342 }*/
11ade432
AE
343
344 // add com.woltlab.wcf to package requirements
345 if (!isset($this->requirements['com.woltlab.wcf']) && $this->packageInfo['name'] != 'com.woltlab.wcf') {
346 $this->requirements['com.woltlab.wcf'] = array('name' => 'com.woltlab.wcf');
347 }
348
349 if ($this->package != null) {
49ec1439 350 $this->filterUpdateInstructions();
11ade432
AE
351 }
352
353 // set default values
aac1247e 354 if (!isset($this->packageInfo['isApplication'])) $this->packageInfo['isApplication'] = 0;
11ade432 355 if (!isset($this->packageInfo['packageURL'])) $this->packageInfo['packageURL'] = '';
11ade432
AE
356 }
357
49ec1439
AE
358 /**
359 * Filters update instructions.
360 */
361 protected function filterUpdateInstructions() {
362 $validFromVersion = null;
363 foreach ($this->instructions['update'] as $fromVersion => $update) {
364 if (Package::checkFromversion($this->package->packageVersion, $fromVersion)) {
365 $validFromVersion = $fromVersion;
366 break;
367 }
368 }
369
370 if ($validFromVersion === null) {
371 $this->instructions['update'] = array();
372 }
373 else {
374 $this->instructions['update'] = $this->instructions['update'][$validFromVersion];
375 }
376 }
377
11ade432
AE
378 /**
379 * Downloads the package archive.
380 *
381 * @return string path to the dowloaded file
382 */
383 public function downloadArchive() {
11ade432
AE
384 $prefix = 'package';
385
386 // file transfer via hypertext transfer protocol.
387 $this->archive = FileUtil::downloadFileFromHttp($this->archive, $prefix);
388
389 // unzip tar
390 $this->archive = self::unzipPackageArchive($this->archive);
391
392 return $this->archive;
393 }
394
395 /**
396 * Closes and deletes the tar archive of this package.
397 */
398 public function deleteArchive() {
399 if ($this->tar instanceof Tar) {
400 $this->tar->close();
401 }
402
403 @unlink($this->archive);
404 }
405
406 /**
28410a97 407 * Returns true if the package archive supports a new installation.
11ade432
AE
408 *
409 * @return boolean
410 */
411 public function isValidInstall() {
412 return !empty($this->instructions['install']);
413 }
414
415 /**
416 * Checks if the new package is compatible with
417 * the package that is about to be updated.
9f959ced 418 *
49ec1439
AE
419 * @param \wcf\data\package\Package $package
420 * @return boolean isValidUpdate
11ade432 421 */
49ec1439
AE
422 public function isValidUpdate(Package $package = null) {
423 if ($this->package === null && $package !== null) {
424 $this->setPackage($package);
425
426 // re-evaluate update data
427 $this->filterUpdateInstructions();
428 }
429
11ade432
AE
430 // Check name of the installed package against the name of the update. Both must be identical.
431 if ($this->packageInfo['name'] != $this->package->package) {
432 return false;
433 }
434
435 // Check if the version number of the installed package is lower than the version number to which
436 // it's about to be updated.
437 if (Package::compareVersion($this->packageInfo['version'], $this->package->packageVersion) != 1) {
438 return false;
439 }
49ec1439 440
11ade432
AE
441 // Check if the package provides an instructions block for the update from the installed package version
442 if (empty($this->instructions['update'])) {
443 return false;
444 }
49ec1439 445
11ade432
AE
446 return true;
447 }
448
449 /**
8c442546
AE
450 * Checks if the current package is already installed, as it is not
451 * possible to install non-applications multiple times within the
452 * same environment.
11ade432 453 *
8c442546 454 * @return boolean
11ade432
AE
455 */
456 public function isAlreadyInstalled() {
f1c1fc65
AE
457 $sql = "SELECT COUNT(*) AS count
458 FROM wcf".WCF_N."_package
39bea7dd 459 WHERE package = ?";
11ade432
AE
460 $statement = WCF::getDB()->prepareStatement($sql);
461 $statement->execute(array($this->packageInfo['name']));
f1c1fc65 462 $row = $statement->fetchArray();
11ade432 463
f1c1fc65 464 return ($row['count'] > 0) ? true : false;
11ade432
AE
465 }
466
549376a4
MS
467 /**
468 * Returns true if the package is an application and has an unique abbrevation.
469 *
470 * @return boolean
471 */
472 public function hasUniqueAbbreviation() {
473 if (!$this->packageInfo['isApplication']) {
474 return true;
475 }
476
477 $sql = "SELECT COUNT(*)
478 FROM wcf".WCF_N."_package
479 WHERE isApplication = ?
480 AND package LIKE ?";
481 $statement = WCF::getDB()->prepareStatement($sql);
482 $statement->execute(array(
483 1,
4e82ea84 484 '%.'.Package::getAbbreviation($this->packageInfo['name'])
549376a4
MS
485 ));
486
487 return $statement->fetchColumn();
488 }
489
11ade432
AE
490 /**
491 * Returns information about the author of this package archive.
492 *
39bea7dd 493 * @param string $name name of the requested information
11ade432
AE
494 * @return string
495 */
496 public function getAuthorInfo($name) {
497 if (isset($this->authorInfo[$name])) return $this->authorInfo[$name];
498 return null;
499 }
500
501 /**
502 * Returns information about this package.
503 *
39bea7dd 504 * @param string $name name of the requested information
11ade432
AE
505 * @return mixed
506 */
507 public function getPackageInfo($name) {
508 if (isset($this->packageInfo[$name])) return $this->packageInfo[$name];
509 return null;
510 }
511
a2ad7897
MS
512 /**
513 * Returns a localized information about this package.
514 *
515 * @param string $name
4000bdbb 516 * @return string
a2ad7897
MS
517 */
518 public function getLocalizedPackageInfo($name) {
519 if (isset($this->packageInfo[$name][WCF::getLanguage()->getFixedLanguageCode()])) {
520 return $this->packageInfo[$name][WCF::getLanguage()->getFixedLanguageCode()];
521 }
522 else if (isset($this->packageInfo[$name]['default'])) {
523 return $this->packageInfo[$name]['default'];
524 }
525
4000bdbb
MW
526 if (!empty($this->packageInfo[$name])) {
527 return reset($this->packageInfo[$name]);
528 }
529
530 return '';
a2ad7897
MS
531 }
532
11ade432
AE
533 /**
534 * Returns a list of all requirements of this package.
535 *
536 * @return array
537 */
538 public function getRequirements() {
539 return $this->requirements;
540 }
541
542 /**
543 * Returns a list of all delivered optional packages of this package.
544 *
545 * @return array
546 */
547 public function getOptionals() {
548 return $this->optionals;
549 }
550
551 /**
552 * Returns a list of excluded packages.
553 *
554 * @return array
555 */
556 public function getExcludedPackages() {
557 return $this->excludedPackages;
558 }
559
560 /**
561 * Returns the package installation instructions.
562 *
563 * @return array
564 */
565 public function getInstallInstructions() {
566 return $this->instructions['install'];
567 }
568
569 /**
570 * Returns the package update instructions.
571 *
572 * @return array
573 */
574 public function getUpdateInstructions() {
575 return $this->instructions['update'];
576 }
577
578 /**
579 * Checks which package requirements do already exist in right version.
580 * Returns a list with all existing requirements.
581 *
582 * @return array
583 */
584 public function getAllExistingRequirements() {
585 $existingRequirements = array();
586 $existingPackages = array();
587 if ($this->package !== null) {
07c78f25 588 $sql = "SELECT package.*
11ade432
AE
589 FROM wcf".WCF_N."_package_requirement requirement
590 LEFT JOIN wcf".WCF_N."_package package
591 ON (package.packageID = requirement.requirement)
592 WHERE requirement.packageID = ?";
593 $statement = WCF::getDB()->prepareStatement($sql);
594 $statement->execute(array($this->package->packageID));
595 while ($row = $statement->fetchArray()) {
596 $existingRequirements[$row['package']] = $row;
597 }
598 }
9f959ced 599
11ade432
AE
600 // build sql
601 $packageNames = array();
602 $requirements = $this->getRequirements();
603 foreach ($requirements as $requirement) {
604 if (isset($existingRequirements[$requirement['name']])) {
605 $existingPackages[$requirement['name']] = array();
606 $existingPackages[$requirement['name']][$existingRequirements[$requirement['name']]['packageID']] = $existingRequirements[$requirement['name']];
607 }
608 else {
609 $packageNames[] = $requirement['name'];
610 }
611 }
9f959ced 612
11ade432
AE
613 // check whether the required packages do already exist
614 if (!empty($packageNames)) {
615 $conditions = new PreparedStatementConditionBuilder();
616 $conditions->add("package.package IN (?)", array($packageNames));
617
07c78f25 618 $sql = "SELECT package.*
11ade432
AE
619 FROM wcf".WCF_N."_package package
620 ".$conditions;
621 $statement = WCF::getDB()->prepareStatement($sql);
622 $statement->execute($conditions->getParameters());
623 while ($row = $statement->fetchArray()) {
624 // check required package version
625 if (isset($requirements[$row['package']]['minversion']) && Package::compareVersion($row['packageVersion'], $requirements[$row['package']]['minversion']) == -1) {
626 continue;
627 }
628
629 if (!isset($existingPackages[$row['package']])) {
630 $existingPackages[$row['package']] = array();
631 }
632
633 $existingPackages[$row['package']][$row['packageID']] = $row;
634 }
635 }
636
637 return $existingPackages;
638 }
639
640 /**
641 * Checks which package requirements do already exist in database.
642 * Returns a list with the existing requirements.
643 *
644 * @return array
645 */
646 public function getExistingRequirements() {
647 // build sql
648 $packageNames = array();
649 foreach ($this->requirements as $requirement) {
650 $packageNames[] = $requirement['name'];
651 }
9f959ced 652
11ade432
AE
653 // check whether the required packages do already exist
654 $existingPackages = array();
655 if (!empty($packageNames)) {
656 $conditions = new PreparedStatementConditionBuilder();
657 $conditions->add("package IN (?)", array($packageNames));
658
39bea7dd 659 $sql = "SELECT *
11ade432
AE
660 FROM wcf".WCF_N."_package
661 ".$conditions;
662 $statement = WCF::getDB()->prepareStatement($sql);
663 $statement->execute($conditions->getParameters());
664 while ($row = $statement->fetchArray()) {
665 if (!isset($existingPackages[$row['package']])) {
666 $existingPackages[$row['package']] = array();
667 }
668
669 $existingPackages[$row['package']][$row['packageVersion']] = $row;
670 }
671
672 // sort multiple packages by version number
673 foreach ($existingPackages as $packageName => $instances) {
674 uksort($instances, array('wcf\data\package\Package', 'compareVersion'));
675
676 // get package with highest version number (get last package)
677 $existingPackages[$packageName] = array_pop($instances);
678 }
679 }
680
681 return $existingPackages;
682 }
683
684 /**
685 * Returns a list of all open requirements of this package.
686 *
687 * @return array
688 */
689 public function getOpenRequirements() {
690 // get all existing requirements
691 $existingPackages = $this->getExistingRequirements();
692
693 // check for open requirements
694 $openRequirements = array();
695 foreach ($this->requirements as $requirement) {
696 if (isset($existingPackages[$requirement['name']])) {
697 // package does already exist
698 // maybe an update is necessary
0eaa9bba
MS
699 if (isset($requirement['minversion'])) {
700 if (Package::compareVersion($existingPackages[$requirement['name']]['packageVersion'], $requirement['minversion']) >= 0) {
701 // package does already exist in needed version
e3369fd2 702 // skip installation of requirement
0eaa9bba
MS
703 continue;
704 }
705 else {
706 $requirement['existingVersion'] = $existingPackages[$requirement['name']]['packageVersion'];
707 }
708 }
709 else {
11ade432
AE
710 continue;
711 }
712
713 $requirement['packageID'] = $existingPackages[$requirement['name']]['packageID'];
714 $requirement['action'] = 'update';
715 }
716 else {
717 // package does not exist
718 // new installation is necessary
719 $requirement['packageID'] = 0;
720 $requirement['action'] = 'install';
721 }
722
723 $openRequirements[$requirement['name']] = $requirement;
724 }
725
726 return $openRequirements;
727 }
728
729 /**
a17de04e
MS
730 * Extracts the requested file in the package archive to the temp folder
731 * and returns the path to the extracted file.
11ade432
AE
732 *
733 * @param string $filename
734 * @param string $tempPrefix
a17de04e 735 * @return string
11ade432
AE
736 */
737 public function extractTar($filename, $tempPrefix = 'package_') {
738 // search the requested tar archive in our package archive.
739 // throw error message if not found.
740 if (($fileIndex = $this->tar->getIndexByFilename($filename)) === false) {
1871b8b3
AE
741 throw new PackageValidationException(PackageValidationException::FILE_NOT_FOUND, array(
742 'archive' => $this->archive,
743 'targetArchive' => $filename
744 ));
11ade432
AE
745 }
746
747 // requested tar archive was found
748 $fileInfo = $this->tar->getFileInfo($fileIndex);
749 $filename = FileUtil::getTemporaryFilename($tempPrefix, preg_replace('!^.*?(\.(?:tar\.gz|tgz|tar))$!i', '\\1', $fileInfo['filename']));
750 $this->tar->extract($fileIndex, $filename);
751
752 return $filename;
753 }
754
755 /**
a17de04e 756 * Unzips compressed package archives and returns the temporary file name.
11ade432 757 *
39bea7dd 758 * @param string $archive filename
a17de04e 759 * @return string
11ade432
AE
760 */
761 public static function unzipPackageArchive($archive) {
762 if (!FileUtil::isURL($archive)) {
763 $tar = new Tar($archive);
764 $tar->close();
765 if ($tar->isZipped()) {
766 $tmpName = FileUtil::getTemporaryFilename('package_');
767 if (FileUtil::uncompressFile($archive, $tmpName)) {
768 return $tmpName;
769 }
770 }
771 }
772
773 return $archive;
774 }
775
776 /**
1f2ce8fd 777 * Returns a list of packages which exclude this package.
11ade432 778 *
0ad90fc3 779 * @return array<\wcf\data\package\Package>
11ade432
AE
780 */
781 public function getConflictedExcludingPackages() {
782 $conflictedPackages = array();
783 $sql = "SELECT package.*, package_exclusion.*
784 FROM wcf".WCF_N."_package_exclusion package_exclusion
785 LEFT JOIN wcf".WCF_N."_package package
1f2ce8fd 786 ON (package.packageID = package_exclusion.packageID)
37001c4e 787 WHERE excludedPackage = ?";
11ade432
AE
788 $statement = WCF::getDB()->prepareStatement($sql);
789 $statement->execute(array($this->packageInfo['name']));
790 while ($row = $statement->fetchArray()) {
791 if (!empty($row['excludedPackageVersion'])) {
792 if (Package::compareVersion($this->packageInfo['version'], $row['excludedPackageVersion'], '<')) {
793 continue;
794 }
795 }
796
1f2ce8fd 797 $conflictedPackages[$row['packageID']] = new Package(null, $row);
11ade432
AE
798 }
799
800 return $conflictedPackages;
801 }
802
803 /**
1f2ce8fd 804 * Returns a list of packages which are excluded by this package.
11ade432 805 *
0ad90fc3 806 * @return array<\wcf\data\package\Package>
11ade432
AE
807 */
808 public function getConflictedExcludedPackages() {
809 $conflictedPackages = array();
15fa2802 810 if (!empty($this->excludedPackages)) {
89b98373
MS
811 $excludedPackages = array();
812 foreach ($this->excludedPackages as $excludedPackageData) {
1f2ce8fd 813 $excludedPackages[$excludedPackageData['name']] = $excludedPackageData['version'];
89b98373
MS
814 }
815
11ade432 816 $conditions = new PreparedStatementConditionBuilder();
1f2ce8fd 817 $conditions->add("package IN (?)", array(array_keys($excludedPackages)));
11ade432
AE
818
819 $sql = "SELECT *
820 FROM wcf".WCF_N."_package
821 ".$conditions;
822 $statement = WCF::getDB()->prepareStatement($sql);
823 $statement->execute($conditions->getParameters());
824 while ($row = $statement->fetchArray()) {
1f2ce8fd
MS
825 if (!empty($excludedPackages[$row['package']])) {
826 if (Package::compareVersion($row['packageVersion'], $excludedPackages[$row['package']], '<')) {
11ade432
AE
827 continue;
828 }
1f2ce8fd 829 $row['excludedPackageVersion'] = $excludedPackages[$row['package']];
11ade432
AE
830 }
831
1f2ce8fd 832 $conflictedPackages[$row['packageID']] = new Package(null, $row);
11ade432
AE
833 }
834 }
835
836 return $conflictedPackages;
837 }
838
839 /**
840 * Returns a list of instructions for installation or update.
841 *
842 * @param string $type
843 * @return array
844 */
845 public function getInstructions($type) {
846 if (isset($this->instructions[$type])) {
847 return $this->instructions[$type];
848 }
849
850 return null;
851 }
852
853 /**
854 * Returns a list of php requirements for current package.
855 *
856 * @return array<array>
857 */
858 public function getPhpRequirements() {
859 return $this->phpRequirements;
860 }
861}