1<?php 2 3/** 4 * Parses a list of expressions delimited by a comma. 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 * Parses a list of expressions delimited by a comma. 16 * 17 * @category Keywords 18 * 19 * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+ 20 */ 21class ExpressionArray extends Component 22{ 23 /** 24 * @param Parser $parser the parser that serves as context 25 * @param TokensList $list the list of tokens that are being parsed 26 * @param array $options parameters for parsing 27 * 28 * @return Expression[] 29 * @throws \PhpMyAdmin\SqlParser\Exceptions\ParserException 30 */ 31 public static function parse(Parser $parser, TokensList $list, array $options = array()) 32 { 33 $ret = array(); 34 35 /** 36 * The state of the parser. 37 * 38 * Below are the states of the parser. 39 * 40 * 0 ----------------------[ array ]---------------------> 1 41 * 42 * 1 ------------------------[ , ]------------------------> 0 43 * 1 -----------------------[ else ]----------------------> (END) 44 * 45 * @var int 46 */ 47 $state = 0; 48 49 for (; $list->idx < $list->count; ++$list->idx) { 50 /** 51 * Token parsed at this moment. 52 * 53 * @var Token 54 */ 55 $token = $list->tokens[$list->idx]; 56 57 // End of statement. 58 if ($token->type === Token::TYPE_DELIMITER) { 59 break; 60 } 61 62 // Skipping whitespaces and comments. 63 if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { 64 continue; 65 } 66 67 if (($token->type === Token::TYPE_KEYWORD) 68 && ($token->flags & Token::FLAG_KEYWORD_RESERVED) 69 && ((~$token->flags & Token::FLAG_KEYWORD_FUNCTION)) 70 && ($token->value !== 'DUAL') 71 && ($token->value !== 'NULL') 72 && ($token->value !== 'CASE') 73 ) { 74 // No keyword is expected. 75 break; 76 } 77 78 if ($state === 0) { 79 if ($token->type === Token::TYPE_KEYWORD 80 && $token->value === 'CASE' 81 ) { 82 $expr = CaseExpression::parse($parser, $list, $options); 83 } else { 84 $expr = Expression::parse($parser, $list, $options); 85 } 86 87 if ($expr === null) { 88 break; 89 } 90 $ret[] = $expr; 91 $state = 1; 92 } elseif ($state === 1) { 93 if ($token->value === ',') { 94 $state = 0; 95 } else { 96 break; 97 } 98 } 99 } 100 101 if ($state === 0) { 102 $parser->error( 103 'An expression was expected.', 104 $list->tokens[$list->idx] 105 ); 106 } 107 108 --$list->idx; 109 110 if (is_array($ret)) { 111 $retIndex = count($ret) - 1; 112 if (isset($ret[$retIndex])) { 113 $expr = $ret[$retIndex]->expr; 114 if (preg_match('/\s*--\s.*$/', $expr, $matches)) { 115 $found = $matches[0]; 116 $ret[$retIndex]->expr = substr($expr, 0, strlen($expr) - strlen($found)); 117 } 118 } 119 } 120 121 return $ret; 122 } 123 124 /** 125 * @param Expression[] $component the component to be built 126 * @param array $options parameters for building 127 * 128 * @return string 129 */ 130 public static function build($component, array $options = array()) 131 { 132 $ret = array(); 133 foreach ($component as $frag) { 134 $ret[] = $frag::build($frag); 135 } 136 137 return implode(', ', $ret); 138 } 139} 140