@@ -37,7 +37,7 @@ public function executeOperation(ServerConfig $config, OperationParams $op)
3737 if (!$ doc instanceof DocumentNode) {
3838 $ doc = Parser::parse ($ doc );
3939 }
40- if (! $ op ->allowsMutation () && AST ::isMutation ($ op ->operation , $ doc )) {
40+ if ($ op ->isReadOnly () && AST ::isMutation ($ op ->operation , $ doc )) {
4141 throw new UserError ("Cannot execute mutation in read-only context " );
4242 }
4343
@@ -133,47 +133,35 @@ public function resolveValidationRules(ServerConfig $config, OperationParams $pa
133133 return $ validationRules ;
134134 }
135135
136-
137136 /**
138137 * Parses HTTP request and returns GraphQL QueryParams contained in this request.
139138 * For batched requests it returns an array of QueryParams.
140139 *
141- * @return OperationParams|OperationParams[]
142- */
143- public function parseHttpRequest ()
144- {
145- list ($ parsedBody , $ isReadonly ) = $ this ->parseRawBody ();
146- return $ this ->toOperationParams ($ parsedBody , $ isReadonly );
147- }
148-
149- /**
150- * Extracts parsed body and readonly flag from HTTP request
140+ * This function doesn't check validity of these params.
151141 *
152142 * If $readRawBodyFn argument is not provided - will attempt to read raw request body from php://input stream
153143 *
154144 * @param callable|null $readRawBodyFn
155- * @return array
145+ * @return OperationParams|OperationParams[]
156146 */
157- public function parseRawBody (callable $ readRawBodyFn = null )
147+ public function parseHttpRequest (callable $ readRawBodyFn = null )
158148 {
159149 $ method = isset ($ _SERVER ['REQUEST_METHOD ' ]) ? $ _SERVER ['REQUEST_METHOD ' ] : null ;
160150
161151 if ($ method === 'GET ' ) {
162- $ isReadonly = true ;
163152 $ request = array_change_key_case ($ _GET );
164153
165154 if (isset ($ request ['query ' ]) || isset ($ request ['queryid ' ]) || isset ($ request ['documentid ' ])) {
166- $ body = $ _GET ;
155+ $ result = OperationParams:: create ( $ _GET , true ) ;
167156 } else {
168157 throw new UserError ('Cannot execute GET request without "query" or "queryId" parameter ' );
169158 }
170159 } else if ($ method === 'POST ' ) {
171- $ isReadonly = false ;
172160 $ contentType = isset ($ _SERVER ['CONTENT_TYPE ' ]) ? $ _SERVER ['CONTENT_TYPE ' ] : null ;
173161
174162 if (stripos ($ contentType , 'application/graphql ' ) !== false ) {
175163 $ rawBody = $ readRawBodyFn ? $ readRawBodyFn () : $ this ->readRawBody ();
176- $ body = ['query ' => $ rawBody ?: '' ];
164+ $ result = OperationParams:: create ( ['query ' => $ rawBody ?: '' ]) ;
177165 } else if (stripos ($ contentType , 'application/json ' ) !== false ) {
178166 $ rawBody = $ readRawBodyFn ? $ readRawBodyFn () : $ this ->readRawBody ();
179167 $ body = json_decode ($ rawBody ?: '' , true );
@@ -187,8 +175,17 @@ public function parseRawBody(callable $readRawBodyFn = null)
187175 Utils::printSafeJson ($ body )
188176 );
189177 }
178+ if (isset ($ body [0 ])) {
179+ $ result = [];
180+ foreach ($ body as $ index => $ entry ) {
181+ $ op = OperationParams::create ($ entry , true );
182+ $ result [] = $ op ;
183+ }
184+ } else {
185+ $ result = OperationParams::create ($ body );
186+ }
190187 } else if (stripos ($ contentType , 'application/x-www-form-urlencoded ' ) !== false ) {
191- $ body = $ _POST ;
188+ $ result = OperationParams:: create ( $ _POST ) ;
192189 } else if (null === $ contentType ) {
193190 throw new UserError ('Missing "Content-Type" header ' );
194191 } else {
@@ -197,92 +194,92 @@ public function parseRawBody(callable $readRawBodyFn = null)
197194 } else {
198195 throw new UserError ('HTTP Method " ' . $ method . '" is not supported ' , 405 );
199196 }
200- return [
201- $ body ,
202- $ isReadonly
203- ];
197+ return $ result ;
204198 }
205199
206200 /**
207- * Converts parsed body to OperationParams (or list of OperationParams for batched request)
208- *
209- * @param $parsedBody
210- * @param $isReadonly
211- * @return OperationParams|OperationParams[]
201+ * @return bool|string
212202 */
213- public function toOperationParams ( $ parsedBody , $ isReadonly )
203+ public function readRawBody ( )
214204 {
215- $ assertValid = function (OperationParams $ opParams , $ queryNum = null ) {
216- $ errors = $ opParams ->validate ();
217- if (!empty ($ errors [0 ])) {
218- $ err = $ queryNum ? "Error in query # $ queryNum: {$ errors [0 ]}" : $ errors [0 ];
219- throw new UserError ($ err );
220- }
221- };
222-
223- if (isset ($ parsedBody [0 ])) {
224- // Batched query
225- $ result = [];
226- foreach ($ parsedBody as $ index => $ entry ) {
227- $ op = OperationParams::create ($ entry , $ isReadonly );
228- $ assertValid ($ op , $ index );
229- $ result [] = $ op ;
230- }
231- } else {
232- $ result = OperationParams::create ($ parsedBody , $ isReadonly );
233- $ assertValid ($ result );
234- }
235- return $ result ;
205+ return file_get_contents ('php://input ' );
236206 }
237207
238208 /**
239- * @return bool|string
209+ * Checks validity of operation params and returns array of errors (empty array when params are valid)
210+ *
211+ * @param OperationParams $params
212+ * @return array
240213 */
241- public function readRawBody ( )
214+ public function validateOperationParams ( OperationParams $ params )
242215 {
243- return file_get_contents ('php://input ' );
216+ $ errors = [];
217+ if (!$ params ->query && !$ params ->queryId ) {
218+ $ errors [] = 'GraphQL Request must include at least one of those two parameters: "query" or "queryId" ' ;
219+ }
220+ if ($ params ->query && $ params ->queryId ) {
221+ $ errors [] = 'GraphQL Request parameters "query" and "queryId" are mutually exclusive ' ;
222+ }
223+
224+ if ($ params ->query !== null && (!is_string ($ params ->query ) || empty ($ params ->query ))) {
225+ $ errors [] = 'GraphQL Request parameter "query" must be string, but got ' .
226+ Utils::printSafeJson ($ params ->query );
227+ }
228+ if ($ params ->queryId !== null && (!is_string ($ params ->queryId ) || empty ($ params ->queryId ))) {
229+ $ errors [] = 'GraphQL Request parameter "queryId" must be string, but got ' .
230+ Utils::printSafeJson ($ params ->queryId );
231+ }
232+
233+ if ($ params ->operation !== null && (!is_string ($ params ->operation ) || empty ($ params ->operation ))) {
234+ $ errors [] = 'GraphQL Request parameter "operation" must be string, but got ' .
235+ Utils::printSafeJson ($ params ->operation );
236+ }
237+ if ($ params ->variables !== null && (!is_array ($ params ->variables ) || isset ($ params ->variables [0 ]))) {
238+ $ errors [] = 'GraphQL Request parameter "variables" must be object, but got ' .
239+ Utils::printSafeJson ($ params ->variables );
240+ }
241+ return $ errors ;
244242 }
245243
246244 /**
247245 * Assertion to check that parsed body is valid instance of OperationParams (or array of instances)
248246 *
249- * @param $method
250- * @param $parsedBody
247+ * @param OperationParams|OperationParams[] $parsedBody
248+ * @throws InvariantViolation
249+ * @throws UserError
251250 */
252- public function assertBodyIsParsedProperly ( $ method , $ parsedBody )
251+ public function assertValidRequest ( $ parsedBody )
253252 {
254253 if (is_array ($ parsedBody )) {
255254 foreach ($ parsedBody as $ index => $ entry ) {
256255 if (!$ entry instanceof OperationParams) {
257256 throw new InvariantViolation (sprintf (
258- '%s expects instance of %s or array of instances. Got invalid array where entry at position %d is %s ' ,
259- $ method ,
257+ 'GraphQL Server: Parsed http request must be an instance of %s or array of such instances, ' .
258+ ' but got invalid array where entry at position %d is %s ' ,
260259 OperationParams::class,
261260 $ index ,
262261 Utils::printSafe ($ entry )
263262 ));
264263 }
265- $ errors = $ entry ->validate ();
264+
265+ $ errors = $ this ->validateOperationParams ($ entry );
266266
267267 if (!empty ($ errors [0 ])) {
268268 $ err = $ index ? "Error in query # $ index: {$ errors [0 ]}" : $ errors [0 ];
269- throw new InvariantViolation ($ err );
269+ throw new UserError ($ err );
270270 }
271271 }
272- }
273-
274- if ($ parsedBody instanceof OperationParams) {
275- $ errors = $ parsedBody ->validate ();
272+ } else if ($ parsedBody instanceof OperationParams) {
273+ $ errors = $ this ->validateOperationParams ($ parsedBody );
276274 if (!empty ($ errors [0 ])) {
277- throw new InvariantViolation ($ errors [0 ]);
275+ throw new UserError ($ errors [0 ]);
278276 }
277+ } else {
278+ throw new InvariantViolation (sprintf (
279+ 'GraphQL Server: Parsed http request must be an instance of %s or array of such instances, but got %s ' ,
280+ OperationParams::class,
281+ Utils::printSafe ($ parsedBody )
282+ ));
279283 }
280-
281- throw new InvariantViolation (sprintf (
282- '%s expects instance of %s or array of instances, but got %s ' ,
283- $ method ,
284- OperationParams::class,
285- Utils::printSafe ($ parsedBody )
286- ));
287284 }
288285}
0 commit comments