core-bundle/src/Resources/contao/library/Contao/DcaExtractor.php
protected function createExtract()
	{
		// Load the default language file (see #7202)
		if (empty($GLOBALS['TL_LANG']['MSC']))
		{
			System::loadLanguageFile('default');
		}

		// Load the data container
		if (!isset($GLOBALS['loadDataContainer'][$this->strTable]))
		{
			$this->loadDataContainer($this->strTable);
		}

		// Return if the table is not defined
		if (!isset($GLOBALS['TL_DCA'][$this->strTable]))
		{
			return;
		}

		// Return if the DC type is "File"
		if ($GLOBALS['TL_DCA'][$this->strTable]['config']['dataContainer'] == 'File')
		{
			return;
		}

		// Return if the DC type is "Folder" and the DC is not database assisted
		if ($GLOBALS['TL_DCA'][$this->strTable]['config']['dataContainer'] == 'Folder' && empty($GLOBALS['TL_DCA'][$this->strTable]['config']['databaseAssisted']))
		{
			return;
		}

		$blnFromFile = false;
		$arrRelations = array();

		// Check whether there are fields (see #4826)
		if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields']))
		{
			foreach ($GLOBALS['TL_DCA'][$this->strTable]['fields'] as $field=>$config)
			{
				// Check whether all fields have an SQL definition
				if (!isset($config['sql']) && isset($config['inputType']))
				{
					$blnFromFile = true;
				}

				// Check whether there is a relation (see #6524)
				if (isset($config['relation']))
				{
					$table = substr($config['foreignKey'], 0, strrpos($config['foreignKey'], '.'));
					$arrRelations[$field] = array_merge(array('table'=>$table, 'field'=>'id'), $config['relation']);

					// Store the field delimiter if the related IDs are stored in CSV format (see #257)
					if (isset($config['eval']['csv']))
					{
						$arrRelations[$field]['delimiter'] = $config['eval']['csv'];
					}

					// Table name and field name are mandatory
					if (empty($arrRelations[$field]['table']) || empty($arrRelations[$field]['field']))
					{
						throw new \Exception('Incomplete relation defined for ' . $this->strTable . '.' . $field);
					}
				}
			}
		}

		$sql = $GLOBALS['TL_DCA'][$this->strTable]['config']['sql'] ?? array();
		$fields = $GLOBALS['TL_DCA'][$this->strTable]['fields'] ?? array();

		// Deprecated since Contao 4.0, to be removed in Contao 5.0
		if ($blnFromFile)
		{
			@trigger_error('Using database.sql files has been deprecated and will no longer work in Contao 5.0. Use a DCA file instead.', E_USER_DEPRECATED);

			if (!isset(static::$arrSql[$this->strTable]))
			{
				$arrSql = array();

				try
				{
					$files = System::getContainer()->get('contao.resource_locator')->locate('config/database.sql', null, false);
				}
				catch (\InvalidArgumentException $e)
				{
					$files = array();
				}

				foreach ($files as $file)
				{
					$arrSql = array_merge_recursive($arrSql, SqlFileParser::parse($file));
				}

				static::$arrSql = $arrSql;
			}

			$arrTable = static::$arrSql[$this->strTable];

			if (\is_array($arrTable['TABLE_OPTIONS']))
			{
				$arrTable['TABLE_OPTIONS'] = $arrTable['TABLE_OPTIONS'][0]; // see #324
			}

			list($engine, , $charset) = explode(' ', trim($arrTable['TABLE_OPTIONS']));

			if ($engine != '')
			{
				$sql['engine'] = str_replace('ENGINE=', '', $engine);
			}
			if ($charset != '')
			{
				$sql['charset'] = str_replace('CHARSET=', '', $charset);
			}

			// Fields
			if (isset($arrTable['TABLE_FIELDS']))
			{
				foreach ($arrTable['TABLE_FIELDS'] as $k=>$v)
				{
					$fields[$k]['sql'] = str_replace('`' . $k . '` ', '', $v);
				}
			}

			// Keys
			if (isset($arrTable['TABLE_CREATE_DEFINITIONS']))
			{
				foreach ($arrTable['TABLE_CREATE_DEFINITIONS'] as $strKey)
				{
					if (preg_match('/^([A-Z]+ )?KEY .+\(([^)]+)\)$/', $strKey, $arrMatches) && preg_match_all('/`([^`]+)`/', $arrMatches[2], $arrFields))
					{
						$type = trim($arrMatches[1]);
						$field = implode(',', $arrFields[1]);
						$sql['keys'][$field] = ($type != '') ? strtolower($type) : 'index';
					}
				}
			}
		}

		// Not a database table or no field information
		if (empty($sql) || empty($fields))
		{
			return;
		}

		$params = System::getContainer()->get('database_connection')->getParams();

		// Add the default engine and charset if none is given
		if (empty($sql['engine']))
		{
			$sql['engine'] = $params['defaultTableOptions']['engine'] ?? 'InnoDB';
		}
		if (empty($sql['charset']))
		{
			$sql['charset'] = $params['defaultTableOptions']['charset'] ?? 'utf8mb4';
		}
		if (empty($sql['collate']))
		{
			$sql['collate'] = $params['defaultTableOptions']['collate'] ?? 'utf8mb4_unicode_ci';
		}

		// Meta
		$this->arrMeta = array
		(
			'engine' => $sql['engine'],
			'charset' => $sql['charset'],
			'collate' => $sql['collate']
		);

		// Fields
		if (!empty($fields))
		{
			$this->arrFields = array();
			$this->arrOrderFields = array();

			foreach ($fields as $field=>$config)
			{
				if (isset($config['sql']))
				{
					$this->arrFields[$field] = $config['sql'];
				}

				// Only add order fields of binary fields (see #7785)
				if (isset($config['inputType']) && $config['inputType'] == 'fileTree' && isset($config['eval']['orderField']))
				{
					$this->arrOrderFields[] = $config['eval']['orderField'];
				}

				if (isset($config['eval']['unique']) && $config['eval']['unique'])
				{
					$this->arrUniqueFields[] = $field;
				}
			}
		}

		// Keys
		if (!empty($sql['keys']) && \is_array($sql['keys']))
		{
			$this->arrKeys = array();

			foreach ($sql['keys'] as $field=>$type)
			{
				$this->arrKeys[$field] = $type;

				if ($type == 'unique')
				{
					$this->arrUniqueFields[] = $field;
				}
			}
		}

		// Relations
		if (!empty($arrRelations))
		{
			$this->arrRelations = array();

			foreach ($arrRelations as $field=>$config)
			{
				$this->arrRelations[$field] = array();

				foreach ($config as $k=>$v)
				{
					$this->arrRelations[$field][$k] = $v;
				}
			}
		}

		$this->arrUniqueFields = array_unique($this->arrUniqueFields);
		$this->blnIsDbTable = true;
	}
