1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the TYPO3 CMS project. 7 * 8 * It is free software; you can redistribute it and/or modify it under 9 * the terms of the GNU General Public License, either version 2 10 * of the License, or any later version. 11 * 12 * For the full copyright and license information, please read the 13 * LICENSE.txt file that was distributed with this source code. 14 * 15 * The TYPO3 project - inspiring people to share! 16 */ 17 18namespace TYPO3\CMS\Core\Database\Schema; 19 20use Psr\EventDispatcher\EventDispatcherInterface; 21use TYPO3\CMS\Core\Database\Event\AlterTableDefinitionStatementsEvent; 22use TYPO3\CMS\Core\Package\PackageManager; 23 24/** 25 * Helper methods to handle raw SQL input and transform it into individual statements 26 * for further processing. 27 * 28 * @internal 29 */ 30class SqlReader 31{ 32 /** 33 * @var EventDispatcherInterface 34 */ 35 protected $eventDispatcher; 36 37 /** 38 * @var PackageManager 39 */ 40 protected $packageManager; 41 42 /** 43 * @param EventDispatcherInterface $eventDispatcher 44 * @param PackageManager $packageManager 45 * @throws \InvalidArgumentException 46 */ 47 public function __construct(EventDispatcherInterface $eventDispatcher, PackageManager $packageManager) 48 { 49 $this->eventDispatcher = $eventDispatcher; 50 $this->packageManager = $packageManager; 51 } 52 53 /** 54 * Cycle through all loaded extensions and get full table definitions as concatenated string 55 * 56 * @param bool $withStatic TRUE if sql from ext_tables_static+adt.sql should be loaded, too. 57 * @return string Concatenated SQL of loaded extensions ext_tables.sql 58 */ 59 public function getTablesDefinitionString(bool $withStatic = false): string 60 { 61 $sqlString = []; 62 63 // Find all ext_tables.sql of loaded extensions 64 foreach ($this->packageManager->getActivePackages() as $package) { 65 $packagePath = $package->getPackagePath(); 66 if (@file_exists($packagePath . 'ext_tables.sql')) { 67 $sqlString[] = (string)file_get_contents($packagePath . 'ext_tables.sql'); 68 } 69 if ($withStatic && @file_exists($packagePath . 'ext_tables_static+adt.sql')) { 70 $sqlString[] = (string)file_get_contents($packagePath . 'ext_tables_static+adt.sql'); 71 } 72 } 73 74 /** @var AlterTableDefinitionStatementsEvent $event */ 75 $event = $this->eventDispatcher->dispatch(new AlterTableDefinitionStatementsEvent($sqlString)); 76 $sqlString = $event->getSqlData(); 77 78 return implode(LF . LF, $sqlString); 79 } 80 81 /** 82 * Returns an array where every entry is a single SQL-statement. 83 * Input must be formatted like an ordinary MySQL dump file. Every statements needs to be terminated by a ';' 84 * and there may only be one statement (or partial statement) per line. 85 * 86 * @param string $dumpContent The SQL dump content. 87 * @param string $queryRegex Regex to select which statements to return. 88 * @return array Array of SQL statements 89 */ 90 public function getStatementArray(string $dumpContent, string $queryRegex = null): array 91 { 92 $statementArray = []; 93 $statementArrayPointer = 0; 94 $isInMultilineComment = false; 95 foreach (explode(LF, $dumpContent) as $lineContent) { 96 $lineContent = trim($lineContent); 97 98 // Skip empty lines and comments 99 if ($lineContent === '' || $lineContent[0] === '#' || strpos($lineContent, '--') === 0 || 100 strpos($lineContent, '/*') === 0 || substr($lineContent, -2) === '*/' || $isInMultilineComment 101 ) { 102 // skip c style multiline comments 103 if (strpos($lineContent, '/*') === 0 && substr($lineContent, -2) !== '*/') { 104 $isInMultilineComment = true; 105 } 106 if (substr($lineContent, -2) === '*/') { 107 $isInMultilineComment = false; 108 } 109 continue; 110 } 111 112 $statementArray[$statementArrayPointer] = ($statementArray[$statementArrayPointer] ?? '') . $lineContent; 113 114 if (substr($lineContent, -1) === ';') { 115 $statement = trim($statementArray[$statementArrayPointer]); 116 if (!$statement || ($queryRegex && !preg_match('/' . $queryRegex . '/i', $statement))) { 117 unset($statementArray[$statementArrayPointer]); 118 } 119 $statementArrayPointer++; 120 } else { 121 $statementArray[$statementArrayPointer] .= ' '; 122 } 123 } 124 125 return $statementArray; 126 } 127 128 /** 129 * Extract only INSERT statements from SQL dump 130 * 131 * @param string $dumpContent 132 * @return array 133 */ 134 public function getInsertStatementArray(string $dumpContent): array 135 { 136 return $this->getStatementArray($dumpContent, '^INSERT'); 137 } 138 139 /** 140 * Extract only CREATE TABLE statements from SQL dump 141 * 142 * @param string $dumpContent 143 * @return array 144 */ 145 public function getCreateTableStatementArray(string $dumpContent): array 146 { 147 return $this->getStatementArray($dumpContent, '^CREATE TABLE'); 148 } 149} 150