GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

isDropKeyStatement()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
crap 2
1
<?php
2
/***************************************************************
3
*  Copyright notice
4
*
5
*  (c) 2016 AOE GmbH <[email protected]>
6
*  All rights reserved
7
*
8
*  This copyright notice MUST APPEAR in all copies of the script!
9
***************************************************************/
10
11
use \TYPO3\CMS\Core\Utility\GeneralUtility;
12
13
/**
14
 * Controller that handles database actions of the t3deploy process inside TYPO3.
15
 *
16
 * @package t3deploy
17
 * @author Oliver Hader <[email protected]>
18
 *
19
 */
20
class tx_t3deploy_databaseController {
21
	/*
22
	 * List of all possible update types:
23
	 *	+ add, change, drop, create_table, change_table, drop_table, clear_table
24
	 * List of all sensible update types:
25
	 *	+ add, change, create_table, change_table
26
	 */
27
	const UpdateTypes_List = 'add,change,create_table,change_table';
0 ignored issues
show
Coding Style introduced by
Constant UpdateTypes_List should be defined in uppercase
Loading history...
28
	const RemoveTypes_list = 'drop,drop_table,clear_table';
0 ignored issues
show
Coding Style introduced by
Constant RemoveTypes_list should be defined in uppercase
Loading history...
29
30
	/**
31
	 * @var \TYPO3\CMS\Install\Service\SqlSchemaMigrationService
32
	 */
33
	protected $schemaMigrationService;
34
35
	/**
36
	 * @var \TYPO3\CMS\Install\Service\SqlExpectedSchemaService
37
	 */
38
	protected $expectedSchemaService;
39
40
	/**
41
	 * @var \TYPO3\CMS\Core\Category\CategoryRegistry
42
	 */
43
	protected $categoryRegistry;
44
45
	/**
46
	 * @var array
47
	 */
48
	protected $consideredTypes;
49
50
	/**
51
	 * Creates this object.
52
	 */
53 5
	public function __construct() {
54
		/** @var TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */
55 5
		$objectManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
56
57 5
		$this->schemaMigrationService = $objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlSchemaMigrationService');
58 5
		$this->expectedSchemaService = $objectManager->get('TYPO3\\CMS\\Install\\Service\\SqlExpectedSchemaService');
59 5
		$this->categoryRegistry = $objectManager->get('TYPO3\\CMS\\Core\\Category\\CategoryRegistry');
60
61 5
		$this->setConsideredTypes($this->getUpdateTypes());
62 5
	}
63
64
	/**
65
	 * Sets the types considered to be executed (updates and/or removal).
66
	 *
67
	 * @param array $consideredTypes
68
	 * @return void
69
	 * @see updateStructureAction()
70
	 */
71 5
	public function setConsideredTypes(array $consideredTypes) {
72 5
		$this->consideredTypes = $consideredTypes;
73 5
	}
74
75
	/**
76
	 * Adds considered types.
77
	 *
78
	 * @param array $consideredTypes
79
	 * @return void
80
	 * @see updateStructureAction()
81
	 */
82 2
	public function addConsideredTypes(array $consideredTypes) {
83 2
		$this->consideredTypes = array_unique(
84 2
			array_merge($this->consideredTypes, $consideredTypes)
85
		);
86 2
	}
87
88
	/**
89
	 * Removes defined types from considered types.
90
	 *
91
	 * @param array $removals
92
	 * @return void
93
	 * @see updateStructureAction()
94
	 */
95
	public function removeConsideredTypes(array $removals) {
96
		$this->consideredTypes = array_diff($this->consideredTypes, $removals);
97
	}
98
99
	/**
100
	 * Updates the database structure.
101
	 *
102
	 * @param array $arguments Optional arguments passed to this action
103
	 * @return string
104
	 */
105 5
	public function updateStructureAction(array $arguments) {
106 5
		$isExecuteEnabled = (isset($arguments['--execute']) || isset($arguments['-e']));
107 5
		$isRemovalEnabled = (isset($arguments['--remove']) || isset($arguments['-r']));
108 5
		$isModifyKeysEnabled = isset($arguments['--drop-keys']);
109
110 5
		$result = $this->executeUpdateStructureUntilNoMoreChanges($arguments, $isModifyKeysEnabled);
111
112 5
		if(isset($arguments['--dump-file'])) {
113 1
			$dumpFileName = $arguments['--dump-file'][0];
114 1
			if(!file_exists(dirname($dumpFileName))) {
115
				throw new InvalidArgumentException(sprintf(
116
					'directory %s does not exist or is not readable', dirname($dumpFileName)
117
				));
118
			}
119 1
			if(file_exists($dumpFileName) && !is_writable($dumpFileName)) {
120
				throw new InvalidArgumentException(sprintf(
121
					'file %s is not writable', $dumpFileName
122
				));
123
			}
124 1
			file_put_contents($dumpFileName, $result);
125 1
			$result = sprintf("Output written to %s\n", $dumpFileName);
126
		}
127
128 5
		if ($isExecuteEnabled) {
129 2
			$result .= ($result ? PHP_EOL : '') . $this->executeUpdateStructureUntilNoMoreChanges($arguments, $isRemovalEnabled);
130
		}
131
132 5
		return $result;
133
	}
134
135
	/**
136
	 * call executeUpdateStructure until there are no more changes.
137
	 *
138
	 * The install tool sometimes relies on the user hitting the "update" button multiple times. This method
139
	 * encapsulates that behaviour.
140
	 *
141
	 * @see executeUpdateStructure()
142
	 * @param array $arguments
143
	 * @param bool $allowKeyModifications
144
	 * @return string
145
	 */
146 5
	protected function executeUpdateStructureUntilNoMoreChanges(array $arguments, $allowKeyModifications = FALSE) {
147 5
		$result = '';
148 5
		$iteration = 1;
149 5
		$loopResult = '';
150
		do {
151 5
			$previousLoopResult = $loopResult;
152 5
			$loopResult = $this->executeUpdateStructure($arguments, $allowKeyModifications);
153 5
			if($loopResult == $previousLoopResult) {
154 5
				break;
155
			}
156
157 3
			$result .= sprintf("\n# Iteration %d\n%s", $iteration++, $loopResult);
158
159 3
			if($iteration > 10) {
160
				$result .= "\nGiving up after 10 iterations.";
161
				break;
162
			}
163 3
		} while(!empty($loopResult));
164
165 5
		return $result;
166
	}
167
168
	/**
169
	 * Executes the database structure updates.
170
	 *
171
	 * @param array $arguments Optional arguments passed to this action
172
	 * @param boolean $allowKeyModifications Whether to allow key modifications
173
	 * @return string
174
	 */
175 5
	protected function executeUpdateStructure(array $arguments, $allowKeyModifications = FALSE) {
176 5
		$result = '';
177
178 5
		$isExecuteEnabled = (isset($arguments['--execute']) || isset($arguments['-e']));
179 5
		$isRemovalEnabled = (isset($arguments['--remove']) || isset($arguments['-r']));
180 5
		$isVerboseEnabled = (isset($arguments['--verbose']) || isset($arguments['-v']));
181 5
		$hasExcludes      = (isset($arguments['--excludes']) && is_array($arguments['--excludes']));
182
183 5
		$changes = $this->schemaMigrationService->getUpdateSuggestions(
184 5
			$this->getStructureDifferencesForUpdate($allowKeyModifications)
185
		);
186
187 5
		if ($isRemovalEnabled) {
188
				// Disable the delete prefix, thus tables and fields can be removed directly:
189 2
			$this->schemaMigrationService->setDeletedPrefixKey('');
190
191
				// Add types considered for removal:
192 2
			$this->addConsideredTypes($this->getRemoveTypes());
193
				// Merge update suggestions:
194 2
			$removals = $this->schemaMigrationService->getUpdateSuggestions(
195 2
				$this->getStructureDifferencesForRemoval($allowKeyModifications),
196 2
				'remove'
197
			);
198 2
			$changes = array_merge($changes, $removals);
199
		}
200
201 5
		if ($hasExcludes) {
202
			$excludes = explode(',', $arguments['--excludes'][0]);
203
			$this->removeConsideredTypes($excludes);
204
		}
205
206 5
		if ($isExecuteEnabled || $isVerboseEnabled) {
207 5
			$statements = array();
208
209
			// Concatenates all statements:
210 5
			foreach ($this->consideredTypes as $consideredType) {
211 5
				if (isset($changes[$consideredType]) && is_array($changes[$consideredType])) {
212 5
					$statements += $changes[$consideredType];
213
				}
214
			}
215
216 5
			$statements = $this->sortStatements($statements);
217
218 5
			if ($isExecuteEnabled) {
219 2
				foreach ($statements as $statement) {
220 2
					$GLOBALS['TYPO3_DB']->admin_query($statement);
221
				}
222
			}
223
224 5
			if ($isVerboseEnabled) {
225 3
				$result = implode(PHP_EOL, $statements);
226
			}
227
		}
228
229 5
		$this->checkChangesSyntax($result);
230
231 5
		return $result;
232
	}
233
234
	/**
235
	 * performs some basic checks on the database changes to identify most common errors
236
	 *
237
	 * @param string $changes the changes to check
238
	 * @throws Exception if the file seems to contain bad data
239
	 */
240 5
	protected function checkChangesSyntax($changes) {
241 5
		if (strlen($changes) < 10) return;
242 3
		$checked = substr(ltrim($changes), 0, 10);
243 3
		if ($checked != trim(strtoupper($checked))) {
244
			throw new Exception('Changes for schema_up seem to contain weird data, it starts with this:'.PHP_EOL.substr($changes, 0, 200).PHP_EOL.'=================================='.PHP_EOL.'If the file is ok, please add your conditions to file res/extensions/t3deploy/classes/class.tx_t3deploy_databaseController.php in t3deploy.');
245
		}
246 3
	}
247
248
	/**
249
	 * Removes key modifications that will cause errors.
250
	 *
251
	 * @param array $differences The differences to be cleaned up
252
	 * @return array The cleaned differences
253
	 */
254 5
	protected function removeKeyModifications(array $differences) {
255 5
		$differences = $this->unsetSubKey($differences, 'extra', 'keys', 'whole_table');
256 5
		$differences = $this->unsetSubKey($differences, 'diff', 'keys');
257
258 5
		return $differences;
259
	}
260
261
	/**
262
	 * Unsets a subkey in a given differences array.
263
	 *
264
	 * @param array $differences
265
	 * @param string $type e.g. extra or diff
266
	 * @param string $subKey e.g. keys or fields
267
	 * @param string $exception e.g. whole_table that stops the removal
268
	 * @return array
269
	 */
270 5
	protected function unsetSubKey(array $differences, $type, $subKey, $exception = '') {
271 5
		if (isset($differences[$type])) {
272 5
			foreach ($differences[$type] as $table => $information) {
273 5
				$isException = ($exception && isset($information[$exception]) && $information[$exception]);
274 5
				if (isset($information[$subKey]) && $isException === FALSE) {
275 5
					unset($differences[$type][$table][$subKey]);
276
				}
277
			}
278
		}
279
280 5
		return $differences;
281
	}
282
283
	/**
284
	 * Gets the differences in the database structure by comparing
285
	 * the current structure with the SQL definitions of all extensions
286
	 * and the TYPO3 core in t3lib/stddb/tables.sql.
287
	 *
288
	 * This method searches for fields/tables to be added/updated.
289
	 *
290
	 * @param boolean $allowKeyModifications Whether to allow key modifications
291
	 * @return array The database statements to update the structure
292
	 */
293 5
	protected function getStructureDifferencesForUpdate($allowKeyModifications = FALSE) {
294 5
		$differences = $this->schemaMigrationService->getDatabaseExtra(
295 5
			$this->getDefinedFieldDefinitions(),
296 5
			$this->schemaMigrationService->getFieldDefinitions_database()
297
		);
298
299 5
		if (!$allowKeyModifications) {
300 5
			$differences = $this->removeKeyModifications($differences);
301
		}
302
303 5
		return $differences;
304
	}
305
306
	/**
307
	 * Gets the differences in the database structure by comparing
308
	 * the current structure with the SQL definitions of all extensions
309
	 * and the TYPO3 core in t3lib/stddb/tables.sql.
310
	 *
311
	 * This method searches for fields/tables to be removed.
312
	 *
313
	 * @param boolean $allowKeyModifications Whether to allow key modifications
314
	 * @return array The database statements to update the structure
315
	 */
316 2
	protected function getStructureDifferencesForRemoval($allowKeyModifications = FALSE) {
317 2
		$differences = $this->schemaMigrationService->getDatabaseExtra(
318 2
			$this->schemaMigrationService->getFieldDefinitions_database(),
319 2
			$this->getDefinedFieldDefinitions()
320
		);
321
322 2
		if (!$allowKeyModifications) {
323 2
			$differences = $this->removeKeyModifications($differences);
324
		}
325
326 2
		return $differences;
327
	}
328
329
	/**
330
	 * Gets the defined field definitions from the ext_tables.sql files.
331
	 *
332
	 * @return array The accordant definitions
333
	 */
334 5
	protected function getDefinedFieldDefinitions() {
335 5
		$cacheTables = $this->categoryRegistry->getDatabaseTableDefinitions();
336 5
		$content = $this->schemaMigrationService->getFieldDefinitions_fileContent (
337 5
			implode(chr(10), $this->getAllRawStructureDefinitions()) . $cacheTables
338
		);
339 5
		return $content;
340
	}
341
342
	/**
343
	 * Gets all structure definitions of extensions the TYPO3 Core.
344
	 *
345
	 * @return array All structure definitions
346
	 */
347 5
	protected function getAllRawStructureDefinitions() {
348
349 5
        $packageStates = include(PATH_typo3conf .'PackageStates.php');
350
351 5
        $tmp = $GLOBALS['TYPO3_LOADED_EXT'];
352
353 5
        $GLOBALS['TYPO3_LOADED_EXT'] = array_merge($packageStates['packages'], $GLOBALS['TYPO3_LOADED_EXT']);
354
355 5
		$expectedSchemaString = $this->expectedSchemaService->getTablesDefinitionString(TRUE);
356 5
		$rawDefinitions = $this->schemaMigrationService->getStatementArray($expectedSchemaString, TRUE);
357
358 5
        $GLOBALS['TYPO3_LOADED_EXT'] = $tmp;
359
360 5
		return $rawDefinitions;
361
	}
362
363
	/**
364
	 * Gets the defined update types.
365
	 *
366
	 * @return array
367
	 */
368 5
	protected function getUpdateTypes() {
369 5
		return GeneralUtility::trimExplode(',', self::UpdateTypes_List, TRUE);
370
	}
371
372
	/**
373
	 * Gets the defined remove types.
374
	 *
375
	 * @return array
376
	 */
377 2
	protected function getRemoveTypes() {
378 2
		return GeneralUtility::trimExplode(',', self::RemoveTypes_list, TRUE);
379
	}
380
381
	/**
382
	 * sorts the statements in an array
383
	 *
384
	 * @param array $statements
385
	 * @return array
386
	 */
387 5
	protected function sortStatements($statements) {
388 5
		$newStatements = array();
389 5
		foreach($statements as $key=>$statement) {
390 5
			if($this->isDropKeyStatement($statement)) {
391 5
				$newStatements[$key] = $statement;
392
			}
393
		}
394 5
		foreach($statements as $key=>$statement) {
395
			// writing the statement again, does not change its position
396
			// this saves a condition check
397 5
			$newStatements[$key] = $statement;
398
		}
399
400 5
		return $newStatements;
401
	}
402
403
	/**
404
	 * returns true if the given statement looks as if it drops a (primary) key
405
	 *
406
	 * @param $statement
407
	 * @return bool
408
	 */
409 5
	protected function isDropKeyStatement($statement) {
410 5
		return strpos($statement, ' DROP ') !== FALSE && strpos($statement, ' KEY') !== FALSE;
411
	}
412
}
413