protected function createExtract()
	{
		// Load the default language file (see #7202)
		if (empty($GLOBALS['TL_LANG']['MSC']))
		{
			System::loadLanguageFile('default');
		}

		// Load the data container
		if (!isset($GLOBALS['loadDataContainer'][$this->strTable]))
		{
			$this->loadDataContainer($this->strTable);
		}

		// Return if the table is not defined
		if (!isset($GLOBALS['TL_DCA'][$this->strTable]))
		{
			return;
		}

		// Return if the DC type is "File"
		if ($GLOBALS['TL_DCA'][$this->strTable]['config']['dataContainer'] == 'File')
		{
			return;
		}

		// Return if the DC type is "Folder" and the DC is not database assisted
		if ($GLOBALS['TL_DCA'][$this->strTable]['config']['dataContainer'] == 'Folder' && empty($GLOBALS['TL_DCA'][$this->strTable]['config']['databaseAssisted']))
		{
			return;
		}

		$blnFromFile = false;
		$arrRelations = array();

		// Check whether there are fields (see #4826)
		if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields']))
		{
			foreach ($GLOBALS['TL_DCA'][$this->strTable]['fields'] as $field=>$config)
			{
				// Check whether all fields have an SQL definition
				if (!isset($config['sql']) && isset($config['inputType']))
				{
					$blnFromFile = true;
				}

				// Check whether there is a relation (see #6524)
				if (isset($config['relation']))
				{
					$table = substr($config['foreignKey'], 0, strrpos($config['foreignKey'], '.'));
					$arrRelations[$field] = array_merge(array('table'=>$table, 'field'=>'id'), $config['relation']);

					// Store the field delimiter if the related IDs are stored in CSV format (see #257)
					if (isset($config['eval']['csv']))
					{
						$arrRelations[$field]['delimiter'] = $config['eval']['csv'];
					}

					// Table name and field name are mandatory
					if (empty($arrRelations[$field]['table']) || empty($arrRelations[$field]['field']))
					{
						throw new \Exception('Incomplete relation defined for ' . $this->strTable . '.' . $field);
					}
				}
			}
		}

		$sql = $GLOBALS['TL_DCA'][$this->strTable]['config']['sql'] ?? array();
		$fields = $GLOBALS['TL_DCA'][$this->strTable]['fields'] ?? array();

		// Deprecated since Contao 4.0, to be removed in Contao 5.0
		if ($blnFromFile)
		{
			@trigger_error('Using database.sql files has been deprecated and will no longer work in Contao 5.0. Use a DCA file instead.', E_USER_DEPRECATED);

			if (!isset(static::$arrSql[$this->strTable]))
			{
				$arrSql = array();

				try
				{
					$files = System::getContainer()->get('contao.resource_locator')->locate('config/database.sql', null, false);
				}
				catch (\InvalidArgumentException $e)
				{
					$files = array();
				}

				foreach ($files as $file)
				{
					$arrSql = array_merge_recursive($arrSql, SqlFileParser::parse($file));
				}

				static::$arrSql = $arrSql;
			}

			$arrTable = static::$arrSql[$this->strTable];

			if (\is_array($arrTable['TABLE_OPTIONS']))
			{
				$arrTable['TABLE_OPTIONS'] = $arrTable['TABLE_OPTIONS'][0]; // see #324
			}

			list($engine, , $charset) = explode(' ', trim($arrTable['TABLE_OPTIONS']));

			if ($engine != '')
			{
				$sql['engine'] = str_replace('ENGINE=', '', $engine);
			}
			if ($charset != '')
			{
				$sql['charset'] = str_replace('CHARSET=', '', $charset);
			}

			// Fields
			if (isset($arrTable['TABLE_FIELDS']))
			{
				foreach ($arrTable['TABLE_FIELDS'] as $k=>$v)
				{
					$fields[$k]['sql'] = str_replace('`' . $k . '` ', '', $v);
				}
			}

			// Keys
			if (isset($arrTable['TABLE_CREATE_DEFINITIONS']))
			{
				foreach ($arrTable['TABLE_CREATE_DEFINITIONS'] as $strKey)
				{
					if (preg_match('/^([A-Z]+ )?KEY .+\(([^)]+)\)$/', $strKey, $arrMatches) && preg_match_all('/`([^`]+)`/', $arrMatches[2], $arrFields))
					{
						$type = trim($arrMatches[1]);
						$field = implode(',', $arrFields[1]);
						$sql['keys'][$field] = ($type != '') ? strtolower($type) : 'index';
					}
				}
			}
		}

		// Not a database table or no field information
		if (empty($sql) || empty($fields))
		{
			return;
		}

		$params = System::getContainer()->get('database_connection')->getParams();

		// Add the default engine and charset if none is given
		if (empty($sql['engine']))
		{
			$sql['engine'] = $params['defaultTableOptions']['engine'] ?? 'InnoDB';
		}
		if (empty($sql['charset']))
		{
			$sql['charset'] = $params['defaultTableOptions']['charset'] ?? 'utf8mb4';
		}
		if (empty($sql['collate']))
		{
			$sql['collate'] = $params['defaultTableOptions']['collate'] ?? 'utf8mb4_unicode_ci';
		}

		// Meta
		$this->arrMeta = array
		(
			'engine' => $sql['engine'],
			'charset' => $sql['charset'],
			'collate' => $sql['collate']
		);

		// Fields
		if (!empty($fields))
		{
			$this->arrFields = array();
			$this->arrOrderFields = array();

			foreach ($fields as $field=>$config)
			{
				if (isset($config['sql']))
				{
					$this->arrFields[$field] = $config['sql'];
				}

				// Only add order fields of binary fields (see #7785)
				if (isset($config['inputType']) && $config['inputType'] == 'fileTree' && isset($config['eval']['orderField']))
				{
					$this->arrOrderFields[] = $config['eval']['orderField'];
				}

				if (isset($config['eval']['unique']) && $config['eval']['unique'])
				{
					$this->arrUniqueFields[] = $field;
				}
			}
		}

		// Keys
		if (!empty($sql['keys']) && \is_array($sql['keys']))
		{
			$this->arrKeys = array();

			foreach ($sql['keys'] as $field=>$type)
			{
				$this->arrKeys[$field] = $type;

				if ($type == 'unique')
				{
					$this->arrUniqueFields[] = $field;
				}
			}
		}

		// Relations
		if (!empty($arrRelations))
		{
			$this->arrRelations = array();

			foreach ($arrRelations as $field=>$config)
			{
				$this->arrRelations[$field] = array();

				foreach ($config as $k=>$v)
				{
					$this->arrRelations[$field][$k] = $v;
				}
			}
		}

		$this->arrUniqueFields = array_unique($this->arrUniqueFields);
		$this->blnIsDbTable = true;
	}
Create the extract from the DCA or the database.sql files