Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

Locator::run()   C

Complexity

Conditions 8
Paths 15

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
eloc 16
nc 15
nop 1
dl 0
loc 32
rs 5.3846
c 0
b 0
f 0
ccs 0
cts 17
cp 0
crap 72
1
<?php
2
3
namespace Elgg\Upgrade;
4
5
use Elgg\Database\PrivateSettingsTable;
6
use Elgg\Database\Plugins;
7
use Elgg\Logger;
8
use ElggUpgrade;
9
10
/**
11
 * Locates and registers both core and plugin upgrades
12
 *
13
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
14
 *
15
 * @since 3.0.0
16
 *
17
 * @access private
18
 */
19
class Locator {
20
21
	/**
22
	 * @var Plugins $plugins
23
	 */
24
	private $plugins;
25
26
	/**
27
	 * @var Logger $logger
28
	 */
29
	private $logger;
30
31
	/**
32
	 * @var PrivateSettingsTable $privateSettings
33
	 */
34
	private $privateSettings;
35
36
	/**
37
	 * Constructor
38
	 *
39
	 * @param Plugins              $plugins          Plugins
40
	 * @param Logger               $logger           Logger
41
	 * @param PrivateSettingsTable $private_settings PrivateSettingsTable
42
	 */
43 7
	public function __construct(Plugins $plugins, Logger $logger, PrivateSettingsTable $private_settings) {
44 7
		$this->plugins = $plugins;
45 7
		$this->logger = $logger;
46 7
		$this->privateSettings = $private_settings;
47 7
	}
48
49
	/**
50
	 * Looks for upgrades and saves them as ElggUpgrade entities
51
	 *
52
	 * @param string[] $core_upgrades Class names of core upgrades
53
	 *
54
	 * @return int[]|boolean $pending_upgrades Are there pending upgrades
55
	 */
56
	public function run(array $core_upgrades) {
57
		$pending_upgrades = [];
58
59
		// Check for core upgrades
60
		foreach ($core_upgrades as $class) {
61
			$upgrade = $this->getUpgrade($class, 'core');
0 ignored issues
show
Bug introduced by Juho Jaakkola
Are you sure the assignment to $upgrade is correct as $this->getUpgrade($class, 'core') targeting Elgg\Upgrade\Locator::getUpgrade() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
62
			if ($upgrade) {
63
				$pending_upgrades[] = $upgrade->guid;
64
			}
65
		}
66
67
		$plugins = $this->plugins->find('active');
68
69
		// Check for plugin upgrades
70
		foreach ($plugins as $plugin) {
71
			$batches = $plugin->getStaticConfig('upgrades');
72
73
			if (empty($batches)) {
74
				continue;
75
			}
76
77
			$plugin_id = $plugin->getID();
78
79
			foreach ($batches as $class) {
80
				$upgrade = $this->getUpgrade($class, $plugin_id);
0 ignored issues
show
Bug introduced by Ismayil Khayredinov
Are you sure the assignment to $upgrade is correct as $this->getUpgrade($class, $plugin_id) targeting Elgg\Upgrade\Locator::getUpgrade() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
81
				if ($upgrade) {
82
					$pending_upgrades[] = $upgrade->guid;
83
				}
84
			}
85
		}
86
87
		return $pending_upgrades ? : false;
88
	}
89
90
	/**
91
	 * Gets intance of an ElggUpgrade based on the given class and id
92
	 *
93
	 * @param string $class Class implementing Elgg\Upgrade\Batch
94
	 * @param string $id    Either plugin_id or "core"
95
	 * @return ElggUpgrade|null
96
	 */
97 2
	public function getUpgrade($class, $id) {
98 2
		$batch = $this->getBatch($class);
99
100 2
		if (!$batch) {
101 1
			return;
102
		}
103
104 1
		$version = $batch->getVersion();
105 1
		$upgrade_id = "{$id}:{$version}";
106
107
		// Database holds the information of which upgrades have been processed
108 1
		if ($this->upgradeExists($upgrade_id)) {
109
			$this->logger->info("Upgrade $id has already been processed");
110
			return;
111
		}
112
113
		// Create a new ElggUpgrade to represent the upgrade in the database
114 1
		$object = new ElggUpgrade();
115 1
		$object->setId($upgrade_id);
116 1
		$object->setClass($class);
117 1
		$object->title = "{$id}:upgrade:{$version}:title";
118 1
		$object->description = "{$id}:upgrade:{$version}:description";
119 1
		$object->offset = 0;
120
121
		try {
122 1
			$object->save();
123 1
			return $object;
124
		} catch (\UnexpectedValueException $ex) {
125
			$this->logger->error($ex->getMessage());
126
		}
127
	}
128
129
	/**
130
	 * Validates class and returns an instance of batch
131
	 *
132
	 * @param string $class The fully qualified class name
133
	 * @return Batch|false if invalid upgrade
134
	 */
135 6
	public function getBatch($class) {
136 6
		if (!class_exists($class)) {
137
			$this->logger->error("Upgrade class $class was not found");
138
			return false;
139
		}
140
141 6
		$batch = new $class;
142 6
		if (!$batch instanceof Batch) {
143
			$this->logger->error("Upgrade class $class should implement " . Batch::class);
144
			return false;
145
		}
146
147
		// check version before shouldBeSkipped() so authors can get immediate feedback on an
148
		// invalid batch.
149 6
		$version = $batch->getVersion();
150
		
151
		// Version must be in format yyyymmddnn
152 6
		if (preg_match("/^[0-9]{10}$/", $version) == 0) {
153
			$this->logger->error("Upgrade $class returned an invalid version: $version");
154
			return false;
155
		}
156
157 6
		if ($batch->shouldBeSkipped()) {
158 1
			return false;
159
		}
160
161 5
		return $batch;
162
	}
163
164
	/**
165
	 * Check if there already is an ElggUpgrade for this upgrade
166
	 *
167
	 * @param string $upgrade_id Id in format <plugin_id>:<yyymmddnn>
168
	 * @return boolean
169
	 */
170 1
	public function upgradeExists($upgrade_id) {
171 1
		$upgrade = \Elgg\Database\Entities::find([
172 1
			'type' => 'object',
173 1
			'subtype' => 'elgg_upgrade',
174
			'private_setting_name_value_pairs' => [
175
				[
176 1
					'name' => 'id',
177 1
					'value' => (string) $upgrade_id,
178
				],
179
			],
180
		]);
181
182 1
		return !empty($upgrade);
183
	}
184
}
185