1<?php 2 3/** 4 * Parses the definition of a key. 5 */ 6 7namespace PhpMyAdmin\SqlParser\Components; 8 9use PhpMyAdmin\SqlParser\Component; 10use PhpMyAdmin\SqlParser\Context; 11use PhpMyAdmin\SqlParser\Parser; 12use PhpMyAdmin\SqlParser\Token; 13use PhpMyAdmin\SqlParser\TokensList; 14 15/** 16 * Parses the definition of a key. 17 * 18 * Used for parsing `CREATE TABLE` statement. 19 * 20 * @category Components 21 * 22 * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+ 23 */ 24class Key extends Component 25{ 26 /** 27 * All key options. 28 * 29 * @var array 30 */ 31 public static $KEY_OPTIONS = array( 32 'KEY_BLOCK_SIZE' => array( 33 1, 34 'var', 35 ), 36 'USING' => array( 37 2, 38 'var', 39 ), 40 'WITH PARSER' => array( 41 3, 42 'var', 43 ), 44 'COMMENT' => array( 45 4, 46 'var=', 47 ) 48 ); 49 50 /** 51 * The name of this key. 52 * 53 * @var string 54 */ 55 public $name; 56 57 /** 58 * Columns. 59 * 60 * @var array 61 */ 62 public $columns; 63 64 /** 65 * The type of this key. 66 * 67 * @var string 68 */ 69 public $type; 70 71 /** 72 * The options of this key. 73 * 74 * @var OptionsArray 75 */ 76 public $options; 77 78 /** 79 * Constructor. 80 * 81 * @param string $name the name of the key 82 * @param array $columns the columns covered by this key 83 * @param string $type the type of this key 84 * @param OptionsArray $options the options of this key 85 */ 86 public function __construct( 87 $name = null, 88 array $columns = array(), 89 $type = null, 90 $options = null 91 ) { 92 $this->name = $name; 93 $this->columns = $columns; 94 $this->type = $type; 95 $this->options = $options; 96 } 97 98 /** 99 * @param Parser $parser the parser that serves as context 100 * @param TokensList $list the list of tokens that are being parsed 101 * @param array $options parameters for parsing 102 * 103 * @return Key 104 */ 105 public static function parse(Parser $parser, TokensList $list, array $options = array()) 106 { 107 $ret = new self(); 108 109 /** 110 * Last parsed column. 111 * 112 * @var array 113 */ 114 $lastColumn = array(); 115 116 /** 117 * The state of the parser. 118 * 119 * Below are the states of the parser. 120 * 121 * 0 ----------------------[ type ]-----------------------> 1 122 * 123 * 1 ----------------------[ name ]-----------------------> 1 124 * 1 ---------------------[ columns ]---------------------> 2 125 * 126 * 2 ---------------------[ options ]---------------------> 3 127 * 128 * @var int 129 */ 130 $state = 0; 131 132 for (; $list->idx < $list->count; ++$list->idx) { 133 /** 134 * Token parsed at this moment. 135 * 136 * @var Token 137 */ 138 $token = $list->tokens[$list->idx]; 139 140 // End of statement. 141 if ($token->type === Token::TYPE_DELIMITER) { 142 break; 143 } 144 145 // Skipping whitespaces and comments. 146 if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { 147 continue; 148 } 149 150 if ($state === 0) { 151 $ret->type = $token->value; 152 $state = 1; 153 } elseif ($state === 1) { 154 if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { 155 $state = 2; 156 } else { 157 $ret->name = $token->value; 158 } 159 } elseif ($state === 2) { 160 if ($token->type === Token::TYPE_OPERATOR) { 161 if ($token->value === '(') { 162 $state = 3; 163 } elseif (($token->value === ',') || ($token->value === ')')) { 164 $state = ($token->value === ',') ? 2 : 4; 165 if (! empty($lastColumn)) { 166 $ret->columns[] = $lastColumn; 167 $lastColumn = array(); 168 } 169 } 170 } else { 171 $lastColumn['name'] = $token->value; 172 } 173 } elseif ($state === 3) { 174 if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ')')) { 175 $state = 2; 176 } else { 177 $lastColumn['length'] = $token->value; 178 } 179 } elseif ($state === 4) { 180 $ret->options = OptionsArray::parse($parser, $list, static::$KEY_OPTIONS); 181 ++$list->idx; 182 break; 183 } 184 } 185 186 --$list->idx; 187 188 return $ret; 189 } 190 191 /** 192 * @param Key $component the component to be built 193 * @param array $options parameters for building 194 * 195 * @return string 196 */ 197 public static function build($component, array $options = array()) 198 { 199 $ret = $component->type . ' '; 200 if (! empty($component->name)) { 201 $ret .= Context::escape($component->name) . ' '; 202 } 203 204 $columns = array(); 205 foreach ($component->columns as $column) { 206 $tmp = Context::escape($column['name']); 207 if (isset($column['length'])) { 208 $tmp .= '(' . $column['length'] . ')'; 209 } 210 $columns[] = $tmp; 211 } 212 213 $ret .= '(' . implode(',', $columns) . ') ' . $component->options; 214 215 return trim($ret); 216 } 217} 218