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\EventListener; 19 20use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; 21use Doctrine\DBAL\Platforms\AbstractPlatform; 22use Doctrine\DBAL\Schema\Column; 23use Doctrine\DBAL\Types\Type; 24 25/** 26 * Event listener to handle additional processing for custom 27 * doctrine types. 28 */ 29class SchemaColumnDefinitionListener 30{ 31 /** 32 * Listener for column definition events. This intercepts definitions 33 * for custom doctrine types and builds the appropriate Column Object. 34 * 35 * @param \Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs $event 36 * @throws \Doctrine\DBAL\Exception 37 */ 38 public function onSchemaColumnDefinition(SchemaColumnDefinitionEventArgs $event) 39 { 40 $tableColumn = $event->getTableColumn(); 41 $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); 42 43 $dbType = $this->getDatabaseType($tableColumn['type']); 44 if ($dbType !== 'enum' && $dbType !== 'set') { 45 return; 46 } 47 48 $column = $this->getEnumerationTableColumnDefinition( 49 $tableColumn, 50 $event->getConnection()->getDatabasePlatform() 51 ); 52 53 $event->setColumn($column); 54 $event->preventDefault(); 55 } 56 57 /** 58 * Build a Doctrine column object for TYPE/TYPE columns. 59 * 60 * @param array $tableColumn 61 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform 62 * @return \Doctrine\DBAL\Schema\Column 63 * @throws \Doctrine\DBAL\Exception 64 * @todo: The $tableColumn source currently only support MySQL definition style. 65 */ 66 protected function getEnumerationTableColumnDefinition(array $tableColumn, AbstractPlatform $platform): Column 67 { 68 $options = [ 69 'length' => $tableColumn['length'] ?? null, 70 'unsigned' => false, 71 'fixed' => false, 72 'default' => $tableColumn['default'] ?? null, 73 'notnull' => ($tableColumn['null'] ?? '') !== 'YES', 74 'scale' => null, 75 'precision' => null, 76 'autoincrement' => false, 77 'comment' => $tableColumn['comment'] ?? null, 78 ]; 79 80 $dbType = $this->getDatabaseType($tableColumn['type']); 81 $doctrineType = $platform->getDoctrineTypeMapping($dbType); 82 83 $column = new Column($tableColumn['field'] ?? '', Type::getType($doctrineType), $options); 84 $column->setPlatformOption('unquotedValues', $this->getUnquotedEnumerationValues($tableColumn['type'])); 85 86 return $column; 87 } 88 89 /** 90 * Extract the field type from the definition string 91 * 92 * @param string $typeDefinition 93 * @return string 94 */ 95 protected function getDatabaseType(string $typeDefinition): string 96 { 97 $dbType = strtolower($typeDefinition); 98 $dbType = strtok($dbType, '(), '); 99 100 return $dbType; 101 } 102 103 /** 104 * @param string $typeDefinition 105 * @return array 106 */ 107 protected function getUnquotedEnumerationValues(string $typeDefinition): array 108 { 109 $valuesDefinition = preg_replace('#^(enum|set)\((.*)\)\s*$#i', '$2', $typeDefinition) ?? ''; 110 $quoteChar = $valuesDefinition[0]; 111 $separator = $quoteChar . ',' . $quoteChar; 112 113 $valuesDefinition = preg_replace( 114 '#' . $quoteChar . ',\s*' . $quoteChar . '#', 115 $separator, 116 $valuesDefinition 117 ) ?? ''; 118 119 $values = explode($quoteChar . ',' . $quoteChar, substr($valuesDefinition, 1, -1)); 120 121 return array_map( 122 static function (string $value) use ($quoteChar) { 123 return str_replace($quoteChar . $quoteChar, $quoteChar, $value); 124 }, 125 $values 126 ); 127 } 128} 129