Completed
Push — master ( 24957e...d8c031 )
by Thomas
16:29 queued 07:32
created

Installer::isDownloaded()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Brice Maron <[email protected]>
6
 * @author Christian Weiske <[email protected]>
7
 * @author Christopher Schäpers <[email protected]>
8
 * @author Frank Karlitschek <[email protected]>
9
 * @author Georg Ehrke <[email protected]>
10
 * @author Jakob Sack <[email protected]>
11
 * @author Joas Schilling <[email protected]>
12
 * @author Jörn Friedrich Dreyer <[email protected]>
13
 * @author Kamil Domanski <[email protected]>
14
 * @author Lukas Reschke <[email protected]>
15
 * @author michag86 <[email protected]>
16
 * @author Morris Jobke <[email protected]>
17
 * @author Robin Appelman <[email protected]>
18
 * @author Roeland Jago Douma <[email protected]>
19
 * @author Thomas Müller <[email protected]>
20
 * @author Thomas Tanghus <[email protected]>
21
 *
22
 * @copyright Copyright (c) 2017, ownCloud GmbH
23
 * @license AGPL-3.0
24
 *
25
 * This code is free software: you can redistribute it and/or modify
26
 * it under the terms of the GNU Affero General Public License, version 3,
27
 * as published by the Free Software Foundation.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License, version 3,
35
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
36
 *
37
 */
38
39
namespace OC;
40
41
use Doctrine\DBAL\Exception\TableExistsException;
42
use OC\DB\MigrationService;
43
use OC_App;
44
use OC_DB;
45
use OC_Helper;
46
use OCP\App\AppAlreadyInstalledException;
47
48
/**
49
 * This class provides the functionality needed to install, update and remove plugins/apps
50
 */
51
class Installer {
52
53
	/**
54
	 *
55
	 * This function installs an app. All information needed are passed in the
56
	 * associative array $data.
57
	 * The following keys are required:
58
	 *   - source: string, can be "path" or "http"
59
	 *
60
	 * One of the following keys is required:
61
	 *   - path: path to the file containing the app
62
	 *   - href: link to the downloadable file containing the app
63
	 *
64
	 * The following keys are optional:
65
	 *   - pretend: boolean, if set true the system won't do anything
66
	 *   - noinstall: boolean, if true appinfo/install.php won't be loaded
67
	 *   - inactive: boolean, if set true the appconfig/app.sample.php won't be
68
	 *     renamed
69
	 *
70
	 * This function works as follows
71
	 *   -# fetching the file
72
	 *   -# unzipping it
73
	 *   -# check the code
74
	 *   -# installing the database at appinfo/database.xml
75
	 *   -# including appinfo/install.php
76
	 *   -# setting the installed version
77
	 *
78
	 * It is the task of oc_app_install to create the tables and do whatever is
79
	 * needed to get the app working.
80
	 *
81
	 * Installs an app
82
	 * @param array $data with all information
83
	 * @throws \Exception
84
	 * @return integer
85
	 */
86
	public static function installApp( $data = []) {
87
		$l = \OC::$server->getL10N('lib');
88
89
		list($extractDir, $path) = self::downloadApp($data);
90
91
		$info = self::checkAppsIntegrity($data, $extractDir, $path);
92
		$appId = OC_App::cleanAppId($info['id']);
93
		$appsFolder = OC_App::getInstallPath();
94
95
		if ($appsFolder === null || !is_writable($appsFolder)) {
96
			throw new \Exception('Apps folder is not writable');
97
		}
98
		$basedir = "$appsFolder/$appId";
99
		//check if the destination directory already exists
100 View Code Duplication
		if(is_dir($basedir)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
			OC_Helper::rmdirr($extractDir);
102
			if($data['source']=='http') {
103
				unlink($path);
104
			}
105
			throw new \Exception($l->t("App directory already exists"));
106
		}
107
108
		if(!empty($data['pretent'])) {
109
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by OC\Installer::installApp of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
110
		}
111
		OC_App::clearAppCache($info['id']);
112
113
		//copy the app to the correct place
114
		if(@!mkdir($basedir)) {
115
			OC_Helper::rmdirr($extractDir);
116
			if($data['source']=='http') {
117
				unlink($path);
118
			}
119
			throw new \Exception($l->t("Can't create app folder. Please fix permissions. %s", [$basedir]));
120
		}
121
122
		$extractDir .= '/' . $info['id'];
123 View Code Duplication
		if(!file_exists($extractDir)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
			OC_Helper::rmdirr($basedir);
125
			throw new \Exception($l->t("Archive does not contain a directory named %s", $info['id']));
126
		}
127
		OC_Helper::copyr($extractDir, $basedir);
128
129
		//remove temporary files
130
		OC_Helper::rmdirr($extractDir);
131
132
		//install the database
133
		if (isset($info['use-migrations']) && $info['use-migrations'] === 'true') {
134
			$ms = new \OC\DB\MigrationService($appId, \OC::$server->getDatabaseConnection());
135
			$ms->migrate();
136
		} else {
137
			if(is_file($basedir.'/appinfo/database.xml')) {
138
				if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) {
139
					OC_DB::createDbFromStructure($basedir . '/appinfo/database.xml');
140
				} else {
141
					OC_DB::updateDbFromStructure($basedir . '/appinfo/database.xml');
142
				}
143
			}
144
		}
145
146
		\OC_App::setupBackgroundJobs($info['background-jobs']);
147
148
		//run appinfo/install.php
149
		if((!isset($data['noinstall']) or $data['noinstall']==false)) {
150
			self::includeAppScript($basedir . '/appinfo/install.php');
151
		}
152
153
		$appData = OC_App::getAppInfo($appId);
154
		OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']);
155
156
		//set the installed version
157
		\OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id']));
158
		\OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no');
159
160
		//set remote/public handlers
161 View Code Duplication
		foreach($info['remote'] as $name=>$path) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
			\OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
163
		}
