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