1: <?php
2:
3: namespace vierbergenlars\Forage\QueryParser;
4:
5: use vierbergenlars\Forage\SearchQuery\QueryBuilder;
6: use vierbergenlars\Forage\QueryParser\Token;
7:
8: 9: 10:
11: class Compiler
12: {
13:
14: 15: 16: 17:
18: protected $queryBuilder;
19:
20: 21: 22: 23:
24: protected $allowedSearchFields = array();
25:
26: 27: 28: 29:
30: protected $allowedFieldNames = array();
31:
32: 33: 34: 35:
36: protected $allowedTokens = array();
37:
38: 39: 40: 41:
42: public function __construct(QueryBuilder $queryBuilder)
43: {
44: $this->queryBuilder = $queryBuilder;
45: }
46:
47: 48: 49: 50: 51:
52: public function setAllowedFieldNames(array $fieldNames)
53: {
54: $this->allowedFieldNames = $fieldNames;
55: return $this;
56: }
57:
58: 59: 60: 61: 62:
63: protected function isAllowedFieldName($field)
64: {
65: return empty($this->allowedFieldNames) || in_array($field, $this->allowedFieldNames, true);
66: }
67:
68: 69: 70: 71: 72:
73: public function setAllowedSearchFields(array $fieldNames)
74: {
75: $this->allowedSearchFields = $fieldNames;
76: return $this;
77: }
78:
79: 80: 81: 82: 83:
84: protected function isAllowedSearchField($field)
85: {
86: return empty($this->allowedSearchFields) || in_array($field, $this->allowedSearchFields, true);
87: }
88:
89: 90: 91: 92: 93:
94: public function setAllowedTokens(array $tokens)
95: {
96: $this->allowedTokens = $tokens;
97: return $this;
98: }
99:
100: 101: 102: 103: 104:
105: protected function isAllowedToken(Token $token)
106: {
107: return empty($this->allowedTokens) || in_array($token->getType(), $this->allowedTokens, true);
108: }
109:
110: 111: 112: 113: 114: 115:
116: public function compileQuery($queryExpr)
117: {
118: $tokens = Lexer::tokenize($queryExpr);
119: $searchQuery = '';
120: while(false !== ($token = current($tokens))) {
121: if(!$this->isAllowedToken($token))
122: throw new ParseException(Token::getName($token->getType()) . ' is disabled', $queryExpr, $token->getStartPosition());
123: switch($token->getType()) {
124: case Token::T_STRING:
125: $searchQuery.= ' ' . $token->getData();
126: break;
127: case Token::T_FIELD_NAME:
128: if(!$this->isAllowedFieldName($token->getData()))
129: throw new ParseException('Field name not allowed', $queryExpr, $token->getStartPosition());
130: $nextToken = next($tokens);
131: if($nextToken === false)
132: throw new ParseException('Unexpected end of token stream', $queryExpr, strlen($queryExpr));
133: if(!$this->isAllowedToken($nextToken))
134: throw new ParseException(Token::getName($nextToken->getType()) . ' is disabled', $queryExpr, $nextToken->getStartPosition());
135: switch($nextToken->getType()) {
136: case Token::T_FIELD_VALUE:
137: $this->queryBuilder->addFilter($token->getData(), $nextToken->getData());
138: break;
139: case Token::T_FIELD_WEIGHT:
140: $this->queryBuilder->addWeight($token->getData(), $nextToken->getData());
141: break;
142: default:
143: throw new ParseException('Unexpected ' . Token::getName($nextToken->getType()), $queryExpr, $nextToken->getStartPosition());
144: }
145: break;
146: case Token::T_FIELD_SEARCH:
147: if(!$this->isAllowedSearchField($token->getData()))
148: throw new ParseException('Search field not allowed', $queryExpr, $token->getStartPosition());
149: $this->queryBuilder->addSearchField($token->getData());
150: break;
151: default:
152: throw new ParseException('Unexpected ' . Token::getName($token->getType()) . ' (This is a lexer bug, please report it)', $queryExpr, $token->getStartPosition());
153: }
154: next($tokens);
155: }
156: $this->queryBuilder->setSearchQuery(substr($searchQuery, 1));
157: return $this;
158: }
159:
160: 161: 162: 163:
164: public function getQueryBuilder()
165: {
166: return $this->queryBuilder;
167: }
168:
169: 170: 171: 172:
173: public function getQuery()
174: {
175: return $this->queryBuilder->getQuery();
176: }
177:
178: public function __clone()
179: {
180: $this->queryBuilder = clone $this->queryBuilder;
181: }
182:
183: }
184: