2 namespace wcf\data\package
;
3 use wcf\data\DatabaseObject
;
4 use wcf\system\io\File
;
5 use wcf\system\package\PackageInstallationDispatcher
;
10 * Represents a package.
12 * @author Alexander Ebert
13 * @copyright 2001-2014 WoltLab GmbH
14 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
15 * @package com.woltlab.wcf
16 * @subpackage data.package
17 * @category Community Framework
19 class Package
extends DatabaseObject
{
21 * list of packages that this package requires
22 * @var array<\wcf\data\package\Package>
24 protected $dependencies = null;
27 * list of packages that require this package
28 * @var array<\wcf\data\package\Package>
30 protected $dependentPackages = null;
33 * installation directory
39 * list of packages that were given as required packages during installation
40 * @var array<\wcf\data\package\Package>
42 protected $requiredPackages = null;
45 * @see \wcf\data\DatabaseObject::$databaseTableName
47 protected static $databaseTableName = 'package';
50 * @see \wcf\data\DatabaseObject::$databaseTableIndexName
52 protected static $databaseTableIndexName = 'packageID';
55 * list of ids of packages which are required by another package
58 protected static $requiredPackageIDs = null;
61 * package requirements
64 protected static $requirements = null;
67 * Returns true if this package is required by other packages.
71 public function isRequired() {
72 self
::loadRequirements();
74 return in_array($this->packageID
, self
::$requiredPackageIDs);
78 * Returns true if package is a plugin.
82 public function isPlugin() {
83 if ($this->isApplication
) {
91 * Returns the name of this package.
95 public function getName() {
96 return WCF
::getLanguage()->get($this->packageName
);
100 * @see \wcf\data\package\Package::getName()
102 public function __toString() {
103 return $this->getName();
107 * Returns the abbreviation of the package name.
109 * @param string $package
112 public static function getAbbreviation($package) {
113 $array = explode('.', $package);
114 return array_pop($array);
118 * Returns the list of packages which are required by this package. The
119 * returned packages are the packages given in the <requiredpackages> tag
120 * in the package.xml of this package.
122 * @return array<\wcf\data\package\Package>
124 public function getRequiredPackages() {
125 if ($this->requiredPackages
=== null) {
126 self
::loadRequirements();
128 $this->requiredPackages
= array();
129 if (isset(self
::$requirements[$this->packageID
])) {
130 foreach (self
::$requirements[$this->packageID
] as $packageID) {
131 $this->requiredPackages
[$packageID] = PackageCache
::getInstance()->getPackage($packageID);
136 return $this->requiredPackages
;
140 * Returns true if current user can uninstall this package.
144 public function canUninstall() {
145 if (!WCF
::getSession()->getPermission('admin.system.package.canUninstallPackage')) {
149 // disallow uninstallation of WCF
150 if ($this->package
== 'com.woltlab.wcf') {
154 // check if package is required by another package
155 if (self
::isRequired($this->packageID
)) {
163 * Returns a list of packages dependent from current package.
165 * @return array<\wcf\data\package\Package>
167 public function getDependentPackages() {
168 if ($this->dependentPackages
=== null) {
169 self
::loadRequirements();
171 $this->dependentPackages
= array();
172 foreach (self
::$requirements as $packageID => $requiredPackageIDs) {
173 if (in_array($this->packageID
, $requiredPackageIDs)) {
174 $this->dependentPackages
[$packageID] = PackageCache
::getInstance()->getPackage($packageID);
179 return $this->dependentPackages
;
183 * Overwrites current package version.
185 * DO NOT call this method outside the package installation!
187 * @param string $packageVersion
189 public function setPackageVersion($packageVersion) {
190 $this->data
['packageVersion'] = $packageVersion;
194 * Loads package requirements.
196 protected static function loadRequirements() {
197 if (self
::$requirements === null) {
198 $sql = "SELECT packageID, requirement
199 FROM wcf".WCF_N
."_package_requirement";
200 $statement = WCF
::getDB()->prepareStatement($sql);
201 $statement->execute();
203 self
::$requiredPackageIDs = array();
204 self
::$requirements = array();
205 while ($row = $statement->fetchArray()) {
206 if (!isset(self
::$requirements[$row['packageID']])) {
207 self
::$requirements[$row['packageID']] = array();
210 self
::$requirements[$row['packageID']][] = $row['requirement'];
212 if (!in_array($row['requirement'], self
::$requiredPackageIDs)) {
213 self
::$requiredPackageIDs[] = $row['requirement'];
220 * Returns true if package identified by $package is already installed.
222 * @param string $package
225 public static function isAlreadyInstalled($package) {
226 $sql = "SELECT COUNT(*) AS count
227 FROM wcf".WCF_N
."_package
229 $statement = WCF
::getDB()->prepareStatement($sql);
230 $statement->execute(array($package));
231 $row = $statement->fetchArray();
233 return ($row['count'] ?
true : false);
237 * Checks if a package name is valid.
239 * A valid package name begins with at least one alphanumeric character
240 * or an underscore, followed by a dot, followed by at least one alphanumeric
241 * character or an underscore and the same again, possibly repeatedly.
245 * Reminder: The package name being examined here contains the 'name' attribute
246 * of the 'package' tag noted in the 'packages.xml' file delivered inside
247 * the respective package.
249 * @param string $packageName
250 * @return boolean isValid
252 public static function isValidPackageName($packageName) {
253 return preg_match('%^[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$%', $packageName);
257 * Returns true if package version is valid.
259 * Examples of valid package versions:
264 * @param string $version
267 public static function isValidVersion($version) {
268 return preg_match('~^([0-9]+)\.([0-9]+)\.([0-9]+)(\ (a|alpha|b|beta|d|dev|rc|pl)\ ([0-9]+))?$~is', $version);
272 * Checks the version number of the installed package against the "fromversion"
273 * number of the update.
275 * The "fromversion" number may contain wildcards (asterisks) which means
276 * that the update covers the whole range of release numbers where the asterisk
277 * wildcards digits from 0 to 9.
278 * For example, if "fromversion" is "1.1.*" and this package updates to
279 * version 1.2.0, all releases from 1.1.0 to 1.1.9 may be updated using
282 * @param string $currentVersion
283 * @param string $fromVersion
286 public static function checkFromversion($currentVersion, $fromversion) {
287 if (mb_strpos($fromversion, '*') !== false) {
288 // from version with wildcard
289 // use regular expression
290 $fromversion = str_replace('\*', '.*', preg_quote($fromversion, '!'));
291 if (preg_match('!^'.$fromversion.'$!i', $currentVersion)) {
296 if (self
::compareVersion($currentVersion, $fromversion, '=')) {
305 * Compares two version number strings.
307 * @param string $version1
308 * @param string $version2
309 * @param string $operator
310 * @return boolean result
311 * @see http://www.php.net/manual/en/function.version-compare.php
313 public static function compareVersion($version1, $version2, $operator = null) {
314 $version1 = self
::formatVersionForCompare($version1);
315 $version2 = self
::formatVersionForCompare($version2);
316 if ($operator === null) return version_compare($version1, $version2);
317 else return version_compare($version1, $version2, $operator);
321 * Formats a package version string for comparing.
323 * @param string $version
324 * @return string formatted version
325 * @see http://www.php.net/manual/en/function.version-compare.php
327 private static function formatVersionForCompare($version) {
329 $version = str_replace(' ', '', $version);
331 // correct special version strings
332 $version = str_ireplace('dev', 'dev', $version);
333 $version = str_ireplace('alpha', 'alpha', $version);
334 $version = str_ireplace('beta', 'beta', $version);
335 $version = str_ireplace('RC', 'RC', $version);
336 $version = str_ireplace('pl', 'pl', $version);
342 * Writes the config.inc.php for an application.
344 * @param integer $packageID
346 public static function writeConfigFile($packageID) {
347 $package = new Package($packageID);
348 $packageDir = FileUtil
::addTrailingSlash(FileUtil
::getRealPath(WCF_DIR
.$package->packageDir
));
349 $file = new File($packageDir.PackageInstallationDispatcher
::CONFIG_FILE
);
350 $file->write("<?php\n");
351 $prefix = strtoupper(self
::getAbbreviation($package->package
));
353 $file->write("// ".$package->package
." (packageID ".$package->packageID
.")\n");
354 $file->write("if (!defined('".$prefix."_DIR')) define('".$prefix."_DIR', dirname(__FILE__).'/');\n");
355 $file->write("if (!defined('RELATIVE_".$prefix."_DIR')) define('RELATIVE_".$prefix."_DIR', '');\n");
358 // write general information
359 $file->write("// general info\n");
360 $file->write("if (!defined('RELATIVE_WCF_DIR')) define('RELATIVE_WCF_DIR', RELATIVE_".$prefix."_DIR.'".FileUtil
::getRelativePath($packageDir, WCF_DIR
)."');\n");
361 $file->write("if (!defined('PACKAGE_ID')) define('PACKAGE_ID', ".$packageID.");\n");
362 $file->write("if (!defined('PACKAGE_NAME')) define('PACKAGE_NAME', '".str_replace("'", "\'", $package->getName())."');\n");
363 $file->write("if (!defined('PACKAGE_VERSION')) define('PACKAGE_VERSION', '".$package->packageVersion
."');\n");