164 View Code Duplication
		foreach($info['public'] as $name=>$path) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
165
			\OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
166
		}
167
168
		OC_App::setAppTypes($info['id']);
169
170
		return $info['id'];
171
	}
172
173
	/**
174
	 * @brief checks whether or not an app is installed
175
	 * @param string $app app
176
	 * @returns bool
177
	 *
178
	 * Checks whether or not an app is installed, i.e. registered in apps table.
179
	 */
180
	public static function isInstalled( $app ) {
181
		return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null);
182
	}
183
184
	/**
185
	 * @brief Update an application
186
	 * @param array $info
187
	 * @param bool $isShipped
188
	 * @throws \Exception
189
	 * @return bool
190
	 *
191
	 * This function could work like described below, but currently it disables and then
192
	 * enables the app again. This does result in an updated app.
193
	 *
194
	 *
195
	 * This function installs an app. All information needed are passed in the
196
	 * associative array $info.
197
	 * The following keys are required:
198
	 *   - source: string, can be "path" or "http"
199
	 *
200
	 * One of the following keys is required:
201
	 *   - path: path to the file containing the app
202
	 *   - href: link to the downloadable file containing the app
203
	 *
204
	 * The following keys are optional:
205
	 *   - pretend: boolean, if set true the system won't do anything
206
	 *   - noupgrade: boolean, if true appinfo/upgrade.php won't be loaded
207
	 *
208
	 * This function works as follows
209
	 *   -# fetching the file
210
	 *   -# removing the old files
211
	 *   -# unzipping new file
212
	 *   -# including appinfo/upgrade.php
213
	 *   -# setting the installed version
214
	 *
215
	 * upgrade.php can determine the current installed version of the app using
216
	 * "\OC::$server->getAppConfig()->getValue($appid, 'installed_version')"
217
	 */
218
	public static function updateApp($info= [], $isShipped=false) {
219
		list($extractDir, $path) = self::downloadApp($info);
220
		$info = self::checkAppsIntegrity($info, $extractDir, $path, $isShipped);
221
222
		$currentDir = OC_App::getAppPath($info['id']);
223
		$basedir  = OC_App::getInstallPath();
224
		$basedir .= '/';
225
		$basedir .= $info['id'];
226
227
		if($currentDir !== false && is_writable($currentDir)) {
228
			$basedir = $currentDir;
229
		}
230
		if(is_dir("$basedir/.git")) {
231
			throw new AppAlreadyInstalledException("App <{$info['id']}> is a git clone - it will not be updated.");
232
		}
233
		if(is_dir($basedir)) {
234
			OC_Helper::rmdirr($basedir);
235
		}
236
237
		$appInExtractDir = $extractDir;
238
		if (substr($extractDir, -1) !== '/') {
239
			$appInExtractDir .= '/';
240
		}
241
242
		$appInExtractDir .= $info['id'];
243
		OC_Helper::copyr($appInExtractDir, $basedir);
244
		OC_Helper::rmdirr($extractDir);
245
246
		return OC_App::updateApp($info['id']);
247
	}
