1<?php 2 3/** 4 * `SET` keyword parser. 5 */ 6 7namespace PhpMyAdmin\SqlParser\Components; 8 9use PhpMyAdmin\SqlParser\Component; 10use PhpMyAdmin\SqlParser\Parser; 11use PhpMyAdmin\SqlParser\Token; 12use PhpMyAdmin\SqlParser\TokensList; 13 14/** 15 * `SET` keyword parser. 16 * 17 * @category Keywords 18 * 19 * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+ 20 */ 21class SetOperation extends Component 22{ 23 /** 24 * The name of the column that is being updated. 25 * 26 * @var string 27 */ 28 public $column; 29 30 /** 31 * The new value. 32 * 33 * @var string 34 */ 35 public $value; 36 37 /** 38 * Constructor. 39 * 40 * @param string $column Field's name.. 41 * @param string $value new value 42 */ 43 public function __construct($column = '', $value = '') 44 { 45 $this->column = $column; 46 $this->value = $value; 47 } 48 49 /** 50 * @param Parser $parser the parser that serves as context 51 * @param TokensList $list the list of tokens that are being parsed 52 * @param array $options parameters for parsing 53 * 54 * @return SetOperation[] 55 */ 56 public static function parse(Parser $parser, TokensList $list, array $options = array()) 57 { 58 $ret = array(); 59 60 $expr = new self(); 61 62 /** 63 * The state of the parser. 64 * 65 * Below are the states of the parser. 66 * 67 * 0 ---------------------[ col_name ]--------------------> 0 68 * 0 ------------------------[ = ]------------------------> 1 69 * 1 -----------------------[ value ]---------------------> 1 70 * 1 ------------------------[ , ]------------------------> 0 71 * 72 * @var int 73 */ 74 $state = 0; 75 76 /** 77 * Token when the parser has seen the latest comma 78 * 79 * @var Token 80 */ 81 $commaLastSeenAt = null; 82 83 for (; $list->idx < $list->count; ++$list->idx) { 84 /** 85 * Token parsed at this moment. 86 * 87 * @var Token 88 */ 89 $token = $list->tokens[$list->idx]; 90 91 // End of statement. 92 if ($token->type === Token::TYPE_DELIMITER) { 93 break; 94 } 95 96 // Skipping whitespaces and comments. 97 if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { 98 continue; 99 } 100 101 // No keyword is expected. 102 if (($token->type === Token::TYPE_KEYWORD) 103 && ($token->flags & Token::FLAG_KEYWORD_RESERVED) 104 && ($state === 0) 105 ) { 106 break; 107 } 108 109 if ($state === 0) { 110 if ($token->token === '=') { 111 $state = 1; 112 } elseif ($token->value !== ',') { 113 $expr->column .= $token->token; 114 } elseif ($token->value === ',') { 115 $commaLastSeenAt = $token; 116 } 117 } elseif ($state === 1) { 118 $tmp = Expression::parse( 119 $parser, 120 $list, 121 array( 122 'breakOnAlias' => true 123 ) 124 ); 125 if (is_null($tmp)) { 126 $parser->error('Missing expression.', $token); 127 break; 128 } 129 $expr->column = trim($expr->column); 130 $expr->value = $tmp->expr; 131 $ret[] = $expr; 132 $expr = new self(); 133 $state = 0; 134 $commaLastSeenAt = null; 135 } 136 } 137 --$list->idx; 138 139 // We saw a comma, but didn't see a column-value pair after it 140 if ($commaLastSeenAt !== null) { 141 $parser->error('Unexpected token.', $commaLastSeenAt); 142 } 143 144 return $ret; 145 } 146 147 /** 148 * @param SetOperation|SetOperation[] $component the component to be built 149 * @param array $options parameters for building 150 * 151 * @return string 152 */ 153 public static function build($component, array $options = array()) 154 { 155 if (is_array($component)) { 156 return implode(', ', $component); 157 } 158 159 return $component->column . ' = ' . $component->value; 160 } 161} 162