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