a4eb9cb68ee7e9bf2d4807b2228575dba2927a5d
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Package / Package.php
1 <?php
2 namespace TYPO3\CMS\Core\Package;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * A Package representing the details of an extension and/or a composer package
21 * Adapted from FLOW for TYPO3 CMS
22 */
23 class Package implements PackageInterface
24 {
25 /**
26 * @var array
27 */
28 protected $extensionManagerConfiguration = array();
29
30 /**
31 * @var array
32 */
33 protected $classAliases;
34
35 /**
36 * If this package is part of factory default, it will be activated
37 * during first installation.
38 *
39 * @var bool
40 */
41 protected $partOfFactoryDefault = false;
42
43 /**
44 * If this package is part of minimal usable system, it will be
45 * activated if PackageStates is created from scratch.
46 *
47 * @var bool
48 */
49 protected $partOfMinimalUsableSystem = false;
50
51 /**
52 * Unique key of this package.
53 * @var string
54 */
55 protected $packageKey;
56
57 /**
58 * Full path to this package's main directory
59 * @var string
60 */
61 protected $packagePath;
62
63 /**
64 * If this package is protected and therefore cannot be deactivated or deleted
65 * @var bool
66 */
67 protected $protected = false;
68
69 /**
70 * @var \stdClass
71 */
72 protected $composerManifest;
73
74 /**
75 * Meta information about this package
76 * @var MetaData
77 */
78 protected $packageMetaData;
79
80 /**
81 * @var PackageManager
82 */
83 protected $packageManager;
84
85 /**
86 * Constructor
87 *
88 * @param PackageManager $packageManager the package manager which knows this package
89 * @param string $packageKey Key of this package
90 * @param string $packagePath Absolute path to the location of the package's composer manifest
91 * @throws Exception\InvalidPackageKeyException if an invalid package key was passed
92 * @throws Exception\InvalidPackagePathException if an invalid package path was passed
93 * @throws Exception\InvalidPackageManifestException if no composer manifest file could be found
94 */
95 public function __construct(PackageManager $packageManager, $packageKey, $packagePath)
96 {
97 if (!$packageManager->isPackageKeyValid($packageKey)) {
98 throw new Exception\InvalidPackageKeyException('"' . $packageKey . '" is not a valid package key.', 1217959511);
99 }
100 if (!(@is_dir($packagePath) || (is_link($packagePath) && is_dir($packagePath)))) {
101 throw new Exception\InvalidPackagePathException(sprintf('Tried to instantiate a package object for package "%s" with a non-existing package path "%s". Either the package does not exist anymore, or the code creating this object contains an error.', $packageKey, $packagePath), 1166631890);
102 }
103 if (substr($packagePath, -1, 1) !== '/') {
104 throw new Exception\InvalidPackagePathException(sprintf('The package path "%s" provided for package "%s" has no trailing forward slash.', $packagePath, $packageKey), 1166633722);
105 }
106 $this->packageManager = $packageManager;
107 $this->packageKey = $packageKey;
108 $this->packagePath = $packagePath;
109 $this->composerManifest = $packageManager->getComposerManifest($this->packagePath);
110 $this->loadFlagsFromComposerManifest();
111 }
112
113 /**
114 * Loads package management related flags from the "extra:typo3/cms:Package" section
115 * of extensions composer.json files into local properties
116 *
117 * @return void
118 */
119 protected function loadFlagsFromComposerManifest()
120 {
121 $extraFlags = $this->getValueFromComposerManifest('extra');
122 if ($extraFlags !== null && isset($extraFlags->{"typo3/cms"}->{"Package"})) {
123 foreach ($extraFlags->{"typo3/cms"}->{"Package"} as $flagName => $flagValue) {
124 if (property_exists($this, $flagName)) {
125 $this->{$flagName} = $flagValue;
126 }
127 }
128 }
129 }
130
131 /**
132 * @return bool
133 */
134 public function isPartOfFactoryDefault()
135 {
136 return $this->partOfFactoryDefault;
137 }
138
139 /**
140 * @return bool
141 */
142 public function isPartOfMinimalUsableSystem()
143 {
144 return $this->partOfMinimalUsableSystem;
145 }
146
147 /**
148 * Returns the package key of this package.
149 *
150 * @return string
151 * @api
152 */
153 public function getPackageKey()
154 {
155 return $this->packageKey;
156 }
157
158 /**
159 * Tells if this package is protected and therefore cannot be deactivated or deleted
160 *
161 * @return bool
162 * @api
163 */
164 public function isProtected()
165 {
166 return $this->protected;
167 }
168
169 /**
170 * Sets the protection flag of the package
171 *
172 * @param bool $protected TRUE if the package should be protected, otherwise FALSE
173 * @return void
174 * @api
175 */
176 public function setProtected($protected)
177 {
178 $this->protected = (bool)$protected;
179 }
180
181 /**
182 * Returns the full path to this package's main directory
183 *
184 * @return string Path to this package's main directory
185 * @api
186 */
187 public function getPackagePath()
188 {
189 return $this->packagePath;
190 }
191
192 /**
193 * Returns the package meta data object of this package.
194 *
195 * @return MetaData
196 */
197 public function getPackageMetaData()
198 {
199 if ($this->packageMetaData === null) {
200 $this->packageMetaData = new MetaData($this->getPackageKey());
201 $this->packageMetaData->setDescription($this->getValueFromComposerManifest('description'));
202 $this->packageMetaData->setVersion($this->getValueFromComposerManifest('version'));
203 $requirements = $this->getValueFromComposerManifest('require');
204 if ($requirements !== null) {
205 foreach ($requirements as $requirement => $version) {
206 $packageKey = $this->packageManager->getPackageKeyFromComposerName($requirement);
207 // dynamically migrate 'cms' dependency to 'core' dependency
208 // see also \TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility::convertDependenciesToObjects
209 if ($packageKey === 'cms') {
210 GeneralUtility::deprecationLog('Extension "' . $this->packageKey . '" defines a dependency on ext:cms, which has been removed. Please remove the dependency.');
211 $packageKey = 'core';
212 }
213 $constraint = new MetaData\PackageConstraint(MetaData::CONSTRAINT_TYPE_DEPENDS, $packageKey);
214 $this->packageMetaData->addConstraint($constraint);
215 }
216 }
217 $suggestions = $this->getValueFromComposerManifest('suggest');
218 if ($suggestions !== null) {
219 foreach ($suggestions as $suggestion => $version) {
220 $packageKey = $this->packageManager->getPackageKeyFromComposerName($suggestion);
221 $constraint = new MetaData\PackageConstraint(MetaData::CONSTRAINT_TYPE_SUGGESTS, $packageKey);
222 $this->packageMetaData->addConstraint($constraint);
223 }
224 }
225 }
226 return $this->packageMetaData;
227 }
228
229 /**
230 * Returns an array of packages this package replaces
231 *
232 * @return array
233 */
234 public function getPackageReplacementKeys()
235 {
236 // The cast to array is required since the manifest returns data with type mixed
237 return (array)$this->getValueFromComposerManifest('replace') ?: array();
238 }
239
240 /**
241 * Returns contents of Composer manifest - or part there of if a key is given.
242 *
243 * @param string $key Optional. Only return the part of the manifest indexed by 'key'
244 * @return mixed|NULL
245 * @see json_decode for return values
246 */
247 public function getValueFromComposerManifest($key = null)
248 {
249 if ($key === null) {
250 return $this->composerManifest;
251 }
252
253 if (isset($this->composerManifest->{$key})) {
254 $value = $this->composerManifest->{$key};
255 } else {
256 $value = null;
257 }
258 return $value;
259 }
260
261 /**
262 * Added by TYPO3 CMS
263 *
264 * The package caching serializes package objects.
265 * The package manager instance may not be serialized
266 * as a fresh instance is created upon every request.
267 *
268 * This method will be removed once the package is
269 * released of the package manager dependency.
270 *
271 * @return array
272 */
273 public function __sleep()
274 {
275 $properties = get_class_vars(get_class($this));
276 unset($properties['packageManager']);
277 return array_keys($properties);
278 }
279
280 /**
281 * Added by TYPO3 CMS
282 *
283 * The package caching deserializes package objects.
284 * A fresh package manager instance has to be set
285 * during bootstrapping.
286 *
287 * This method will be removed once the package is
288 * released of the package manager dependency.
289 */
290 public function __wakeup()
291 {
292 if (isset($GLOBALS['TYPO3_currentPackageManager'])) {
293 $this->packageManager = $GLOBALS['TYPO3_currentPackageManager'];
294 }
295 }
296 }