1<?php 2 3/** 4 * `ORDER BY` 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 * `ORDER BY` keyword parser. 16 * 17 * @category Keywords 18 * 19 * @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+ 20 */ 21class OrderKeyword extends Component 22{ 23 /** 24 * The expression that is used for ordering. 25 * 26 * @var Expression 27 */ 28 public $expr; 29 30 /** 31 * The order type. 32 * 33 * @var string 34 */ 35 public $type; 36 37 /** 38 * Constructor. 39 * 40 * @param Expression $expr the expression that we are sorting by 41 * @param string $type the sorting type 42 */ 43 public function __construct($expr = null, $type = 'ASC') 44 { 45 $this->expr = $expr; 46 $this->type = $type; 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 OrderKeyword[] 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 --------------------[ expression ]-------------------> 1 68 * 69 * 1 ------------------------[ , ]------------------------> 0 70 * 1 -------------------[ ASC / DESC ]--------------------> 1 71 * 72 * @var int 73 */ 74 $state = 0; 75 76 for (; $list->idx < $list->count; ++$list->idx) { 77 /** 78 * Token parsed at this moment. 79 * 80 * @var Token 81 */ 82 $token = $list->tokens[$list->idx]; 83 84 // End of statement. 85 if ($token->type === Token::TYPE_DELIMITER) { 86 break; 87 } 88 89 // Skipping whitespaces and comments. 90 if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { 91 continue; 92 } 93 94 if ($state === 0) { 95 $expr->expr = Expression::parse($parser, $list); 96 $state = 1; 97 } elseif ($state === 1) { 98 if (($token->type === Token::TYPE_KEYWORD) 99 && (($token->keyword === 'ASC') || ($token->keyword === 'DESC')) 100 ) { 101 $expr->type = $token->keyword; 102 } elseif (($token->type === Token::TYPE_OPERATOR) 103 && ($token->value === ',') 104 ) { 105 if (! empty($expr->expr)) { 106 $ret[] = $expr; 107 } 108 $expr = new self(); 109 $state = 0; 110 } else { 111 break; 112 } 113 } 114 } 115 116 // Last iteration was not processed. 117 if (! empty($expr->expr)) { 118 $ret[] = $expr; 119 } 120 121 --$list->idx; 122 123 return $ret; 124 } 125 126 /** 127 * @param OrderKeyword|OrderKeyword[] $component the component to be built 128 * @param array $options parameters for building 129 * 130 * @return string 131 */ 132 public static function build($component, array $options = array()) 133 { 134 if (is_array($component)) { 135 return implode(', ', $component); 136 } 137 138 return $component->expr . ' ' . $component->type; 139 } 140} 141