Resolve language item-related PIP GUI todos
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / package / validation / PackageValidationManager.class.php
1 <?php
2 namespace wcf\system\package\validation;
3 use wcf\data\package\installation\plugin\PackageInstallationPluginList;
4 use wcf\data\package\Package;
5 use wcf\system\package\PackageArchive;
6 use wcf\system\SingletonFactory;
7
8 /**
9 * Manages recursive validation of package archives.
10 *
11 * @author Alexander Ebert
12 * @copyright 2001-2018 WoltLab GmbH
13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @package WoltLabSuite\Core\System\Package\Validation
15 */
16 class PackageValidationManager extends SingletonFactory {
17 /**
18 * list of known package installation plugins
19 * @var string[]
20 */
21 protected $packageInstallationPlugins = [];
22
23 /**
24 * package validation archive object
25 * @var PackageValidationArchive
26 */
27 protected $packageValidationArchive = null;
28
29 /**
30 * virtual package list containing package => packageVersion
31 * @var string[]
32 */
33 protected $virtualPackageList = [];
34
35 /**
36 * validation will only check if the primary package looks like it can be installed or updated
37 * @var integer
38 */
39 const VALIDATION_WEAK = 0;
40
41 /**
42 * validation will recursively check dependencies
43 * @var integer
44 */
45 const VALIDATION_RECURSIVE = 1;
46
47 /**
48 * validation will use the previously gathered exclusions and check them
49 * @var integer
50 */
51 const VALIDATION_EXCLUSION = 2;
52
53 /**
54 * @inheritDoc
55 */
56 protected function init() {
57 $pipList = new PackageInstallationPluginList();
58 $pipList->readObjects();
59 foreach ($pipList as $pip) {
60 $this->packageInstallationPlugins[$pip->pluginName] = $pip->className;
61 }
62 }
63
64 /**
65 * Validates given archive for existence and ability to be installed/updated. If you set the
66 * second parameter $deepInspection to "false", the system will only check if the archive
67 * looks fine, this is useful for a rough check during upload when a more detailed check will
68 * be performed afterwards.
69 *
70 * @param string $archive
71 * @param boolean $deepInspection
72 * @return boolean
73 */
74 public function validate($archive, $deepInspection) {
75 $this->virtualPackageList = [];
76 $this->packageValidationArchive = new PackageValidationArchive($archive);
77
78 if ($deepInspection) {
79 if (!$this->packageValidationArchive->validate(self::VALIDATION_RECURSIVE)) {
80 return false;
81 }
82
83 return $this->packageValidationArchive->validate(self::VALIDATION_EXCLUSION);
84 }
85
86 return $this->packageValidationArchive->validate(self::VALIDATION_WEAK);
87 }
88
89 /**
90 * Returns package validation archive object.
91 *
92 * @return PackageValidationArchive
93 */
94 public function getPackageValidationArchive() {
95 return $this->packageValidationArchive;
96 }
97
98 /**
99 * Adds a virtual package with the corresponding version, if the package is already known,
100 * the higher version number will be stored.
101 *
102 * @param string $package
103 * @param string $packageVersion
104 * @return boolean
105 */
106 public function addVirtualPackage($package, $packageVersion) {
107 if (isset($this->virtualPackageList[$package])) {
108 if (Package::compareVersion($packageVersion, $this->virtualPackageList[$package], '<')) {
109 return false;
110 }
111 }
112
113 $this->virtualPackageList[$package] = $packageVersion;
114
115 return true;
116 }
117
118 /**
119 * Returns the version number of a virtual package or null if it doesn't exist.
120 *
121 * @param string $package
122 * @return string
123 */
124 public function getVirtualPackage($package) {
125 if (isset($this->virtualPackageList[$package])) {
126 return $this->virtualPackageList[$package];
127 }
128
129 return null;
130 }
131
132 /**
133 * Returns the iterable package archive list.
134 *
135 * @return \RecursiveIteratorIterator
136 */
137 public function getPackageValidationArchiveList() {
138 $packageValidationArchive = new PackageValidationArchive('');
139 $packageValidationArchive->setChildren([$this->packageValidationArchive]);
140
141 return new \RecursiveIteratorIterator($packageValidationArchive, \RecursiveIteratorIterator::SELF_FIRST);
142 }
143
144 /**
145 * Recursively traverses the package validation archives and returns the first exception message.
146 *
147 * @return string
148 */
149 public function getExceptionMessage() {
150 foreach ($this->getPackageValidationArchiveList() as $packageArchive) {
151 if ($packageArchive->getExceptionMessage()) {
152 return $packageArchive->getExceptionMessage();
153 }
154 }
155
156 return '';
157 }
158
159 /**
160 * Recursively traverses the package validation archives and returns the first exception.
161 *
162 * @return \Exception
163 */
164 public function getException() {
165 foreach ($this->getPackageValidationArchiveList() as $packageArchive) {
166 if ($packageArchive->getException() !== null) {
167 return $packageArchive->getException();
168 }
169 }
170
171 return null;
172 }
173
174 /**
175 * Validates an instruction against the corresponding package installation plugin.
176 *
177 * Please be aware that unknown PIPs will silently ignored and cause no error.
178 *
179 * @param PackageArchive $archive
180 * @param string $pip
181 * @param string $instruction
182 * @return boolean
183 */
184 public function validatePackageInstallationPluginInstruction(PackageArchive $archive, $pip, $instruction) {
185 if (isset($this->packageInstallationPlugins[$pip])) {
186 return call_user_func([$this->packageInstallationPlugins[$pip], 'isValid'], $archive, $instruction);
187 }
188
189 return true;
190 }
191
192 /**
193 * Returns the default filename for the given pip name. If no default filename
194 * exists `null` is returned.
195 *
196 * @param string $pip
197 * @return string|null
198 * @since 3.1
199 */
200 public function getDefaultFilenameForPackageInstallationPlugin($pip) {
201 if (isset($this->packageInstallationPlugins[$pip])) {
202 return call_user_func([$this->packageInstallationPlugins[$pip], 'getDefaultFilename']);
203 }
204
205 return null;
206 }
207 }