248
249
	/**
250
	 * @param array $data
251
	 * @return array
252
	 * @throws \Exception
253
	 */
254
	public static function downloadApp($data = []) {
255
		$l = \OC::$server->getL10N('lib');
256
257
		if(!isset($data['source'])) {
258
			throw new \Exception($l->t("No source specified when installing app"));
259
		}
260
261
		//download the file if necessary
262
		if($data['source']=='http') {
263
			$pathInfo = pathinfo($data['href']);
264
			$extension = isset($pathInfo['extension']) ? '.' . $pathInfo['extension'] : '';
265
			$path = \OC::$server->getTempManager()->getTemporaryFile($extension);
266
			if(!isset($data['href'])) {
267
				throw new \Exception($l->t("No href specified when installing app from http"));
268
			}
269
			$client = \OC::$server->getHTTPClientService()->newClient();
270
			$client->get($data['href'], ['save_to' => $path]);
271
		} else {
272
			if(!isset($data['path'])) {
273
				throw new \Exception($l->t("No path specified when installing app from local file"));
274
			}
275
			$path=$data['path'];
276
		}
277
278
		//detect the archive type
279
		$mime = \OC::$server->getMimeTypeDetector()->detect($path);
280
		if ($mime !=='application/zip' && $mime !== 'application/x-gzip' && $mime !== 'application/x-bzip2') {
281
			throw new \Exception($l->t("Archives of type %s are not supported", [$mime]));
282
		}
283
284
		//extract the archive in a temporary folder
285
		$extractDir = \OC::$server->getTempManager()->getTemporaryFolder();
286
		OC_Helper::rmdirr($extractDir);
287
		mkdir($extractDir);
288
		if($archive=\OC\Archive\Archive::open($path)) {
289
			$archive->extract($extractDir);
290 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
			OC_Helper::rmdirr($extractDir);
292
			if($data['source']=='http') {
293
				unlink($path);
294
			}
295
			throw new \Exception($l->t("Failed to open archive when installing app"));
296
		}
297
298
		return [
299
			$extractDir,
300
			$path
301
		];
302
	}
303
304
	/**
305
	 * check an app's integrity
306
	 * @param array $data
307
	 * @param string $extractDir
308
	 * @param string $path
309
	 * @param bool $isShipped
310
	 * @return array
311
	 * @throws \Exception
312
	 */
