├── .gitignore ├── .phpspec.yml ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── lib ├── Lexer.php └── Parser.php └── spec ├── LexerSpec.php └── ParserSpec.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /bin/ 3 | -------------------------------------------------------------------------------- /.phpspec.yml: -------------------------------------------------------------------------------- 1 | suites: 2 | default: 3 | namespace: GraphQL 4 | psr4_prefix: GraphQL 5 | src_path: lib 6 | formatter.name: pretty 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Rifat Nabi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL PHP 2 | 3 | ``` 4 | $ bin/phpspec run --config .phpspec.yml 5 | ``` 6 | 7 | Or, you can run `composer test` too. 8 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "torifat/graphql-php", 3 | "description": "A GraphQL implementation for PHP", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Rifat Nabi", 9 | "email": "to.rifat@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=5.6.0", 14 | "doctrine/lexer": "^1.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "GraphQL\\": "lib/" 19 | } 20 | }, 21 | "require-dev": { 22 | "phpspec/phpspec": "^2.2" 23 | }, 24 | "config": { 25 | "bin-dir": "bin" 26 | }, 27 | "scripts": { 28 | "test": "bin/phpspec run --config .phpspec.yml" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "129dcb58b79a8008ec1434c1ad4565e7", 8 | "packages": [ 9 | { 10 | "name": "doctrine/lexer", 11 | "version": "v1.0.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/doctrine/lexer.git", 15 | "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", 20 | "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.2" 25 | }, 26 | "type": "library", 27 | "extra": { 28 | "branch-alias": { 29 | "dev-master": "1.0.x-dev" 30 | } 31 | }, 32 | "autoload": { 33 | "psr-0": { 34 | "Doctrine\\Common\\Lexer\\": "lib/" 35 | } 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "Roman Borschel", 44 | "email": "roman@code-factory.org" 45 | }, 46 | { 47 | "name": "Guilherme Blanco", 48 | "email": "guilhermeblanco@gmail.com" 49 | }, 50 | { 51 | "name": "Johannes Schmitt", 52 | "email": "schmittjoh@gmail.com" 53 | } 54 | ], 55 | "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", 56 | "homepage": "http://www.doctrine-project.org", 57 | "keywords": [ 58 | "lexer", 59 | "parser" 60 | ], 61 | "time": "2014-09-09 13:34:57" 62 | } 63 | ], 64 | "packages-dev": [ 65 | { 66 | "name": "doctrine/instantiator", 67 | "version": "1.0.5", 68 | "source": { 69 | "type": "git", 70 | "url": "https://github.com/doctrine/instantiator.git", 71 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 72 | }, 73 | "dist": { 74 | "type": "zip", 75 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 76 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 77 | "shasum": "" 78 | }, 79 | "require": { 80 | "php": ">=5.3,<8.0-DEV" 81 | }, 82 | "require-dev": { 83 | "athletic/athletic": "~0.1.8", 84 | "ext-pdo": "*", 85 | "ext-phar": "*", 86 | "phpunit/phpunit": "~4.0", 87 | "squizlabs/php_codesniffer": "~2.0" 88 | }, 89 | "type": "library", 90 | "extra": { 91 | "branch-alias": { 92 | "dev-master": "1.0.x-dev" 93 | } 94 | }, 95 | "autoload": { 96 | "psr-4": { 97 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 98 | } 99 | }, 100 | "notification-url": "https://packagist.org/downloads/", 101 | "license": [ 102 | "MIT" 103 | ], 104 | "authors": [ 105 | { 106 | "name": "Marco Pivetta", 107 | "email": "ocramius@gmail.com", 108 | "homepage": "http://ocramius.github.com/" 109 | } 110 | ], 111 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 112 | "homepage": "https://github.com/doctrine/instantiator", 113 | "keywords": [ 114 | "constructor", 115 | "instantiate" 116 | ], 117 | "time": "2015-06-14 21:17:01" 118 | }, 119 | { 120 | "name": "phpdocumentor/reflection-docblock", 121 | "version": "2.0.4", 122 | "source": { 123 | "type": "git", 124 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 125 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" 126 | }, 127 | "dist": { 128 | "type": "zip", 129 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", 130 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", 131 | "shasum": "" 132 | }, 133 | "require": { 134 | "php": ">=5.3.3" 135 | }, 136 | "require-dev": { 137 | "phpunit/phpunit": "~4.0" 138 | }, 139 | "suggest": { 140 | "dflydev/markdown": "~1.0", 141 | "erusev/parsedown": "~1.0" 142 | }, 143 | "type": "library", 144 | "extra": { 145 | "branch-alias": { 146 | "dev-master": "2.0.x-dev" 147 | } 148 | }, 149 | "autoload": { 150 | "psr-0": { 151 | "phpDocumentor": [ 152 | "src/" 153 | ] 154 | } 155 | }, 156 | "notification-url": "https://packagist.org/downloads/", 157 | "license": [ 158 | "MIT" 159 | ], 160 | "authors": [ 161 | { 162 | "name": "Mike van Riel", 163 | "email": "mike.vanriel@naenius.com" 164 | } 165 | ], 166 | "time": "2015-02-03 12:10:50" 167 | }, 168 | { 169 | "name": "phpspec/php-diff", 170 | "version": "v1.0.2", 171 | "source": { 172 | "type": "git", 173 | "url": "https://github.com/phpspec/php-diff.git", 174 | "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a" 175 | }, 176 | "dist": { 177 | "type": "zip", 178 | "url": "https://api.github.com/repos/phpspec/php-diff/zipball/30e103d19519fe678ae64a60d77884ef3d71b28a", 179 | "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a", 180 | "shasum": "" 181 | }, 182 | "type": "library", 183 | "autoload": { 184 | "psr-0": { 185 | "Diff": "lib/" 186 | } 187 | }, 188 | "notification-url": "https://packagist.org/downloads/", 189 | "license": [ 190 | "BSD-3-Clause" 191 | ], 192 | "authors": [ 193 | { 194 | "name": "Chris Boulton", 195 | "homepage": "http://github.com/chrisboulton", 196 | "role": "Original developer" 197 | } 198 | ], 199 | "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", 200 | "time": "2013-11-01 13:02:21" 201 | }, 202 | { 203 | "name": "phpspec/phpspec", 204 | "version": "2.2.1", 205 | "source": { 206 | "type": "git", 207 | "url": "https://github.com/phpspec/phpspec.git", 208 | "reference": "e9a40577323e67f1de2e214abf32976a0352d8f8" 209 | }, 210 | "dist": { 211 | "type": "zip", 212 | "url": "https://api.github.com/repos/phpspec/phpspec/zipball/e9a40577323e67f1de2e214abf32976a0352d8f8", 213 | "reference": "e9a40577323e67f1de2e214abf32976a0352d8f8", 214 | "shasum": "" 215 | }, 216 | "require": { 217 | "doctrine/instantiator": "^1.0.1", 218 | "php": ">=5.3.3", 219 | "phpspec/php-diff": "~1.0.0", 220 | "phpspec/prophecy": "~1.4", 221 | "sebastian/exporter": "~1.0", 222 | "symfony/console": "~2.3", 223 | "symfony/event-dispatcher": "~2.1", 224 | "symfony/finder": "~2.1", 225 | "symfony/process": "~2.1", 226 | "symfony/yaml": "~2.1" 227 | }, 228 | "require-dev": { 229 | "behat/behat": "^3.0.11", 230 | "bossa/phpspec2-expect": "~1.0", 231 | "phpunit/phpunit": "~4.4", 232 | "symfony/filesystem": "~2.1", 233 | "symfony/process": "~2.1" 234 | }, 235 | "suggest": { 236 | "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters" 237 | }, 238 | "bin": [ 239 | "bin/phpspec" 240 | ], 241 | "type": "library", 242 | "extra": { 243 | "branch-alias": { 244 | "dev-master": "2.2.x-dev" 245 | } 246 | }, 247 | "autoload": { 248 | "psr-0": { 249 | "PhpSpec": "src/" 250 | } 251 | }, 252 | "notification-url": "https://packagist.org/downloads/", 253 | "license": [ 254 | "MIT" 255 | ], 256 | "authors": [ 257 | { 258 | "name": "Konstantin Kudryashov", 259 | "email": "ever.zet@gmail.com", 260 | "homepage": "http://everzet.com" 261 | }, 262 | { 263 | "name": "Marcello Duarte", 264 | "homepage": "http://marcelloduarte.net/" 265 | } 266 | ], 267 | "description": "Specification-oriented BDD framework for PHP 5.3+", 268 | "homepage": "http://phpspec.net/", 269 | "keywords": [ 270 | "BDD", 271 | "SpecBDD", 272 | "TDD", 273 | "spec", 274 | "specification", 275 | "testing", 276 | "tests" 277 | ], 278 | "time": "2015-05-30 15:21:40" 279 | }, 280 | { 281 | "name": "phpspec/prophecy", 282 | "version": "v1.4.1", 283 | "source": { 284 | "type": "git", 285 | "url": "https://github.com/phpspec/prophecy.git", 286 | "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373" 287 | }, 288 | "dist": { 289 | "type": "zip", 290 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", 291 | "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", 292 | "shasum": "" 293 | }, 294 | "require": { 295 | "doctrine/instantiator": "^1.0.2", 296 | "phpdocumentor/reflection-docblock": "~2.0", 297 | "sebastian/comparator": "~1.1" 298 | }, 299 | "require-dev": { 300 | "phpspec/phpspec": "~2.0" 301 | }, 302 | "type": "library", 303 | "extra": { 304 | "branch-alias": { 305 | "dev-master": "1.4.x-dev" 306 | } 307 | }, 308 | "autoload": { 309 | "psr-0": { 310 | "Prophecy\\": "src/" 311 | } 312 | }, 313 | "notification-url": "https://packagist.org/downloads/", 314 | "license": [ 315 | "MIT" 316 | ], 317 | "authors": [ 318 | { 319 | "name": "Konstantin Kudryashov", 320 | "email": "ever.zet@gmail.com", 321 | "homepage": "http://everzet.com" 322 | }, 323 | { 324 | "name": "Marcello Duarte", 325 | "email": "marcello.duarte@gmail.com" 326 | } 327 | ], 328 | "description": "Highly opinionated mocking framework for PHP 5.3+", 329 | "homepage": "https://github.com/phpspec/prophecy", 330 | "keywords": [ 331 | "Double", 332 | "Dummy", 333 | "fake", 334 | "mock", 335 | "spy", 336 | "stub" 337 | ], 338 | "time": "2015-04-27 22:15:08" 339 | }, 340 | { 341 | "name": "sebastian/comparator", 342 | "version": "1.1.1", 343 | "source": { 344 | "type": "git", 345 | "url": "https://github.com/sebastianbergmann/comparator.git", 346 | "reference": "1dd8869519a225f7f2b9eb663e225298fade819e" 347 | }, 348 | "dist": { 349 | "type": "zip", 350 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e", 351 | "reference": "1dd8869519a225f7f2b9eb663e225298fade819e", 352 | "shasum": "" 353 | }, 354 | "require": { 355 | "php": ">=5.3.3", 356 | "sebastian/diff": "~1.2", 357 | "sebastian/exporter": "~1.2" 358 | }, 359 | "require-dev": { 360 | "phpunit/phpunit": "~4.4" 361 | }, 362 | "type": "library", 363 | "extra": { 364 | "branch-alias": { 365 | "dev-master": "1.1.x-dev" 366 | } 367 | }, 368 | "autoload": { 369 | "classmap": [ 370 | "src/" 371 | ] 372 | }, 373 | "notification-url": "https://packagist.org/downloads/", 374 | "license": [ 375 | "BSD-3-Clause" 376 | ], 377 | "authors": [ 378 | { 379 | "name": "Jeff Welch", 380 | "email": "whatthejeff@gmail.com" 381 | }, 382 | { 383 | "name": "Volker Dusch", 384 | "email": "github@wallbash.com" 385 | }, 386 | { 387 | "name": "Bernhard Schussek", 388 | "email": "bschussek@2bepublished.at" 389 | }, 390 | { 391 | "name": "Sebastian Bergmann", 392 | "email": "sebastian@phpunit.de" 393 | } 394 | ], 395 | "description": "Provides the functionality to compare PHP values for equality", 396 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 397 | "keywords": [ 398 | "comparator", 399 | "compare", 400 | "equality" 401 | ], 402 | "time": "2015-01-29 16:28:08" 403 | }, 404 | { 405 | "name": "sebastian/diff", 406 | "version": "1.3.0", 407 | "source": { 408 | "type": "git", 409 | "url": "https://github.com/sebastianbergmann/diff.git", 410 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" 411 | }, 412 | "dist": { 413 | "type": "zip", 414 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", 415 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", 416 | "shasum": "" 417 | }, 418 | "require": { 419 | "php": ">=5.3.3" 420 | }, 421 | "require-dev": { 422 | "phpunit/phpunit": "~4.2" 423 | }, 424 | "type": "library", 425 | "extra": { 426 | "branch-alias": { 427 | "dev-master": "1.3-dev" 428 | } 429 | }, 430 | "autoload": { 431 | "classmap": [ 432 | "src/" 433 | ] 434 | }, 435 | "notification-url": "https://packagist.org/downloads/", 436 | "license": [ 437 | "BSD-3-Clause" 438 | ], 439 | "authors": [ 440 | { 441 | "name": "Kore Nordmann", 442 | "email": "mail@kore-nordmann.de" 443 | }, 444 | { 445 | "name": "Sebastian Bergmann", 446 | "email": "sebastian@phpunit.de" 447 | } 448 | ], 449 | "description": "Diff implementation", 450 | "homepage": "http://www.github.com/sebastianbergmann/diff", 451 | "keywords": [ 452 | "diff" 453 | ], 454 | "time": "2015-02-22 15:13:53" 455 | }, 456 | { 457 | "name": "sebastian/exporter", 458 | "version": "1.2.0", 459 | "source": { 460 | "type": "git", 461 | "url": "https://github.com/sebastianbergmann/exporter.git", 462 | "reference": "84839970d05254c73cde183a721c7af13aede943" 463 | }, 464 | "dist": { 465 | "type": "zip", 466 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", 467 | "reference": "84839970d05254c73cde183a721c7af13aede943", 468 | "shasum": "" 469 | }, 470 | "require": { 471 | "php": ">=5.3.3", 472 | "sebastian/recursion-context": "~1.0" 473 | }, 474 | "require-dev": { 475 | "phpunit/phpunit": "~4.4" 476 | }, 477 | "type": "library", 478 | "extra": { 479 | "branch-alias": { 480 | "dev-master": "1.2.x-dev" 481 | } 482 | }, 483 | "autoload": { 484 | "classmap": [ 485 | "src/" 486 | ] 487 | }, 488 | "notification-url": "https://packagist.org/downloads/", 489 | "license": [ 490 | "BSD-3-Clause" 491 | ], 492 | "authors": [ 493 | { 494 | "name": "Jeff Welch", 495 | "email": "whatthejeff@gmail.com" 496 | }, 497 | { 498 | "name": "Volker Dusch", 499 | "email": "github@wallbash.com" 500 | }, 501 | { 502 | "name": "Bernhard Schussek", 503 | "email": "bschussek@2bepublished.at" 504 | }, 505 | { 506 | "name": "Sebastian Bergmann", 507 | "email": "sebastian@phpunit.de" 508 | }, 509 | { 510 | "name": "Adam Harvey", 511 | "email": "aharvey@php.net" 512 | } 513 | ], 514 | "description": "Provides the functionality to export PHP variables for visualization", 515 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 516 | "keywords": [ 517 | "export", 518 | "exporter" 519 | ], 520 | "time": "2015-01-27 07:23:06" 521 | }, 522 | { 523 | "name": "sebastian/recursion-context", 524 | "version": "1.0.0", 525 | "source": { 526 | "type": "git", 527 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 528 | "reference": "3989662bbb30a29d20d9faa04a846af79b276252" 529 | }, 530 | "dist": { 531 | "type": "zip", 532 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", 533 | "reference": "3989662bbb30a29d20d9faa04a846af79b276252", 534 | "shasum": "" 535 | }, 536 | "require": { 537 | "php": ">=5.3.3" 538 | }, 539 | "require-dev": { 540 | "phpunit/phpunit": "~4.4" 541 | }, 542 | "type": "library", 543 | "extra": { 544 | "branch-alias": { 545 | "dev-master": "1.0.x-dev" 546 | } 547 | }, 548 | "autoload": { 549 | "classmap": [ 550 | "src/" 551 | ] 552 | }, 553 | "notification-url": "https://packagist.org/downloads/", 554 | "license": [ 555 | "BSD-3-Clause" 556 | ], 557 | "authors": [ 558 | { 559 | "name": "Jeff Welch", 560 | "email": "whatthejeff@gmail.com" 561 | }, 562 | { 563 | "name": "Sebastian Bergmann", 564 | "email": "sebastian@phpunit.de" 565 | }, 566 | { 567 | "name": "Adam Harvey", 568 | "email": "aharvey@php.net" 569 | } 570 | ], 571 | "description": "Provides functionality to recursively process PHP variables", 572 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 573 | "time": "2015-01-24 09:48:32" 574 | }, 575 | { 576 | "name": "symfony/console", 577 | "version": "v2.7.2", 578 | "source": { 579 | "type": "git", 580 | "url": "https://github.com/symfony/Console.git", 581 | "reference": "8cf484449130cabfd98dcb4694ca9945802a21ed" 582 | }, 583 | "dist": { 584 | "type": "zip", 585 | "url": "https://api.github.com/repos/symfony/Console/zipball/8cf484449130cabfd98dcb4694ca9945802a21ed", 586 | "reference": "8cf484449130cabfd98dcb4694ca9945802a21ed", 587 | "shasum": "" 588 | }, 589 | "require": { 590 | "php": ">=5.3.9" 591 | }, 592 | "require-dev": { 593 | "psr/log": "~1.0", 594 | "symfony/event-dispatcher": "~2.1", 595 | "symfony/phpunit-bridge": "~2.7", 596 | "symfony/process": "~2.1" 597 | }, 598 | "suggest": { 599 | "psr/log": "For using the console logger", 600 | "symfony/event-dispatcher": "", 601 | "symfony/process": "" 602 | }, 603 | "type": "library", 604 | "extra": { 605 | "branch-alias": { 606 | "dev-master": "2.7-dev" 607 | } 608 | }, 609 | "autoload": { 610 | "psr-4": { 611 | "Symfony\\Component\\Console\\": "" 612 | } 613 | }, 614 | "notification-url": "https://packagist.org/downloads/", 615 | "license": [ 616 | "MIT" 617 | ], 618 | "authors": [ 619 | { 620 | "name": "Fabien Potencier", 621 | "email": "fabien@symfony.com" 622 | }, 623 | { 624 | "name": "Symfony Community", 625 | "homepage": "https://symfony.com/contributors" 626 | } 627 | ], 628 | "description": "Symfony Console Component", 629 | "homepage": "https://symfony.com", 630 | "time": "2015-07-09 16:07:40" 631 | }, 632 | { 633 | "name": "symfony/event-dispatcher", 634 | "version": "v2.7.2", 635 | "source": { 636 | "type": "git", 637 | "url": "https://github.com/symfony/EventDispatcher.git", 638 | "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3" 639 | }, 640 | "dist": { 641 | "type": "zip", 642 | "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/9310b5f9a87ec2ea75d20fec0b0017c77c66dac3", 643 | "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3", 644 | "shasum": "" 645 | }, 646 | "require": { 647 | "php": ">=5.3.9" 648 | }, 649 | "require-dev": { 650 | "psr/log": "~1.0", 651 | "symfony/config": "~2.0,>=2.0.5", 652 | "symfony/dependency-injection": "~2.6", 653 | "symfony/expression-language": "~2.6", 654 | "symfony/phpunit-bridge": "~2.7", 655 | "symfony/stopwatch": "~2.3" 656 | }, 657 | "suggest": { 658 | "symfony/dependency-injection": "", 659 | "symfony/http-kernel": "" 660 | }, 661 | "type": "library", 662 | "extra": { 663 | "branch-alias": { 664 | "dev-master": "2.7-dev" 665 | } 666 | }, 667 | "autoload": { 668 | "psr-4": { 669 | "Symfony\\Component\\EventDispatcher\\": "" 670 | } 671 | }, 672 | "notification-url": "https://packagist.org/downloads/", 673 | "license": [ 674 | "MIT" 675 | ], 676 | "authors": [ 677 | { 678 | "name": "Fabien Potencier", 679 | "email": "fabien@symfony.com" 680 | }, 681 | { 682 | "name": "Symfony Community", 683 | "homepage": "https://symfony.com/contributors" 684 | } 685 | ], 686 | "description": "Symfony EventDispatcher Component", 687 | "homepage": "https://symfony.com", 688 | "time": "2015-06-18 19:21:56" 689 | }, 690 | { 691 | "name": "symfony/finder", 692 | "version": "v2.7.2", 693 | "source": { 694 | "type": "git", 695 | "url": "https://github.com/symfony/Finder.git", 696 | "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4" 697 | }, 698 | "dist": { 699 | "type": "zip", 700 | "url": "https://api.github.com/repos/symfony/Finder/zipball/ae0f363277485094edc04c9f3cbe595b183b78e4", 701 | "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4", 702 | "shasum": "" 703 | }, 704 | "require": { 705 | "php": ">=5.3.9" 706 | }, 707 | "require-dev": { 708 | "symfony/phpunit-bridge": "~2.7" 709 | }, 710 | "type": "library", 711 | "extra": { 712 | "branch-alias": { 713 | "dev-master": "2.7-dev" 714 | } 715 | }, 716 | "autoload": { 717 | "psr-4": { 718 | "Symfony\\Component\\Finder\\": "" 719 | } 720 | }, 721 | "notification-url": "https://packagist.org/downloads/", 722 | "license": [ 723 | "MIT" 724 | ], 725 | "authors": [ 726 | { 727 | "name": "Fabien Potencier", 728 | "email": "fabien@symfony.com" 729 | }, 730 | { 731 | "name": "Symfony Community", 732 | "homepage": "https://symfony.com/contributors" 733 | } 734 | ], 735 | "description": "Symfony Finder Component", 736 | "homepage": "https://symfony.com", 737 | "time": "2015-07-09 16:07:40" 738 | }, 739 | { 740 | "name": "symfony/process", 741 | "version": "v2.7.2", 742 | "source": { 743 | "type": "git", 744 | "url": "https://github.com/symfony/Process.git", 745 | "reference": "48aeb0e48600321c272955132d7606ab0a49adb3" 746 | }, 747 | "dist": { 748 | "type": "zip", 749 | "url": "https://api.github.com/repos/symfony/Process/zipball/48aeb0e48600321c272955132d7606ab0a49adb3", 750 | "reference": "48aeb0e48600321c272955132d7606ab0a49adb3", 751 | "shasum": "" 752 | }, 753 | "require": { 754 | "php": ">=5.3.9" 755 | }, 756 | "require-dev": { 757 | "symfony/phpunit-bridge": "~2.7" 758 | }, 759 | "type": "library", 760 | "extra": { 761 | "branch-alias": { 762 | "dev-master": "2.7-dev" 763 | } 764 | }, 765 | "autoload": { 766 | "psr-4": { 767 | "Symfony\\Component\\Process\\": "" 768 | } 769 | }, 770 | "notification-url": "https://packagist.org/downloads/", 771 | "license": [ 772 | "MIT" 773 | ], 774 | "authors": [ 775 | { 776 | "name": "Fabien Potencier", 777 | "email": "fabien@symfony.com" 778 | }, 779 | { 780 | "name": "Symfony Community", 781 | "homepage": "https://symfony.com/contributors" 782 | } 783 | ], 784 | "description": "Symfony Process Component", 785 | "homepage": "https://symfony.com", 786 | "time": "2015-07-01 11:25:50" 787 | }, 788 | { 789 | "name": "symfony/yaml", 790 | "version": "v2.7.2", 791 | "source": { 792 | "type": "git", 793 | "url": "https://github.com/symfony/Yaml.git", 794 | "reference": "4bfbe0ed3909bfddd75b70c094391ec1f142f860" 795 | }, 796 | "dist": { 797 | "type": "zip", 798 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/4bfbe0ed3909bfddd75b70c094391ec1f142f860", 799 | "reference": "4bfbe0ed3909bfddd75b70c094391ec1f142f860", 800 | "shasum": "" 801 | }, 802 | "require": { 803 | "php": ">=5.3.9" 804 | }, 805 | "require-dev": { 806 | "symfony/phpunit-bridge": "~2.7" 807 | }, 808 | "type": "library", 809 | "extra": { 810 | "branch-alias": { 811 | "dev-master": "2.7-dev" 812 | } 813 | }, 814 | "autoload": { 815 | "psr-4": { 816 | "Symfony\\Component\\Yaml\\": "" 817 | } 818 | }, 819 | "notification-url": "https://packagist.org/downloads/", 820 | "license": [ 821 | "MIT" 822 | ], 823 | "authors": [ 824 | { 825 | "name": "Fabien Potencier", 826 | "email": "fabien@symfony.com" 827 | }, 828 | { 829 | "name": "Symfony Community", 830 | "homepage": "https://symfony.com/contributors" 831 | } 832 | ], 833 | "description": "Symfony Yaml Component", 834 | "homepage": "https://symfony.com", 835 | "time": "2015-07-01 11:25:50" 836 | } 837 | ], 838 | "aliases": [], 839 | "minimum-stability": "stable", 840 | "stability-flags": [], 841 | "prefer-stable": false, 842 | "prefer-lowest": false, 843 | "platform": { 844 | "php": ">=5.6.0" 845 | }, 846 | "platform-dev": [] 847 | } 848 | -------------------------------------------------------------------------------- /lib/Lexer.php: -------------------------------------------------------------------------------- 1 | self::BANG, 34 | '$' => self::DOLLAR, 35 | '(' => self::PAREN_L, 36 | ')' => self::PAREN_R, 37 | '...' => self::SPREAD, 38 | ':' => self::COLON, 39 | '=' => self::EQUALS, 40 | '@' => self::AT, 41 | '[' => self::BRACKET_L, 42 | ']' => self::BRACKET_R, 43 | '{' => self::BRACE_L, 44 | '|' => self::PIPE, 45 | '}' => self::BRACE_R, 46 | 47 | ]; 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | protected function getCatchablePatterns() 53 | { 54 | return [ 55 | // Numbers 56 | '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?', 57 | // String with quote 58 | '"(?:\\"|.)*"', 59 | '[\w-]+', 60 | ]; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | protected function getNonCatchablePatterns() 67 | { 68 | return [ 69 | // Skip whitespace 70 | '[\s,]+', 71 | // Skip comments 72 | '#.*' 73 | ]; 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | */ 79 | protected function getType(&$value) 80 | { 81 | if (isset($this->mapping[$value])) { 82 | $type = $this->mapping[$value]; 83 | $value = null; 84 | return $type; 85 | } 86 | 87 | if($this->isName($value)) { 88 | return self::NAME; 89 | } 90 | 91 | if (is_numeric($value)) { 92 | $value = +$value; 93 | return (is_float($value)) ? self::FLOAT : self::INT; 94 | } 95 | 96 | if ($value[0] === '"') { 97 | if ($this->isValidString($value)) { 98 | // Replace escape character 99 | $value = str_replace('\"', '"', $value); 100 | // Replace escape character 101 | $value = str_replace('\\\\', '\\', $value); 102 | // Remove quotes 103 | $value = substr($value, 1, -1); 104 | return self::STRING; 105 | } 106 | else { 107 | // TODO: Unterminated string exception 108 | } 109 | } 110 | 111 | trigger_error("Unexpected character ${value}", E_WARNING); 112 | } 113 | 114 | /** 115 | * @param $text 116 | * @return boolean 117 | */ 118 | protected function isName($text) 119 | { 120 | return preg_match('/^[_A-Za-z][_0-9A-Za-z]*$/', $text); 121 | } 122 | 123 | /** 124 | * @param $text 125 | * @return boolean 126 | */ 127 | protected function isValidString($text) 128 | { 129 | return ($text[0] === '"' && $text[strlen($text) - 1] === '"'); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /lib/Parser.php: -------------------------------------------------------------------------------- 1 | lexer = new Lexer; 15 | } 16 | 17 | public function parse($input) 18 | { 19 | $this->lexer->setInput($input); 20 | 21 | $definitions = []; 22 | do { 23 | if ($this->lexer->isNextToken(Lexer::BRACE_L)) { 24 | // parseOperationDefinition 25 | } 26 | elseif ($this->lexer->isNextToken(Lexer::NAME)) { 27 | if ($this->lexer->token['value'] === 'query' || 28 | $this->lexer->token['value'] === 'mutation') { 29 | // parseOperationDefinition 30 | } 31 | elseif ($this->lexer->token['value'] === 'fragment') { 32 | // parseFragmentDefinition 33 | } 34 | else { 35 | // TODO: Unexpected lexed token 36 | } 37 | } 38 | else { 39 | // TODO: Unexpected lexed token 40 | } 41 | } while($this->lexer->moveNext()); 42 | return $definitions; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /spec/LexerSpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('GraphQL\Lexer'); 14 | } 15 | 16 | function it_skips_whitespace() 17 | { 18 | $this->setInput(<<glimpse(); 25 | $token->shouldHaveKeyWithValue('type', Lexer::NAME); 26 | $token->shouldHaveKeyWithValue('value', 'foo'); 27 | $token->shouldHaveKeyWithValue('position', 9); 28 | 29 | $this->setInput(<<glimpse(); 35 | $token->shouldHaveKeyWithValue('type', Lexer::NAME); 36 | $token->shouldHaveKeyWithValue('value', 'foo'); 37 | $token->shouldHaveKeyWithValue('position', 25); 38 | 39 | $this->setInput(",,,foo,,,"); 40 | $token = $this->glimpse(); 41 | $token->shouldHaveKeyWithValue('type', Lexer::NAME); 42 | $token->shouldHaveKeyWithValue('value', 'foo'); 43 | $token->shouldHaveKeyWithValue('position', 3); 44 | } 45 | 46 | function it_lexes_strings() 47 | { 48 | $this->setInput('"simple"'); 49 | $token = $this->glimpse(); 50 | $token->shouldHaveKeyWithValue('type', Lexer::STRING); 51 | $token->shouldHaveKeyWithValue('value', 'simple'); 52 | $token->shouldHaveKeyWithValue('position', 0); 53 | 54 | $this->setInput('" white space "'); 55 | $token = $this->glimpse(); 56 | $token->shouldHaveKeyWithValue('type', Lexer::STRING); 57 | $token->shouldHaveKeyWithValue('value', ' white space '); 58 | $token->shouldHaveKeyWithValue('position', 0); 59 | 60 | $this->setInput('"quote \\""'); 61 | $token = $this->glimpse(); 62 | $token->shouldHaveKeyWithValue('type', Lexer::STRING); 63 | $token->shouldHaveKeyWithValue('value', 'quote "'); 64 | $token->shouldHaveKeyWithValue('position', 0); 65 | 66 | $this->setInput('"escaped \\n\\r\\b\\t\\f"'); 67 | $token = $this->glimpse(); 68 | $token->shouldHaveKeyWithValue('type', Lexer::STRING); 69 | $token->shouldHaveKeyWithValue('value', 'escaped \n\r\b\t\f'); 70 | $token->shouldHaveKeyWithValue('position', 0); 71 | 72 | $this->setInput('"slashes \\\\ \\/"'); 73 | $token = $this->glimpse(); 74 | $token->shouldHaveKeyWithValue('type', Lexer::STRING); 75 | $token->shouldHaveKeyWithValue('value', 'slashes \\ \/'); 76 | $token->shouldHaveKeyWithValue('position', 0); 77 | } 78 | 79 | function it_lexes_numbers() 80 | { 81 | $this->setInput('4'); 82 | $token = $this->glimpse(); 83 | $token->shouldHaveKeyWithValue('type', Lexer::INT); 84 | $token->shouldHaveKeyWithValue('value', 4); 85 | $token->shouldHaveKeyWithValue('position', 0); 86 | 87 | $this->setInput('4.123'); 88 | $token = $this->glimpse(); 89 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 90 | $token->shouldHaveKeyWithValue('value', 4.123); 91 | $token->shouldHaveKeyWithValue('position', 0); 92 | 93 | $this->setInput('-4'); 94 | $token = $this->glimpse(); 95 | $token->shouldHaveKeyWithValue('type', Lexer::INT); 96 | $token->shouldHaveKeyWithValue('value', -4); 97 | $token->shouldHaveKeyWithValue('position', 0); 98 | 99 | $this->setInput('9'); 100 | $token = $this->glimpse(); 101 | $token->shouldHaveKeyWithValue('type', Lexer::INT); 102 | $token->shouldHaveKeyWithValue('value', 9); 103 | $token->shouldHaveKeyWithValue('position', 0); 104 | 105 | $this->setInput('0'); 106 | $token = $this->glimpse(); 107 | $token->shouldHaveKeyWithValue('type', Lexer::INT); 108 | $token->shouldHaveKeyWithValue('value', 0); 109 | $token->shouldHaveKeyWithValue('position', 0); 110 | 111 | $this->setInput('00'); 112 | $token = $this->glimpse(); 113 | $token->shouldHaveKeyWithValue('type', Lexer::INT); 114 | $token->shouldHaveKeyWithValue('value', 0); 115 | $token->shouldHaveKeyWithValue('position', 0); 116 | 117 | $this->setInput('-4.123'); 118 | $token = $this->glimpse(); 119 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 120 | $token->shouldHaveKeyWithValue('value', -4.123); 121 | $token->shouldHaveKeyWithValue('position', 0); 122 | 123 | $this->setInput('0.123'); 124 | $token = $this->glimpse(); 125 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 126 | $token->shouldHaveKeyWithValue('value', 0.123); 127 | $token->shouldHaveKeyWithValue('position', 0); 128 | 129 | $this->setInput('123e4'); 130 | $token = $this->glimpse(); 131 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 132 | $token->shouldHaveKeyWithValue('value', 123e4); 133 | $token->shouldHaveKeyWithValue('position', 0); 134 | 135 | $this->setInput('123E4'); 136 | $token = $this->glimpse(); 137 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 138 | $token->shouldHaveKeyWithValue('value', 123E4); 139 | $token->shouldHaveKeyWithValue('position', 0); 140 | 141 | $this->setInput('123e-4'); 142 | $token = $this->glimpse(); 143 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 144 | $token->shouldHaveKeyWithValue('value', 123e-4); 145 | $token->shouldHaveKeyWithValue('position', 0); 146 | 147 | $this->setInput('123e+4'); 148 | $token = $this->glimpse(); 149 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 150 | $token->shouldHaveKeyWithValue('value', 123e4); 151 | $token->shouldHaveKeyWithValue('position', 0); 152 | 153 | $this->setInput('-1.123e4'); 154 | $token = $this->glimpse(); 155 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 156 | $token->shouldHaveKeyWithValue('value', -1.123e4); 157 | $token->shouldHaveKeyWithValue('position', 0); 158 | 159 | $this->setInput('-1.123E4'); 160 | $token = $this->glimpse(); 161 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 162 | $token->shouldHaveKeyWithValue('value', -1.123E4); 163 | $token->shouldHaveKeyWithValue('position', 0); 164 | 165 | $this->setInput('-1.123e-4'); 166 | $token = $this->glimpse(); 167 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 168 | $token->shouldHaveKeyWithValue('value', -1.123e-4); 169 | $token->shouldHaveKeyWithValue('position', 0); 170 | 171 | $this->setInput('-1.123e+4'); 172 | $token = $this->glimpse(); 173 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 174 | $token->shouldHaveKeyWithValue('value', -1.123e+4); 175 | $token->shouldHaveKeyWithValue('position', 0); 176 | 177 | $this->setInput('-1.123e4567'); 178 | $token = $this->glimpse(); 179 | $token->shouldHaveKeyWithValue('type', Lexer::FLOAT); 180 | $token->shouldHaveKeyWithValue('value', -1.123e4567); 181 | $token->shouldHaveKeyWithValue('position', 0); 182 | } 183 | 184 | function it_lexes_punctuation() 185 | { 186 | $this->setInput('!'); 187 | $token = $this->glimpse(); 188 | $token->shouldHaveKeyWithValue('type', Lexer::BANG); 189 | $token->shouldHaveKeyWithValue('value', null); 190 | $token->shouldHaveKeyWithValue('position', 0); 191 | 192 | $this->setInput('$'); 193 | $token = $this->glimpse(); 194 | $token->shouldHaveKeyWithValue('type', Lexer::DOLLAR); 195 | $token->shouldHaveKeyWithValue('value', null); 196 | $token->shouldHaveKeyWithValue('position', 0); 197 | 198 | $this->setInput('('); 199 | $token = $this->glimpse(); 200 | $token->shouldHaveKeyWithValue('type', Lexer::PAREN_L); 201 | $token->shouldHaveKeyWithValue('value', null); 202 | $token->shouldHaveKeyWithValue('position', 0); 203 | 204 | $this->setInput(')'); 205 | $token = $this->glimpse(); 206 | $token->shouldHaveKeyWithValue('type', Lexer::PAREN_R); 207 | $token->shouldHaveKeyWithValue('value', null); 208 | $token->shouldHaveKeyWithValue('position', 0); 209 | 210 | $this->setInput('...'); 211 | $token = $this->glimpse(); 212 | $token->shouldHaveKeyWithValue('type', Lexer::SPREAD); 213 | $token->shouldHaveKeyWithValue('value', null); 214 | $token->shouldHaveKeyWithValue('position', 0); 215 | 216 | $this->setInput(':'); 217 | $token = $this->glimpse(); 218 | $token->shouldHaveKeyWithValue('type', Lexer::COLON); 219 | $token->shouldHaveKeyWithValue('value', null); 220 | $token->shouldHaveKeyWithValue('position', 0); 221 | 222 | $this->setInput('='); 223 | $token = $this->glimpse(); 224 | $token->shouldHaveKeyWithValue('type', Lexer::EQUALS); 225 | $token->shouldHaveKeyWithValue('value', null); 226 | $token->shouldHaveKeyWithValue('position', 0); 227 | 228 | $this->setInput('@'); 229 | $token = $this->glimpse(); 230 | $token->shouldHaveKeyWithValue('type', Lexer::AT); 231 | $token->shouldHaveKeyWithValue('value', null); 232 | $token->shouldHaveKeyWithValue('position', 0); 233 | 234 | $this->setInput('['); 235 | $token = $this->glimpse(); 236 | $token->shouldHaveKeyWithValue('type', Lexer::BRACKET_L); 237 | $token->shouldHaveKeyWithValue('value', null); 238 | $token->shouldHaveKeyWithValue('position', 0); 239 | 240 | $this->setInput(']'); 241 | $token = $this->glimpse(); 242 | $token->shouldHaveKeyWithValue('type', Lexer::BRACKET_R); 243 | $token->shouldHaveKeyWithValue('value', null); 244 | $token->shouldHaveKeyWithValue('position', 0); 245 | 246 | $this->setInput('{'); 247 | $token = $this->glimpse(); 248 | $token->shouldHaveKeyWithValue('type', Lexer::BRACE_L); 249 | $token->shouldHaveKeyWithValue('value', null); 250 | $token->shouldHaveKeyWithValue('position', 0); 251 | 252 | $this->setInput('|'); 253 | $token = $this->glimpse(); 254 | $token->shouldHaveKeyWithValue('type', Lexer::PIPE); 255 | $token->shouldHaveKeyWithValue('value', null); 256 | $token->shouldHaveKeyWithValue('position', 0); 257 | 258 | $this->setInput('}'); 259 | $token = $this->glimpse(); 260 | $token->shouldHaveKeyWithValue('type', Lexer::BRACE_R); 261 | $token->shouldHaveKeyWithValue('value', null); 262 | $token->shouldHaveKeyWithValue('position', 0); 263 | } 264 | 265 | // TODO: Handle errors in Lexer 266 | /* 267 | function it_respects_whitespace_in_errors() 268 | { 269 | $this->setInput(<<glimpse(); 276 | $token->shouldHaveKeyWithValue('type', Lexer::NAME); 277 | $token->shouldHaveKeyWithValue('value', 'foo'); 278 | $token->shouldHaveKeyWithValue('position', 9); 279 | } 280 | */ 281 | } 282 | -------------------------------------------------------------------------------- /spec/ParserSpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('GraphQL\Parser'); 13 | } 14 | 15 | function it_is_a_test() 16 | { 17 | $this->parse('{ field }'); 18 | } 19 | } 20 | --------------------------------------------------------------------------------