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