313
	public static function checkAppsIntegrity($data, $extractDir, $path, $isShipped = false) {
314
		$l = \OC::$server->getL10N('lib');
315
		//load the info.xml file of the app
316
		if(!is_file($extractDir.'/appinfo/info.xml')) {
317
			//try to find it in a subdir
318
			$dh=opendir($extractDir);
319
			if(is_resource($dh)) {
320
				while (($folder = readdir($dh)) !== false) {
321
					if($folder[0]!='.' and is_dir($extractDir.'/'.$folder)) {
322
						if(is_file($extractDir.'/'.$folder.'/appinfo/info.xml')) {
323
							$extractDir.='/'.$folder;
324
						}
325
					}
326
				}
327
			}
328
		}
329 View Code Duplication
		if(!is_file($extractDir.'/appinfo/info.xml')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
330
			OC_Helper::rmdirr($extractDir);
331
			if($data['source'] === 'http') {
332
				unlink($path);
333
			}
334
			throw new \Exception($l->t("App does not provide an info.xml file"));
335
		}
336
337
		$info = OC_App::getAppInfo($extractDir.'/appinfo/info.xml', true);
338
		if(!is_array($info)) {
339
			throw new \Exception($l->t('App cannot be installed because appinfo file cannot be read.'));
340
		}
341
342
		// We can't trust the parsed info.xml file as it may have been tampered
343
		// with by an attacker and thus we need to use the local data to check
344
		// whether the application needs to be signed.
345
		$appId = OC_App::cleanAppId(isset($data['appdata']['id']) ? $data['appdata']['id'] : '');
346
		$appBelongingToId = OC_App::getInternalAppIdByOcs($appId);
347 View Code Duplication
		if(is_string($appBelongingToId)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
348
			$previouslySigned = \OC::$server->getConfig()->getAppValue($appBelongingToId, 'signed', 'false');
349
		} else {
350
			$appBelongingToId = $info['id'];
351
			$previouslySigned = 'false';
352
		}
353
		if (file_exists($extractDir . '/appinfo/signature.json') || $previouslySigned === 'true') {
354
			\OC::$server->getConfig()->setAppValue($appBelongingToId, 'signed', 'true');
355
			$integrityResult = \OC::$server->getIntegrityCodeChecker()->verifyAppSignature(
356
					$appBelongingToId,
357
					$extractDir
358
			);
359
			if($integrityResult !== []) {
360
				$e = new \Exception(
361
						$l->t(
362
								'Signature could not get checked. Please contact the app developer and check your admin screen.'
363
						)
364
				);
365
				throw $e;
366
			}
367
		}
368
369
		// check if the app is compatible with this version of ownCloud
370
		if(!OC_App::isAppCompatible(\OCP\Util::getVersion(), $info)) {
371
			OC_Helper::rmdirr($extractDir);
372
			throw new \Exception($l->t("App can't be installed because it is not compatible with this version of ownCloud"));
373
		}
374
375
		// check if shipped tag is set which is only allowed for apps that are shipped with ownCloud
376
		if(!$isShipped && isset($info['shipped']) && ($info['shipped']=='true')) {
377
			OC_Helper::rmdirr($extractDir);
378
			throw new \Exception($l->t("App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps"));
379
		}
380
381
		// check if the ocs version is the same as the version in info.xml/version
382
		$version = trim($info['version']);
383
384
		if(isset($data['appdata']['version']) && $version<>trim($data['appdata']['version'])) {
385
			OC_Helper::rmdirr($extractDir);
386
			throw new \Exception($l->t("App can't be installed because the version in info.xml is not the same as the version reported from the app store"));
387
		}
388
389
		return $info;
390
	}
391
392
	/**
393
	 * Check if app is already downloaded
394
	 * @param string $name name of the application to remove
395
	 * @return boolean
396
	 *
397
	 * The function will check if the app is already downloaded in the apps repository
398
	 */
399
	public static function isDownloaded( $name ) {
400
		foreach(\OC::$APPSROOTS as $dir) {
401
			$dirToTest  = $dir['path'];
402
			$dirToTest .= '/';
403
			$dirToTest .= $name;
404
			$dirToTest .= '/';
405
406
			if (is_dir($dirToTest)) {
407
				return true;
408
			}
409
		}
410
411
		return false;
412
	}
413
414
	/**
415
	 * Removes an app
416
	 * @param string $appId name of the application to remove
417
	 * @return boolean
418
	 * @throws AppAlreadyInstalledException
419
	 *
420
	 *
421
	 * This function works as follows
422
	 *   -# call uninstall repair steps
423
	 *   -# removing the files
424
	 *
425
	 * The function will not delete preferences, tables and the configuration,
426
	 * this has to be done by the function oc_app_uninstall().
427
	 */
428
	public static function removeApp($appId) {
429
430
		if(Installer::isDownloaded( $appId )) {
431
			$appDir = OC_App::getAppPath($appId);
432
			if ($appDir === false) {
433
				return false;
434
			}
435
			if(is_dir("$appDir/.git")) {
436
				throw new AppAlreadyInstalledException("App <$appId> is a git clone - it will not be deleted.");
437
			}
438
439
			OC_Helper::rmdirr($appDir);
440
441
			return true;
442
		}
443
		\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
444
445
		return false;
446
	}
447
448
	protected static function getShippedApps() {
449
		$shippedApps = [];
450
		foreach(\OC::$APPSROOTS as $app_dir) {
451
			if($dir = opendir( $app_dir['path'] )) {
452
				$nodes = scandir($app_dir['path']);
453
				foreach($nodes as $filename) {
454
					if( substr( $filename, 0, 1 ) != '.' and is_dir($app_dir['path']."/$filename") ) {
455
						if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) {
456
							if(!Installer::isInstalled($filename)) {
457
								$info=OC_App::getAppInfo($filename);
458
								$enabled = isset($info['default_enable']);
459
								if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps()))
460
									&& \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') {
461
									$shippedApps[] = $filename;
462
								}
463
							}
464
						}
465
					}
466
				}
467
				closedir( $dir );
468
			}
469
		}
470
471
472
		// Fix the order - make files first
473
		$shippedApps = array_diff($shippedApps,['files', 'dav']);
474
		array_unshift($shippedApps, 'dav');
475
		array_unshift($shippedApps, 'files');
476
		return $shippedApps;
477
	}
478
479
	/**
480
	 * Installs shipped apps
481
	 *
482
	 * This function installs all apps found in the 'apps' directory that should be enabled by default;
483
	 * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
484
	 *                         working ownCloud at the end instead of an aborted update.
485
	 * @return array Array of error messages (appid => Exception)
486
	 */
487
	public static function installShippedApps($softErrors = false) {
488
		$errors = [];
489
		$appsToInstall = Installer::getShippedApps();
490
491
		foreach($appsToInstall as $appToInstall) {
492
			if(!Installer::isInstalled($appToInstall)) {
493
				if ($softErrors) {
494
					try {
495
						Installer::installShippedApp($appToInstall);
496
					} catch (TableExistsException $e) {
497
						\OC::$server->getLogger()->logException($e, ['app' => __CLASS__]);
498
						$errors[$appToInstall] = $e;
499
						continue;
500
					}
501
				} else {
502
					Installer::installShippedApp($appToInstall);
503
				}
504
				\OC::$server->getConfig()->setAppValue($appToInstall, 'enabled', 'yes');
505
			}
506
		}
507
508
		return $errors;
509
510
	}
511
512
	/**
513
	 * install an app already placed in the app folder
514
	 * @param string $app id of the app to install
515
	 * @return integer|false
516
	 */
517
	public static function installShippedApp($app) {
518
519
		\OC::$server->getLogger()->info('Attempting to install shipped app: '.$app);
520
521
		$info = OC_App::getAppInfo($app);
522
		if (is_null($info)) {
523
			return false;
524
		}
525
526
		//install the database
527
		$appPath = OC_App::getAppPath($app);
528
		if (isset($info['use-migrations']) && $info['use-migrations'] === 'true') {
529
			\OC::$server->getLogger()->debug('Running app database migrations');
530
			$ms = new MigrationService($app, \OC::$server->getDatabaseConnection());
531
			$ms->migrate();
532
		} else {
533
			if(is_file($appPath.'/appinfo/database.xml')) {
534
				\OC::$server->getLogger()->debug('Create app database from schema file');
535
				OC_DB::createDbFromStructure($appPath . '/appinfo/database.xml');
536
			}
537
		}
538
539
		//run appinfo/install.php
540
		\OC_App::registerAutoloading($app, $appPath);
0 ignored issues
show
Security Bug introduced by
It seems like $appPath defined by \OC_App::getAppPath($app) on line 527 can also be of type false; however, OC_App::registerAutoloading() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
541
542
		\OC::$server->getLogger()->debug('Running app install script');
543
		self::includeAppScript("$appPath/appinfo/install.php");
544
545
		\OC_App::setupBackgroundJobs($info['background-jobs']);
546
547
		\OC::$server->getLogger()->debug('Running app install repair steps');
548
		OC_App::executeRepairSteps($app, $info['repair-steps']['install']);
549
550
		$config = \OC::$server->getConfig();
551
552
		$config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app));
553
		if (array_key_exists('ocsid', $info)) {
554
			$config->setAppValue($app, 'ocsid', $info['ocsid']);
555
		}
556
557
		//set remote/public handlers
558
		foreach($info['remote'] as $name=>$path) {
559
			$config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
560
		}
561
		foreach($info['public'] as $name=>$path) {
562
			$config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
563
		}
564
565
		OC_App::setAppTypes($info['id']);
566
567
		return $info['id'];
568
	}
569
570
	/**
571
	 * @param $script
572
	 */
573
	private static function includeAppScript($script) {
574
		if ( file_exists($script) ){
575
			include $script;
576
		}
577
	}
578
}
579