├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── examples ├── 01_sandbox │ └── index.php ├── 02_blog │ ├── Schema │ │ ├── BannerType.php │ │ ├── BlogSchema.php │ │ ├── ContentBlockInterface.php │ │ ├── ContentBlockUnion.php │ │ ├── DataProvider.php │ │ ├── LatestPostField.php │ │ ├── LikePostField.php │ │ ├── PostInputType.php │ │ ├── PostStatus.php │ │ └── PostType.php │ ├── index.php │ ├── router.php │ ├── schema-bootstrap.php │ └── server.sh ├── 02_blog_inline │ ├── index.php │ └── inline-schema.php ├── 03_relay_star_wars │ ├── Schema │ │ ├── FactionType.php │ │ ├── ShipType.php │ │ ├── StarWarsRelaySchema.php │ │ └── TestDataProvider.php │ ├── index.php │ ├── router.php │ ├── schema-bootstrap.php │ └── server.sh ├── 04_bookstore │ ├── DataProvider.php │ ├── Schema │ │ ├── BookStoreSchema.php │ │ ├── Field │ │ │ ├── Book │ │ │ │ └── RecentBooksField.php │ │ │ └── CategoriesField.php │ │ └── Type │ │ │ ├── AuthorType.php │ │ │ ├── BookType.php │ │ │ └── CategoryType.php │ ├── index.php │ ├── schema-bootstrap.php │ └── server.sh ├── GraphiQL │ ├── cookiejar.js │ ├── fetch.js │ ├── fetch.min.js │ ├── graphiql.css │ ├── graphiql.js │ ├── graphiql.min.js │ ├── index-js.html │ ├── index.html │ ├── react-dom.min.js │ ├── react.min.js │ └── screenshot.png ├── js-relay │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── build │ │ └── babelRelayPlugin.js │ ├── data │ │ ├── database.js │ │ ├── schema.js │ │ └── schema.json │ ├── js │ │ ├── app.js │ │ ├── components │ │ │ ├── StarWarsApp.js │ │ │ └── StarWarsShip.js │ │ ├── mutation │ │ │ └── AddShipMutation.js │ │ └── routes │ │ │ └── StarWarsAppHomeRoute.js │ ├── package.json │ ├── public │ │ └── index.html │ ├── scripts │ │ └── updateSchema.js │ └── server.js ├── js │ ├── .babelrc │ ├── index.js │ └── package.json ├── structure.sketch └── structure.xd ├── phpstan.neon ├── phpunit.xml.dist ├── src ├── Config │ ├── AbstractConfig.php │ ├── Directive │ │ └── DirectiveConfig.php │ ├── Field │ │ ├── FieldConfig.php │ │ └── InputFieldConfig.php │ ├── Object │ │ ├── EnumTypeConfig.php │ │ ├── InputObjectTypeConfig.php │ │ ├── InterfaceTypeConfig.php │ │ ├── ListTypeConfig.php │ │ ├── ObjectTypeConfig.php │ │ └── UnionTypeConfig.php │ ├── Schema │ │ └── SchemaConfig.php │ ├── Traits │ │ ├── ArgumentsAwareConfigTrait.php │ │ ├── ConfigAwareTrait.php │ │ ├── DirectivesAwareConfigTrait.php │ │ ├── FieldsAwareConfigTrait.php │ │ └── ResolvableObjectTrait.php │ └── TypeConfigInterface.php ├── Directive │ ├── Directive.php │ ├── DirectiveInterface.php │ └── DirectiveLocation.php ├── Exception │ ├── ConfigurationException.php │ ├── Interfaces │ │ ├── ExtendedExceptionInterface.php │ │ └── LocationableExceptionInterface.php │ ├── Parser │ │ ├── AbstractParserError.php │ │ ├── InvalidRequestException.php │ │ └── SyntaxErrorException.php │ ├── ResolveException.php │ └── ValidationException.php ├── Execution │ ├── Container │ │ ├── Container.php │ │ └── ContainerInterface.php │ ├── Context │ │ ├── ExecutionContext.php │ │ └── ExecutionContextInterface.php │ ├── DeferredResolver.php │ ├── DeferredResolverInterface.php │ ├── DeferredResult.php │ ├── Processor.php │ ├── Reducer.php │ ├── Request.php │ ├── ResolveInfo.php │ └── Visitor │ │ ├── AbstractQueryVisitor.php │ │ └── MaxComplexityQueryVisitor.php ├── Field │ ├── AbstractField.php │ ├── AbstractInputField.php │ ├── Field.php │ ├── FieldInterface.php │ ├── InputField.php │ └── InputFieldInterface.php ├── Introspection │ ├── DirectiveLocationType.php │ ├── DirectiveType.php │ ├── EnumValueType.php │ ├── Field │ │ ├── SchemaField.php │ │ ├── TypeDefinitionField.php │ │ └── TypesField.php │ ├── FieldType.php │ ├── InputValueType.php │ ├── QueryType.php │ ├── SchemaType.php │ └── Traits │ │ └── TypeCollectorTrait.php ├── Parser │ ├── Ast │ │ ├── AbstractAst.php │ │ ├── Argument.php │ │ ├── ArgumentValue │ │ │ ├── InputList.php │ │ │ ├── InputObject.php │ │ │ ├── Literal.php │ │ │ ├── Variable.php │ │ │ └── VariableReference.php │ │ ├── AstArgumentsTrait.php │ │ ├── AstDirectivesTrait.php │ │ ├── Directive.php │ │ ├── Field.php │ │ ├── Fragment.php │ │ ├── FragmentReference.php │ │ ├── Interfaces │ │ │ ├── FieldInterface.php │ │ │ ├── FragmentInterface.php │ │ │ ├── LocatableInterface.php │ │ │ └── ValueInterface.php │ │ ├── Mutation.php │ │ ├── Query.php │ │ └── TypedFragmentReference.php │ ├── Location.php │ ├── Parser.php │ ├── Token.php │ └── Tokenizer.php ├── Relay │ ├── Connection │ │ ├── ArrayConnection.php │ │ └── Connection.php │ ├── Fetcher │ │ ├── CallableFetcher.php │ │ └── FetcherInterface.php │ ├── Field │ │ ├── GlobalIdField.php │ │ └── NodeField.php │ ├── Node.php │ ├── NodeInterfaceType.php │ ├── RelayMutation.php │ └── Type │ │ └── PageInfoType.php ├── Schema │ ├── AbstractSchema.php │ ├── InternalSchemaMutationObject.php │ ├── InternalSchemaQueryObject.php │ └── Schema.php ├── Type │ ├── AbstractInterfaceTypeInterface.php │ ├── AbstractType.php │ ├── CompositeTypeInterface.php │ ├── Enum │ │ ├── AbstractEnumType.php │ │ └── EnumType.php │ ├── InputObject │ │ ├── AbstractInputObjectType.php │ │ └── InputObjectType.php │ ├── InputTypeInterface.php │ ├── InterfaceType │ │ ├── AbstractInterfaceType.php │ │ └── InterfaceType.php │ ├── ListType │ │ ├── AbstractListType.php │ │ └── ListType.php │ ├── NonNullType.php │ ├── Object │ │ ├── AbstractMutationObjectType.php │ │ ├── AbstractObjectType.php │ │ └── ObjectType.php │ ├── Scalar │ │ ├── AbstractScalarType.php │ │ ├── BooleanType.php │ │ ├── DateTimeType.php │ │ ├── DateTimeTzType.php │ │ ├── DateType.php │ │ ├── FloatType.php │ │ ├── IdType.php │ │ ├── IntType.php │ │ ├── StringType.php │ │ └── TimestampType.php │ ├── SchemaDirectivesList.php │ ├── SchemaTypesList.php │ ├── Traits │ │ ├── ArgumentsAwareObjectTrait.php │ │ ├── AutoNameTrait.php │ │ ├── FieldsArgumentsAwareObjectTrait.php │ │ └── FieldsAwareObjectTrait.php │ ├── TypeFactory.php │ ├── TypeInterface.php │ ├── TypeMap.php │ ├── TypeService.php │ └── Union │ │ ├── AbstractUnionType.php │ │ └── UnionType.php └── Validator │ ├── ConfigValidator │ ├── ConfigValidator.php │ ├── ConfigValidatorInterface.php │ └── Rules │ │ ├── TypeValidationRule.php │ │ └── ValidationRuleInterface.php │ ├── ErrorContainer │ ├── ErrorContainerInterface.php │ └── ErrorContainerTrait.php │ ├── RequestValidator │ ├── RequestValidator.php │ └── RequestValidatorInterface.php │ ├── ResolveValidator │ ├── ResolveValidator.php │ └── ResolveValidatorInterface.php │ └── SchemaValidator │ └── SchemaValidator.php └── tests ├── DataProvider ├── TestConfig.php ├── TestConfigExtraFields.php ├── TestConfigInvalidRule.php ├── TestEmptySchema.php ├── TestEnumType.php ├── TestExtendedType.php ├── TestField.php ├── TestInputField.php ├── TestInputObjectType.php ├── TestInterfaceType.php ├── TestListType.php ├── TestMutationObjectType.php ├── TestObjectType.php ├── TestResolveInfo.php ├── TestScalarDataProvider.php ├── TestSchema.php ├── TestTimeType.php └── TestUnionType.php ├── Issues ├── Issue109 │ ├── Issue109Schema.php │ └── Issue109Test.php ├── Issue116 │ └── Issue116Test.php ├── Issue131 │ └── Issue131Test.php ├── Issue149 │ └── Issue149Test.php ├── Issue151 │ └── Issue151Test.php ├── Issue171 │ ├── Issue171Schema.php │ └── Issue171Test.php ├── Issue193 │ └── Issue193Test.php ├── Issue194 │ └── ProcessorTest.php ├── Issue201 │ └── Issue201Test.php ├── Issue210 │ └── TypeServiceTest.php ├── Issue220 │ └── ResolvableObjectTraitTest.php ├── Issue90 │ ├── Issue90Schema.php │ └── Issue90Test.php ├── Issue98 │ └── TypeServiceTest.php └── Issue99 │ ├── Issue99Schema.php │ └── Issue99Test.php ├── Library ├── Config │ ├── ConfigTest.php │ ├── FieldConfigTest.php │ ├── InterfaceTypeConfigTest.php │ └── ObjectTypeConfigTest.php ├── Field │ ├── ArgumentsAwareConfigTraitTest.php │ ├── FieldAwareConfigTraitTest.php │ ├── FieldTest.php │ └── InputFieldTest.php ├── Relay │ ├── ArrayConnectionTest.php │ ├── CallableFetcherTest.php │ ├── ConnectionTest.php │ ├── GlobalIdFieldTest.php │ ├── MutationTest.php │ ├── NodeFieldTest.php │ ├── NodeInterfaceTypeTest.php │ └── NodeTest.php ├── Type │ ├── EnumTypeTest.php │ ├── InputObjectTypeTest.php │ ├── InterfaceTypeTest.php │ ├── ListTypeTest.php │ ├── NonNullTypeTest.php │ ├── ObjectTypeTest.php │ ├── ScalarExtendTypeTest.php │ ├── ScalarTypeTest.php │ ├── SchemaDirectivesListTest.php │ └── UnionTypeTest.php ├── Utilities │ ├── ErrorContainerTraitTest.php │ └── TypeUtilitiesTest.php └── Validator │ ├── RequestValidatorTest.php │ ├── SchemaValidatorTest.php │ └── TypeValidationRuleTest.php ├── Parser ├── AstTest.php ├── ParserTest.php ├── RequestTest.php └── VariableTest.php ├── Performance ├── LoadTest.php └── NPlusOneTest.php ├── Schema ├── DeferredTest.php ├── FragmentsTest.php ├── InputObjectDefaultValuesTest.php ├── InputParseTest.php ├── IntrospectionTest.php ├── NonNullableTest.php ├── ProcessorTest.php ├── ResolveInfoTest.php ├── SchemaTest.php └── VariablesTest.php └── StarWars ├── Schema ├── CharacterInterface.php ├── DroidType.php ├── EpisodeEnum.php ├── HumanType.php ├── StarWarsData.php ├── StarWarsQueryType.php └── StarWarsSchema.php └── StarWarsTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | phpunit.xml 3 | vendor/ 4 | data/schema.graphql 5 | composer.lock 6 | .idea 7 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - php 3 | 4 | tools: 5 | external_code_coverage: 6 | timeout: 600 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | matrix: 4 | include: 5 | - php: 5.5 6 | dist: trusty 7 | - php: 5.6 8 | - php: 7.0 9 | - php: 7.1 10 | - php: 7.2 11 | - php: 7.3 12 | env: STATIC_ANALYSIS=true 13 | 14 | install: composer install --prefer-dist --no-interaction 15 | 16 | script: 17 | - ./vendor/bin/phpunit --coverage-clover=coverage.clover 18 | - | 19 | if [[ $STATIC_ANALYSIS == true ]]; then 20 | composer require --ignore-platform-reqs --dev phpstan/phpstan 21 | vendor/bin/phpstan analyze src -c phpstan.neon -l 1 22 | fi 23 | 24 | after_script: 25 | - wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover 26 | 27 | notifications: 28 | email: 29 | recipients: 30 | - portey@gmail.com 31 | - a@viniychuk.com 32 | on_success: change 33 | on_failure: always 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Please update this file right before tagging a new release 4 | 5 | ## v1.7.1 6 | 7 | * relaxed symfony/property-accessor version constraints so the package can be installed in Symfony 4.x projects 8 | 9 | ## v1.7.0 10 | 11 | * fix some README badges 12 | * add a more robust implementation for `TypeService::getPropertyValue()` 13 | * fix PhpStorm inspection performance complaints 14 | * fix a bug that prevented using `null` as a default value 15 | * add support for error extensions 16 | * throw `ConfigurationException` when trying to add two types with the same name to the schema 17 | * add a `getImplementations()` method to `AbstractInterfaceType`, can be used to properly discover all possible types during introspection 18 | * run Travis CI on PHP 7.2 and 7.3 too 19 | * run phpstan static analysis (level 1 only) during Travis CI builds 20 | * rename the `Tests` directory to `tests` for consistency with other projects 21 | * remove some obsolete documentation 22 | 23 | ## v1.6.0 24 | 25 | * fix the Travis CI build for PHP 5.5 26 | * improve examples somewhat 27 | * make `Node` stricter when validating input 28 | * return null not just when the field is scalar but whenever it's non-null 29 | 30 | ## v1.5.5 31 | 32 | * add missing directive locations 33 | * add a `totalCount` field to connections 34 | * fix a regression introduced in #151 35 | * add a type/field lookup cache for improved performance 36 | * add support for nested deferred resolvers 37 | * properly handle null values in `BooleanType` 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Youshido 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 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "youshido/graphql", 3 | "description": "Pure PHP GraphQL", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Alexandr Viniychuk", 8 | "email": "a@viniychuk.com" 9 | }, 10 | { 11 | "name": "Portey Vasil", 12 | "email": "portey@gmail.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.5", 17 | "ext-mbstring": "*", 18 | "symfony/property-access": "^2.8 || ^3.4 || ^4.4" 19 | }, 20 | "require-dev": { 21 | "phpunit/phpunit": "^4.8" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "Youshido\\GraphQL\\": "src" 26 | } 27 | }, 28 | "autoload-dev": { 29 | "psr-4": { 30 | "Youshido\\Tests\\": "tests" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/01_sandbox/index.php: -------------------------------------------------------------------------------- 1 | new ObjectType([ 18 | 'name' => 'RootQueryType', 19 | 'fields' => [ 20 | 'currentTime' => [ 21 | 'type' => new StringType(), 22 | 'resolve' => function () { 23 | return date('Y-m-d H:ia'); 24 | } 25 | ] 26 | ] 27 | ]) 28 | ])); 29 | 30 | $processor->processPayload('{ currentTime }'); 31 | echo json_encode($processor->getResponseData()) . "\n"; 32 | -------------------------------------------------------------------------------- /examples/02_blog/Schema/BannerType.php: -------------------------------------------------------------------------------- 1 | addField('title', new NonNullType(new StringType())) 18 | ->addField('summary', new StringType()) 19 | ->addField('imageLink', new StringType()); 20 | } 21 | 22 | public function getInterfaces() 23 | { 24 | return [new ContentBlockInterface()]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/02_blog/Schema/ContentBlockInterface.php: -------------------------------------------------------------------------------- 1 | addField('title', new NonNullType(new StringType())); 19 | $config->addField('summary', new StringType()); 20 | } 21 | 22 | public function resolveType($object) 23 | { 24 | return empty($object['id']) ? null : (strpos($object['id'], 'post') !== false ? new PostType() : new BannerType()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/02_blog/Schema/ContentBlockUnion.php: -------------------------------------------------------------------------------- 1 | "post-" . $id, 14 | "title" => "Post " . $id . " title", 15 | "summary" => "This new GraphQL library for PHP works really well", 16 | "status" => 1, 17 | "likeCount" => 2 18 | ]; 19 | } 20 | 21 | public static function getBanner($id) 22 | { 23 | return [ 24 | 'id' => "banner-" . $id, 25 | 'title' => "Banner " . $id, 26 | 'imageLink' => "banner" . $id . ".jpg" 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/02_blog/Schema/LatestPostField.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/20/16 12:07 AM 7 | */ 8 | 9 | namespace Examples\Blog\Schema; 10 | 11 | 12 | use BlogTest\PostType; 13 | use Youshido\GraphQL\Execution\ResolveInfo; 14 | use Youshido\GraphQL\Field\AbstractField; 15 | 16 | class LatestPostField extends AbstractField 17 | { 18 | public function getType() 19 | { 20 | return new PostType(); 21 | } 22 | 23 | public function resolve($value, array $args, ResolveInfo $info) 24 | { 25 | return [ 26 | "title" => "New approach in API has been revealed", 27 | "summary" => "This post describes a new approach to create and maintain APIs", 28 | ]; 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /examples/02_blog/Schema/LikePostField.php: -------------------------------------------------------------------------------- 1 | getReturnType()->getOne($args['id']); 27 | } 28 | 29 | public function getType() 30 | { 31 | return new PostType(); 32 | } 33 | 34 | public function build(FieldConfig $config) 35 | { 36 | $config->addArgument('id', new NonNullType(new IntType())); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /examples/02_blog/Schema/PostInputType.php: -------------------------------------------------------------------------------- 1 | addField('title', new NonNullType(new StringType())) 20 | ->addField('summary', new StringType()); 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /examples/02_blog/Schema/PostStatus.php: -------------------------------------------------------------------------------- 1 | 0, 18 | 'name' => 'DRAFT', 19 | ], 20 | [ 21 | 'value' => 1, 22 | 'name' => 'PUBLISHED', 23 | ] 24 | ]; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /examples/02_blog/Schema/PostType.php: -------------------------------------------------------------------------------- 1 | addField('oldTitle', [ 24 | 'type' => new NonNullType(new StringType()), 25 | 'description' => 'This field contains a post title', 26 | 'isDeprecated' => true, 27 | 'deprecationReason' => 'field title is now deprecated', 28 | 'args' => [ 29 | 'truncated' => new BooleanType() 30 | ], 31 | 'resolve' => function ($value, $args) { 32 | return (!empty($args['truncated'])) ? explode(' ', $value)[0] . '...' : $value; 33 | } 34 | ]) 35 | ->addField( 36 | 'title', [ 37 | 'type' => new NonNullType(new StringType()), 38 | 'args' => [ 39 | 'truncated' => new BooleanType() 40 | ], 41 | ]) 42 | ->addField('status', new PostStatus()) 43 | ->addField('summary', new StringType()) 44 | ->addField('likeCount', new IntType()); 45 | } 46 | 47 | public function getOne($id) 48 | { 49 | return DataProvider::getPost($id); 50 | } 51 | 52 | public function getInterfaces() 53 | { 54 | return [new ContentBlockInterface()]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/02_blog/index.php: -------------------------------------------------------------------------------- 1 | processPayload($payload); 21 | echo json_encode($processor->getResponseData()) . "\n"; 22 | -------------------------------------------------------------------------------- /examples/02_blog/router.php: -------------------------------------------------------------------------------- 1 | processPayload($payload, $variables)->getResponseData(); 31 | 32 | header('Content-Type: application/json'); 33 | echo json_encode($response); 34 | -------------------------------------------------------------------------------- /examples/02_blog/schema-bootstrap.php: -------------------------------------------------------------------------------- 1 | $rootQueryType 20 | ])); 21 | $payload = '{ latestPost { title(truncated: true), summary } }'; 22 | 23 | $processor->processPayload($payload); 24 | echo json_encode($processor->getResponseData()) . "\n"; 25 | -------------------------------------------------------------------------------- /examples/03_relay_star_wars/Schema/ShipType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/10/16 11:17 PM 7 | */ 8 | 9 | namespace Examples\StarWars; 10 | 11 | 12 | use Youshido\GraphQL\Relay\Field\GlobalIdField; 13 | use Youshido\GraphQL\Relay\NodeInterfaceType; 14 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 15 | use Youshido\GraphQL\Type\TypeMap; 16 | 17 | class ShipType extends AbstractObjectType 18 | { 19 | const TYPE_KEY = 'ship'; 20 | 21 | public function build($config) 22 | { 23 | $config 24 | ->addField(new GlobalIdField(self::TYPE_KEY)) 25 | ->addField('name', ['type' => TypeMap::TYPE_STRING, 'description' => 'The name of the ship.']); 26 | } 27 | 28 | public function getOne($id) 29 | { 30 | return TestDataProvider::getShip($id); 31 | } 32 | 33 | public function getDescription() 34 | { 35 | return 'A ship in the Star Wars saga'; 36 | } 37 | 38 | public function getInterfaces() 39 | { 40 | return [new NodeInterfaceType()]; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /examples/03_relay_star_wars/index.php: -------------------------------------------------------------------------------- 1 | processPayload($payload, ['names_0' => ['rebels']]); 51 | echo json_encode($processor->getResponseData()) . "\n"; 52 | -------------------------------------------------------------------------------- /examples/03_relay_star_wars/router.php: -------------------------------------------------------------------------------- 1 | processPayload($payload, $variables)->getResponseData(); 43 | 44 | header('Content-Type: application/json'); 45 | echo json_encode($response); 46 | -------------------------------------------------------------------------------- /examples/03_relay_star_wars/schema-bootstrap.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4/23/17 11:10 PM 7 | */ 8 | 9 | namespace Examples\BookStore; 10 | 11 | 12 | class DataProvider 13 | { 14 | 15 | static private $authors = [ 16 | '1' => [ 17 | 'id' => '1', 18 | 'firstName' => 'Mark', 19 | 'lastName' => 'Twain' 20 | ] 21 | ]; 22 | 23 | public static function getBooks() 24 | { 25 | return [ 26 | [ 27 | 'id' => 1, 28 | 'title' => 'The Adventures of Tom Sawyer', 29 | 'year' => 1876, 30 | 'isbn' => '978-0996584838', 31 | 'author' => self::$authors['1'] 32 | ], 33 | ]; 34 | } 35 | 36 | public static function getAuthors() 37 | { 38 | return self::$authors; 39 | } 40 | } -------------------------------------------------------------------------------- /examples/04_bookstore/Schema/BookStoreSchema.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4/23/17 10:43 PM 7 | */ 8 | 9 | namespace Examples\BookStore\Schema; 10 | 11 | 12 | use Examples\BookStore\DataProvider; 13 | use Examples\BookStore\Schema\Field\Book\RecentBooksField; 14 | use Examples\BookStore\Schema\Field\CategoriesField; 15 | use Examples\BookStore\Schema\Type\AuthorType; 16 | use Youshido\GraphQL\Config\Schema\SchemaConfig; 17 | use Youshido\GraphQL\Schema\AbstractSchema; 18 | use Youshido\GraphQL\Type\ListType\ListType; 19 | 20 | class BookStoreSchema extends AbstractSchema 21 | { 22 | public function build(SchemaConfig $config) 23 | { 24 | $config->getQuery()->addFields([ 25 | 'authors' => [ 26 | 'type' => new ListType(new AuthorType()), 27 | 'resolve' => function () { 28 | return DataProvider::getAuthors(); 29 | } 30 | ], 31 | new RecentBooksField(), 32 | new CategoriesField(), 33 | ]); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /examples/04_bookstore/Schema/Field/Book/RecentBooksField.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4/23/17 11:09 PM 7 | */ 8 | 9 | namespace Examples\BookStore\Schema\Field\Book; 10 | 11 | 12 | use Examples\BookStore\DataProvider; 13 | use Examples\BookStore\Schema\Type\BookType; 14 | use Youshido\GraphQL\Execution\ResolveInfo; 15 | use Youshido\GraphQL\Field\AbstractField; 16 | use Youshido\GraphQL\Type\ListType\ListType; 17 | 18 | class RecentBooksField extends AbstractField 19 | { 20 | public function getType() 21 | { 22 | return new ListType(new BookType()); 23 | } 24 | 25 | public function resolve($value, array $args, ResolveInfo $info) 26 | { 27 | return DataProvider::getBooks(); 28 | } 29 | 30 | 31 | } -------------------------------------------------------------------------------- /examples/04_bookstore/Schema/Field/CategoriesField.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4/24/17 11:42 PM 7 | */ 8 | 9 | namespace Examples\BookStore\Schema\Field; 10 | 11 | 12 | use Examples\BookStore\Schema\Type\CategoryType; 13 | use Youshido\GraphQL\Execution\DeferredResolver; 14 | use Youshido\GraphQL\Execution\ResolveInfo; 15 | use Youshido\GraphQL\Field\AbstractField; 16 | use Youshido\GraphQL\Type\ListType\ListType; 17 | 18 | class CategoriesField extends AbstractField 19 | { 20 | public function getType() 21 | { 22 | return new ListType(new CategoryType()); 23 | } 24 | 25 | public function resolve($value, array $args, ResolveInfo $info) 26 | { 27 | return new DeferredResolver(function () use ($value) { 28 | $id = empty($value['id']) ? "1" : $value['id'] . ".1"; 29 | 30 | return [ 31 | 'id' => $id, 32 | 'title' => 'Category ' . $id, 33 | 'authors' => [ 34 | [ 35 | 'id' => 'author1', 36 | 'firstName' => 'John', 37 | 'lastName' => 'Doe', 38 | ], 39 | ], 40 | 'children' => [], 41 | ]; 42 | }); 43 | } 44 | } -------------------------------------------------------------------------------- /examples/04_bookstore/Schema/Type/AuthorType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4/23/17 11:05 PM 7 | */ 8 | 9 | namespace Examples\BookStore\Schema\Type; 10 | 11 | use Youshido\GraphQL\Type\NonNullType; 12 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 13 | use Youshido\GraphQL\Type\Scalar\IdType; 14 | use Youshido\GraphQL\Type\Scalar\StringType; 15 | 16 | class AuthorType extends AbstractObjectType 17 | { 18 | public function build($config) 19 | { 20 | $config->addFields([ 21 | 'id' => new NonNullType(new IdType()), 22 | 'firstName' => new StringType(), 23 | 'lastName' => new StringType(), 24 | ]); 25 | } 26 | } -------------------------------------------------------------------------------- /examples/04_bookstore/Schema/Type/BookType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4/23/17 11:08 PM 7 | */ 8 | 9 | namespace Examples\BookStore\Schema\Type; 10 | 11 | 12 | use Youshido\GraphQL\Config\Object\ObjectTypeConfig; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\Scalar\IdType; 15 | use Youshido\GraphQL\Type\Scalar\IntType; 16 | use Youshido\GraphQL\Type\Scalar\StringType; 17 | 18 | class BookType extends AbstractObjectType 19 | { 20 | public function build($config) 21 | { 22 | $config->addFields([ 23 | 'id' => new IdType(), 24 | 'title' => new StringType(), 25 | 'year' => new IntType(), 26 | 'isbn' => new StringType(), 27 | 'author' => new AuthorType(), 28 | ]); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /examples/04_bookstore/Schema/Type/CategoryType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4/24/17 11:40 PM 7 | */ 8 | 9 | namespace Examples\BookStore\Schema\Type; 10 | 11 | use Examples\BookStore\Schema\Field\CategoriesField; 12 | use Youshido\GraphQL\Type\ListType\ListType; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\Scalar\IdType; 15 | use Youshido\GraphQL\Type\Scalar\StringType; 16 | 17 | class CategoryType extends AbstractObjectType 18 | { 19 | public function build($config) 20 | { 21 | $config->addFields([ 22 | 'id' => new IdType(), 23 | 'title' => new StringType(), 24 | 'authors' => new ListType(new AuthorType()), 25 | new CategoriesField(), 26 | ]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/04_bookstore/index.php: -------------------------------------------------------------------------------- 1 | processPayload($payload, $variables)->getResponseData(); 44 | 45 | header('Content-Type: application/json'); 46 | echo json_encode($response); 47 | -------------------------------------------------------------------------------- /examples/04_bookstore/schema-bootstrap.php: -------------------------------------------------------------------------------- 1 | , 32 | document.getElementById('root') 33 | ); 34 | -------------------------------------------------------------------------------- /examples/js-relay/js/components/StarWarsShip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file provided by Facebook is for non-commercial testing and evaluation 3 | * purposes only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | import React from 'react'; 14 | import Relay from 'react-relay'; 15 | 16 | class StarWarsShip extends React.Component { 17 | render() { 18 | const {ship} = this.props; 19 | return
{ship.name}
; 20 | } 21 | } 22 | 23 | export default Relay.createContainer(StarWarsShip, { 24 | fragments: { 25 | ship: () => Relay.QL` 26 | fragment on Ship { 27 | id, 28 | name 29 | } 30 | `, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /examples/js-relay/js/mutation/AddShipMutation.js: -------------------------------------------------------------------------------- 1 | import Relay from 'react-relay'; 2 | 3 | export default class AddShipMutation extends Relay.Mutation { 4 | 5 | static fragments = { 6 | faction: () => Relay.QL` 7 | fragment on Faction { 8 | id, 9 | factionId 10 | } 11 | `, 12 | }; 13 | 14 | getMutation() { 15 | return Relay.QL`mutation { introduceShip }`; 16 | } 17 | 18 | getVariables() { 19 | return { 20 | shipName: this.props.name, 21 | factionId: this.props.faction.factionId, 22 | }; 23 | } 24 | 25 | getFatQuery() { 26 | return Relay.QL` 27 | fragment on IntroduceShipPayload @relay(pattern: true) { 28 | faction { 29 | name 30 | ships { 31 | edges { 32 | node { 33 | name 34 | } 35 | } 36 | } 37 | } 38 | newShipEdge 39 | } 40 | `; 41 | } 42 | 43 | getConfigs() { 44 | return [{ 45 | type: 'RANGE_ADD', 46 | parentName: 'faction', 47 | parentID: this.props.faction.id, 48 | connectionName: 'ships', 49 | edgeName: 'newShipEdge', 50 | rangeBehaviors: { 51 | '': 'append', 52 | 'orderby(oldest)': 'prepend', 53 | }, 54 | }]; 55 | } 56 | 57 | getOptimisticResponse() { 58 | return { 59 | newShipEdge: { 60 | node: { 61 | name: this.props.name, 62 | }, 63 | }, 64 | faction: { 65 | id: this.props.faction.id, 66 | }, 67 | }; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /examples/js-relay/js/routes/StarWarsAppHomeRoute.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file provided by Facebook is for non-commercial testing and evaluation 3 | * purposes only. Facebook reserves all rights not expressly granted. 4 | * 5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 10 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | */ 12 | 13 | import Relay from 'react-relay'; 14 | 15 | export default class extends Relay.Route { 16 | static queries = { 17 | factions: () => Relay.QL`query { factions(names: $factionNames) }`, 18 | }; 19 | static routeName = 'StarWarsAppHomeRoute'; 20 | } 21 | -------------------------------------------------------------------------------- /examples/js-relay/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "relay-starter-kit", 3 | "private": true, 4 | "description": "A quick way to get up and running with Relay", 5 | "repository": "facebook/relay-starter-kit", 6 | "version": "0.1.0", 7 | "scripts": { 8 | "start": "babel-node ./server.js", 9 | "update-schema": "babel-node ./scripts/updateSchema.js" 10 | }, 11 | "dependencies": { 12 | "babel-cli": "^6.7.7", 13 | "babel-core": "^6.7.7", 14 | "babel-loader": "6.2.4", 15 | "babel-polyfill": "6.7.4", 16 | "babel-preset-es2015": "6.6.0", 17 | "babel-preset-react": "6.5.0", 18 | "babel-preset-stage-0": "6.5.0", 19 | "babel-relay-plugin": "0.8.1", 20 | "classnames": "2.2.4", 21 | "express": "4.13.4", 22 | "express-graphql": "0.5.1", 23 | "graphql": "0.5.0", 24 | "graphql-relay": "0.4.1", 25 | "react": "15.0.1", 26 | "react-dom": "15.0.1", 27 | "react-relay": "0.8.1", 28 | "webpack": "1.13.0", 29 | "webpack-dev-server": "1.14.1" 30 | }, 31 | "devDependencies": { 32 | "babel-cli": "6.7.7" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/js-relay/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Relay • Star Wars 7 | 8 | 9 |
10 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/js-relay/scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env babel-node --optional es7.asyncFunctions 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import fs from 'fs'; 15 | import path from 'path'; 16 | import { schema } from '../data/schema'; 17 | import { graphql } from 'graphql'; 18 | import { introspectionQuery, printSchema } from 'graphql/utilities'; 19 | 20 | // Save JSON of full schema introspection for Babel Relay Plugin to use 21 | (async () => { 22 | const result = await (graphql(schema, introspectionQuery)); 23 | if (result.errors) { 24 | console.error( 25 | 'ERROR introspecting schema: ', 26 | JSON.stringify(result.errors, null, 2) 27 | ); 28 | } else { 29 | fs.writeFileSync( 30 | path.join(__dirname, '../data/schema.json'), 31 | JSON.stringify(result, null, 2) 32 | ); 33 | } 34 | })(); 35 | 36 | // Save user readable type system shorthand of schema 37 | fs.writeFileSync( 38 | path.join(__dirname, '../data/schema.graphql'), 39 | printSchema(schema) 40 | ); 41 | -------------------------------------------------------------------------------- /examples/js/.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["es2015"] } 2 | -------------------------------------------------------------------------------- /examples/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-js-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "babel-preset-es2015": "^6.6.0", 8 | "express": "^4.13.4", 9 | "express-graphql": "^0.5.1", 10 | "graphql": "^0.5.0", 11 | "graphql-relay": "^0.4.1" 12 | }, 13 | "devDependencies": { 14 | "babel-cli": "^6.14.0", 15 | "babel-preset-es2015": "^6.14.0", 16 | "graphql-relay": "^0.4.3" 17 | }, 18 | "scripts": { 19 | "test": "exit 1" 20 | }, 21 | "author": "", 22 | "license": "ISC" 23 | } 24 | -------------------------------------------------------------------------------- /examples/structure.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youshido-php/GraphQL/b080756d8d441d68473b12569bfe621f5be73f7b/examples/structure.sketch -------------------------------------------------------------------------------- /examples/structure.xd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youshido-php/GraphQL/b080756d8d441d68473b12569bfe621f5be73f7b/examples/structure.xd -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | # nothing here for now 2 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ./tests 7 | 8 | 9 | 10 | 11 | 12 | ./src/ 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Config/Directive/DirectiveConfig.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Config\Directive; 9 | 10 | 11 | use Youshido\GraphQL\Config\AbstractConfig; 12 | use Youshido\GraphQL\Config\Traits\ArgumentsAwareConfigTrait; 13 | use Youshido\GraphQL\Type\TypeService; 14 | 15 | /** 16 | * Class DirectiveConfig 17 | * 18 | * @package Youshido\GraphQL\Config\Directive 19 | */ 20 | class DirectiveConfig extends AbstractConfig 21 | { 22 | 23 | use ArgumentsAwareConfigTrait; 24 | 25 | protected $locations = []; 26 | 27 | public function getRules() 28 | { 29 | return [ 30 | 'name' => ['type' => TypeService::TYPE_STRING, 'final' => true], 31 | 'description' => ['type' => TypeService::TYPE_STRING], 32 | 'args' => ['type' => TypeService::TYPE_ARRAY], 33 | 'locations' => ['type' => TypeService::TYPE_ARRAY], 34 | ]; 35 | } 36 | 37 | public function getLocations() 38 | { 39 | return $this->locations; 40 | } 41 | 42 | public function build() 43 | { 44 | $this->buildArguments(); 45 | 46 | if (!empty($this->data['locations'])) { 47 | foreach ($this->data['locations'] as $location) { 48 | $this->locations[] = $location; 49 | } 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/Config/Field/FieldConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/30/15 1:30 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Field; 10 | 11 | use Youshido\GraphQL\Config\AbstractConfig; 12 | use Youshido\GraphQL\Config\Traits\ArgumentsAwareConfigTrait; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\TypeInterface; 15 | use Youshido\GraphQL\Type\TypeService; 16 | 17 | /** 18 | * Class FieldConfig 19 | * @package Youshido\GraphQL\Config\Field 20 | * @method $this setDescription(string $description) 21 | * @method $this setCost(int $cost) 22 | */ 23 | class FieldConfig extends AbstractConfig 24 | { 25 | 26 | use ArgumentsAwareConfigTrait; 27 | 28 | public function getRules() 29 | { 30 | return [ 31 | 'name' => ['type' => TypeService::TYPE_STRING, 'final' => true], 32 | 'type' => ['type' => TypeService::TYPE_GRAPHQL_TYPE, 'final' => true], 33 | 'args' => ['type' => TypeService::TYPE_ARRAY], 34 | 'description' => ['type' => TypeService::TYPE_STRING], 35 | 'resolve' => ['type' => TypeService::TYPE_CALLABLE], 36 | 'isDeprecated' => ['type' => TypeService::TYPE_BOOLEAN], 37 | 'deprecationReason' => ['type' => TypeService::TYPE_STRING], 38 | 'cost' => ['type' => TypeService::TYPE_ANY] 39 | ]; 40 | } 41 | 42 | protected function build() 43 | { 44 | $this->buildArguments(); 45 | } 46 | 47 | /** 48 | * @return TypeInterface|AbstractObjectType 49 | */ 50 | public function getType() 51 | { 52 | return $this->data['type']; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/Config/Field/InputFieldConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/1/15 11:28 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Field; 10 | 11 | 12 | use Youshido\GraphQL\Config\AbstractConfig; 13 | use Youshido\GraphQL\Type\TypeService; 14 | 15 | /** 16 | * Class InputFieldConfig 17 | * @package Youshido\GraphQL\Config\Field 18 | * @method $this setDescription(string $description) 19 | */ 20 | class InputFieldConfig extends AbstractConfig 21 | { 22 | 23 | public function getRules() 24 | { 25 | return [ 26 | 'name' => ['type' => TypeService::TYPE_STRING, 'final' => true], 27 | 'type' => ['type' => TypeService::TYPE_ANY_INPUT, 'final' => true], 28 | 'defaultValue' => ['type' => TypeService::TYPE_ANY], 29 | 'description' => ['type' => TypeService::TYPE_STRING], 30 | 'isDeprecated' => ['type' => TypeService::TYPE_BOOLEAN], 31 | 'deprecationReason' => ['type' => TypeService::TYPE_STRING], 32 | ]; 33 | } 34 | 35 | public function getDefaultValue() 36 | { 37 | return $this->get('defaultValue'); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Config/Object/EnumTypeConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/5/15 12:18 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Object; 10 | 11 | 12 | use Youshido\GraphQL\Config\AbstractConfig; 13 | use Youshido\GraphQL\Config\Traits\ArgumentsAwareConfigTrait; 14 | use Youshido\GraphQL\Config\Traits\FieldsAwareConfigTrait; 15 | use Youshido\GraphQL\Config\TypeConfigInterface; 16 | use Youshido\GraphQL\Type\TypeService; 17 | 18 | class EnumTypeConfig extends AbstractConfig implements TypeConfigInterface 19 | { 20 | use FieldsAwareConfigTrait, ArgumentsAwareConfigTrait; 21 | 22 | public function getRules() 23 | { 24 | return [ 25 | 'name' => ['type' => TypeService::TYPE_STRING, 'final' => true], 26 | 'description' => ['type' => TypeService::TYPE_STRING], 27 | 'values' => ['type' => TypeService::TYPE_ENUM_VALUES, 'required' => true], 28 | ]; 29 | } 30 | 31 | public function getValues() 32 | { 33 | return $this->get('values', []); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/Config/Object/InputObjectTypeConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/1/15 11:13 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Object; 10 | 11 | 12 | use Youshido\GraphQL\Type\TypeService; 13 | 14 | class InputObjectTypeConfig extends ObjectTypeConfig 15 | { 16 | public function getRules() 17 | { 18 | return [ 19 | 'name' => ['type' => TypeService::TYPE_STRING, 'required' => true], 20 | 'fields' => ['type' => TypeService::TYPE_ARRAY_OF_INPUT_FIELDS, 'final' => true], 21 | 'description' => ['type' => TypeService::TYPE_STRING], 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Config/Object/InterfaceTypeConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/5/15 12:18 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Object; 10 | 11 | 12 | use Youshido\GraphQL\Config\AbstractConfig; 13 | use Youshido\GraphQL\Config\Traits\ArgumentsAwareConfigTrait; 14 | use Youshido\GraphQL\Config\Traits\FieldsAwareConfigTrait; 15 | use Youshido\GraphQL\Config\TypeConfigInterface; 16 | use Youshido\GraphQL\Exception\ConfigurationException; 17 | use Youshido\GraphQL\Type\TypeService; 18 | 19 | /** 20 | * Class InterfaceTypeConfig 21 | * @package Youshido\GraphQL\Config\Object 22 | * @method $this setDescription(string $description) 23 | */ 24 | class InterfaceTypeConfig extends AbstractConfig implements TypeConfigInterface 25 | { 26 | use FieldsAwareConfigTrait, ArgumentsAwareConfigTrait; 27 | 28 | public function getRules() 29 | { 30 | return [ 31 | 'name' => ['type' => TypeService::TYPE_STRING, 'final' => true], 32 | 'fields' => ['type' => TypeService::TYPE_ARRAY_OF_FIELDS_CONFIG, 'final' => true], 33 | 'description' => ['type' => TypeService::TYPE_STRING], 34 | 'resolveType' => ['type' => TypeService::TYPE_CALLABLE, 'final' => true], 35 | ]; 36 | } 37 | 38 | protected function build() 39 | { 40 | $this->buildFields(); 41 | } 42 | 43 | public function resolveType($object) 44 | { 45 | $callable = $this->get('resolveType'); 46 | 47 | if ($callable && is_callable($callable)) { 48 | return $callable($object); 49 | } elseif (is_callable([$this->contextObject, 'resolveType'])) { 50 | return $this->contextObject->resolveType($object); 51 | } 52 | 53 | throw new ConfigurationException('There is no valid resolveType for ' . $this->getName()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Config/Object/ListTypeConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/1/15 1:22 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Object; 10 | 11 | 12 | use Youshido\GraphQL\Type\TypeService; 13 | 14 | class ListTypeConfig extends ObjectTypeConfig 15 | { 16 | public function getRules() 17 | { 18 | return [ 19 | 'itemType' => ['type' => TypeService::TYPE_GRAPHQL_TYPE, 'final' => true], 20 | 'resolve' => ['type' => TypeService::TYPE_CALLABLE], 21 | 'args' => ['type' => TypeService::TYPE_ARRAY_OF_INPUT_FIELDS], 22 | ]; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/Config/Object/ObjectTypeConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 2:32 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Object; 10 | 11 | use Youshido\GraphQL\Config\AbstractConfig; 12 | use Youshido\GraphQL\Config\Traits\FieldsAwareConfigTrait; 13 | use Youshido\GraphQL\Config\TypeConfigInterface; 14 | use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType; 15 | use Youshido\GraphQL\Type\TypeService; 16 | 17 | /** 18 | * Class ObjectTypeConfig 19 | * @package Youshido\GraphQL\Config\Object 20 | * @method setDescription(string $description) 21 | * @method string getDescription() 22 | */ 23 | class ObjectTypeConfig extends AbstractConfig implements TypeConfigInterface 24 | { 25 | 26 | use FieldsAwareConfigTrait; 27 | 28 | public function getRules() 29 | { 30 | return [ 31 | 'name' => ['type' => TypeService::TYPE_STRING, 'required' => true], 32 | 'description' => ['type' => TypeService::TYPE_STRING], 33 | 'fields' => ['type' => TypeService::TYPE_ARRAY_OF_FIELDS_CONFIG, 'final' => true], 34 | 'interfaces' => ['type' => TypeService::TYPE_ARRAY_OF_INTERFACES] 35 | ]; 36 | } 37 | 38 | protected function build() 39 | { 40 | $this->buildFields(); 41 | } 42 | 43 | /** 44 | * @return AbstractInterfaceType[] 45 | */ 46 | public function getInterfaces() 47 | { 48 | return $this->get('interfaces', []); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/Config/Object/UnionTypeConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/5/15 12:18 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Object; 10 | 11 | 12 | use Youshido\GraphQL\Config\AbstractConfig; 13 | use Youshido\GraphQL\Config\Traits\ArgumentsAwareConfigTrait; 14 | use Youshido\GraphQL\Config\Traits\FieldsAwareConfigTrait; 15 | use Youshido\GraphQL\Config\TypeConfigInterface; 16 | use Youshido\GraphQL\Type\TypeService; 17 | 18 | class UnionTypeConfig extends AbstractConfig implements TypeConfigInterface 19 | { 20 | use FieldsAwareConfigTrait, ArgumentsAwareConfigTrait; 21 | 22 | public function getRules() 23 | { 24 | return [ 25 | 'name' => ['type' => TypeService::TYPE_STRING, 'required' => true], 26 | 'types' => ['type' => TypeService::TYPE_ARRAY_OF_OBJECT_TYPES], 27 | 'description' => ['type' => TypeService::TYPE_STRING], 28 | 'resolveType' => ['type' => TypeService::TYPE_CALLABLE, 'final' => true] 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Config/Traits/ConfigAwareTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 2:02 PM 5/13/16 7 | */ 8 | 9 | namespace Youshido\GraphQL\Config\Traits; 10 | 11 | 12 | use Youshido\GraphQL\Config\AbstractConfig; 13 | use Youshido\GraphQL\Config\Field\FieldConfig; 14 | use Youshido\GraphQL\Config\Field\InputFieldConfig; 15 | use Youshido\GraphQL\Config\Object\ObjectTypeConfig; 16 | 17 | trait ConfigAwareTrait 18 | { 19 | 20 | /** @var AbstractConfig|ObjectTypeConfig|FieldConfig|InputFieldConfig */ 21 | protected $config; 22 | protected $configCache = []; 23 | 24 | public function getConfig() 25 | { 26 | return $this->config; 27 | } 28 | 29 | protected function getConfigValue($key, $defaultValue = null) 30 | { 31 | if (array_key_exists($key, $this->configCache)) { 32 | return $this->configCache[$key]; 33 | } 34 | $this->configCache[$key] = !empty($this->config) ? $this->config->get($key, $defaultValue) : $defaultValue; 35 | return $this->configCache[$key]; 36 | } 37 | 38 | public function getDescription() 39 | { 40 | return $this->getConfigValue('description'); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/Config/Traits/ResolvableObjectTrait.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Config\Traits; 9 | 10 | use Youshido\GraphQL\Execution\ResolveInfo; 11 | use Youshido\GraphQL\Type\TypeMap; 12 | use Youshido\GraphQL\Type\TypeService; 13 | 14 | trait ResolvableObjectTrait 15 | { 16 | 17 | public function resolve($value, array $args, ResolveInfo $info) 18 | { 19 | if ($resolveFunction = $this->getConfig()->getResolveFunction()) { 20 | return $resolveFunction($value, $args, $info); 21 | } else { 22 | if (is_array($value) && array_key_exists($this->getName(), $value)) { 23 | return $value[$this->getName()]; 24 | } elseif (is_object($value)) { 25 | return TypeService::getPropertyValue($value, $this->getName()); 26 | } elseif ($this->getType()->getKind() !== TypeMap::KIND_NON_NULL) { 27 | return null; 28 | } else { 29 | throw new \Exception(sprintf('Property "%s" not found in resolve result', $this->getName())); 30 | } 31 | } 32 | } 33 | 34 | 35 | public function getResolveFunction() 36 | { 37 | return $this->getConfig()->getResolveFunction(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Config/TypeConfigInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Config; 9 | 10 | 11 | use Youshido\GraphQL\Field\Field; 12 | 13 | interface TypeConfigInterface 14 | { 15 | 16 | /** 17 | * @param Field|string $field 18 | * @param array $fieldInfo 19 | */ 20 | public function addField($field, $fieldInfo = null); 21 | 22 | public function getField($name); 23 | 24 | public function removeField($name); 25 | 26 | public function hasField($name); 27 | 28 | public function getFields(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/Directive/Directive.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Directive; 9 | 10 | use Youshido\GraphQL\Config\Directive\DirectiveConfig; 11 | use Youshido\GraphQL\Type\Traits\ArgumentsAwareObjectTrait; 12 | use Youshido\GraphQL\Type\Traits\AutoNameTrait; 13 | 14 | class Directive implements DirectiveInterface 15 | { 16 | 17 | use ArgumentsAwareObjectTrait; 18 | use AutoNameTrait; 19 | 20 | protected $isFinal = false; 21 | 22 | public function __construct(array $config = []) 23 | { 24 | if (empty($config['name'])) { 25 | $config['name'] = $this->getName(); 26 | } 27 | 28 | $this->config = new DirectiveConfig($config, $this, $this->isFinal); 29 | $this->build($this->config); 30 | } 31 | 32 | public function build(DirectiveConfig $config) 33 | { 34 | 35 | } 36 | 37 | public function addArguments($argumentsList) 38 | { 39 | return $this->getConfig()->addArguments($argumentsList); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Directive/DirectiveInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Directive; 9 | 10 | 11 | use Youshido\GraphQL\Type\AbstractType; 12 | 13 | interface DirectiveInterface 14 | { 15 | 16 | public function getName(); 17 | 18 | public function addArguments($argumentsList); 19 | 20 | public function removeArgument($argumentName); 21 | 22 | public function addArgument($argument, $ArgumentInfo = null); 23 | 24 | /** 25 | * @return AbstractType[] 26 | */ 27 | public function getArguments(); 28 | 29 | /** 30 | * @param string $argumentName 31 | * 32 | * @return AbstractType 33 | */ 34 | public function getArgument($argumentName); 35 | 36 | /** 37 | * @param string $argumentName 38 | * 39 | * @return bool 40 | */ 41 | public function hasArgument($argumentName); 42 | 43 | /** 44 | * @return boolean 45 | */ 46 | public function hasArguments(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/Directive/DirectiveLocation.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Directive; 9 | 10 | 11 | class DirectiveLocation 12 | { 13 | 14 | const QUERY = 'QUERY'; 15 | const MUTATION = 'MUTATION'; 16 | const FIELD = 'FIELD'; 17 | const FIELD_DEFINITION = 'FIELD_DEFINITION'; 18 | const FRAGMENT_DEFINITION = 'FRAGMENT_DEFINITION'; 19 | const FRAGMENT_SPREAD = 'FRAGMENT_SPREAD'; 20 | const INLINE_FRAGMENT = 'INLINE_FRAGMENT'; 21 | const ENUM_VALUE = 'ENUM_VALUE'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/ConfigurationException.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 2:28 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Exception; 10 | 11 | 12 | class ConfigurationException extends \Exception 13 | { 14 | 15 | } -------------------------------------------------------------------------------- /src/Exception/Interfaces/ExtendedExceptionInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Exception\Interfaces; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Location; 12 | 13 | interface LocationableExceptionInterface 14 | { 15 | 16 | /** 17 | * @return Location 18 | */ 19 | public function getLocation(); 20 | 21 | } -------------------------------------------------------------------------------- /src/Exception/Parser/AbstractParserError.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Exception\Parser; 9 | 10 | 11 | use Youshido\GraphQL\Exception\Interfaces\LocationableExceptionInterface; 12 | use Youshido\GraphQL\Parser\Location; 13 | 14 | abstract class AbstractParserError extends \Exception implements LocationableExceptionInterface 15 | { 16 | 17 | /** @var Location */ 18 | private $location; 19 | 20 | public function __construct($message, Location $location) 21 | { 22 | parent::__construct($message); 23 | 24 | $this->location = $location; 25 | } 26 | 27 | public function getLocation() 28 | { 29 | return $this->location; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Exception/Parser/InvalidRequestException.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Exception\Parser; 9 | 10 | class InvalidRequestException extends AbstractParserError 11 | { 12 | 13 | } -------------------------------------------------------------------------------- /src/Exception/Parser/SyntaxErrorException.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Exception\Parser; 9 | 10 | class SyntaxErrorException extends AbstractParserError 11 | { 12 | 13 | } -------------------------------------------------------------------------------- /src/Exception/ResolveException.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Exception; 9 | 10 | 11 | use Youshido\GraphQL\Exception\Interfaces\LocationableExceptionInterface; 12 | use Youshido\GraphQL\Parser\Location; 13 | 14 | class ResolveException extends \Exception implements LocationableExceptionInterface 15 | { 16 | 17 | /** @var Location */ 18 | private $location; 19 | 20 | public function __construct($message, Location $location = null) 21 | { 22 | parent::__construct($message); 23 | 24 | $this->location = $location; 25 | } 26 | 27 | 28 | /** 29 | * @return Location 30 | */ 31 | public function getLocation() 32 | { 33 | return $this->location; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Exception/ValidationException.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Exception; 9 | 10 | 11 | class ValidationException extends \Exception 12 | { 13 | 14 | } -------------------------------------------------------------------------------- /src/Execution/Container/Container.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 9/22/16 7:00 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Execution\Container; 10 | 11 | class Container implements ContainerInterface 12 | { 13 | 14 | private $keyset = []; 15 | private $values = []; 16 | private $services = []; 17 | 18 | 19 | /** 20 | * @param $id 21 | * @return mixed 22 | * @throws \Exception if there was no value set under specified id 23 | */ 24 | public function get($id) 25 | { 26 | $this->assertIdentifierSet($id); 27 | if (isset($this->services['id'])) { 28 | return $this->services['id']($this); 29 | } 30 | return $this->values[$id]; 31 | } 32 | 33 | public function set($id, $value) 34 | { 35 | $this->values[$id] = $value; 36 | $this->keyset[$id] = true; 37 | return $this; 38 | } 39 | 40 | protected function setAsService($id, $service) 41 | { 42 | if (!is_object($service)) { 43 | throw new \RuntimeException(sprintf('Service %s has to be an object', $id)); 44 | } 45 | 46 | $this->services[$id] = $service; 47 | if (isset($this->values[$id])) { 48 | unset($this->values[$id]); 49 | } 50 | $this->keyset[$id] = true; 51 | } 52 | 53 | public function remove($id) 54 | { 55 | $this->assertIdentifierSet($id); 56 | if (array_key_exists($id, $this->values)) { 57 | unset($this->values[$id]); 58 | } 59 | if (array_key_exists($id, $this->services)) { 60 | unset($this->services[$id]); 61 | } 62 | } 63 | 64 | public function has($id) 65 | { 66 | return isset($this->keyset[$id]); 67 | } 68 | 69 | private function assertIdentifierSet($id) 70 | { 71 | if (!$this->has($id)) { 72 | throw new \RuntimeException(sprintf('Container item "%s" was not set', $id)); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /src/Execution/Container/ContainerInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Execution\Context; 9 | 10 | 11 | use Youshido\GraphQL\Execution\Container\ContainerInterface; 12 | use Youshido\GraphQL\Execution\Request; 13 | use Youshido\GraphQL\Schema\AbstractSchema; 14 | use Youshido\GraphQL\Validator\ErrorContainer\ErrorContainerInterface; 15 | 16 | interface ExecutionContextInterface extends ErrorContainerInterface 17 | { 18 | 19 | /** 20 | * @return AbstractSchema 21 | */ 22 | public function getSchema(); 23 | 24 | /** 25 | * @param AbstractSchema $schema 26 | * 27 | * @return $this 28 | */ 29 | public function setSchema(AbstractSchema $schema); 30 | 31 | /** 32 | * @return Request 33 | */ 34 | public function getRequest(); 35 | 36 | /** 37 | * @param Request $request 38 | * 39 | * @return $this 40 | */ 41 | public function setRequest(Request $request); 42 | 43 | /** 44 | * @return ContainerInterface 45 | */ 46 | public function getContainer(); 47 | 48 | /** 49 | * @param ContainerInterface $container 50 | * @return mixed 51 | */ 52 | public function setContainer(ContainerInterface $container); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/Execution/DeferredResolver.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4/24/17 11:50 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Execution; 10 | 11 | 12 | /** 13 | * Default implementation of DeferredResolverInterface 14 | * 15 | * @package Youshido\GraphQL\Execution 16 | */ 17 | class DeferredResolver implements DeferredResolverInterface { 18 | 19 | /** @var callable */ 20 | private $resolver; 21 | 22 | public function __construct($resolver) 23 | { 24 | $this->resolver = $resolver; 25 | } 26 | 27 | public function resolve() { 28 | return call_user_func($this->resolver); 29 | } 30 | } -------------------------------------------------------------------------------- /src/Execution/DeferredResolverInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 7/25/17 12:34 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Execution; 10 | 11 | /** 12 | * Interface definition for deferred resolvers. 13 | * 14 | * Fields may return a value implementing this interface to use deferred 15 | * resolving to optimize query performance. 16 | */ 17 | interface DeferredResolverInterface { 18 | 19 | /** 20 | * @return mixed 21 | * The actual result value. 22 | */ 23 | public function resolve(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/Execution/DeferredResult.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 7/25/17 12:34 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Execution; 10 | 11 | 12 | /** 13 | * Wrapper class for deferred resolvers during execution process. 14 | * Not part of the public API. 15 | * 16 | * @internal 17 | */ 18 | class DeferredResult implements DeferredResolverInterface { 19 | 20 | /** @var \Youshido\GraphQL\Execution\DeferredResolver */ 21 | private $resolver; 22 | 23 | /** @var callable */ 24 | protected $callback; 25 | 26 | /** @var mixed */ 27 | public $result; 28 | 29 | public function __construct(DeferredResolverInterface $resolver, callable $callback) 30 | { 31 | $this->resolver = $resolver; 32 | $this->callback = $callback; 33 | } 34 | 35 | public function resolve() { 36 | $this->result = call_user_func($this->callback, $this->resolver->resolve()); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Execution/Visitor/AbstractQueryVisitor.php: -------------------------------------------------------------------------------- 1 | memo. A reasonable thing to do is raise an 20 | * exception if some limit is reached. (see MaxComplexityQueryVisitor for example concrete implementation) 21 | * 22 | * @author Ben Roberts 23 | * created: 7/11/16 11:03 AM 24 | */ 25 | 26 | namespace Youshido\GraphQL\Execution\Visitor; 27 | 28 | use Youshido\GraphQL\Config\Field\FieldConfig; 29 | 30 | abstract class AbstractQueryVisitor 31 | { 32 | 33 | /** 34 | * @var int initial value of $this->memo 35 | */ 36 | protected $initialValue = 0; 37 | 38 | /** 39 | * @var mixed the accumulator 40 | */ 41 | protected $memo; 42 | 43 | /** 44 | * AbstractQueryVisitor constructor. 45 | */ 46 | public function __construct() 47 | { 48 | $this->memo = $this->initialValue; 49 | } 50 | 51 | /** 52 | * @return mixed getter for the memo, in case callers want to inspect it after a process run 53 | */ 54 | public function getMemo() 55 | { 56 | return $this->memo; 57 | } 58 | 59 | /** 60 | * Visit a query node. See class docstring. 61 | * 62 | * @param array $args 63 | * @param FieldConfig $fieldConfig 64 | * @param int $childScore 65 | * 66 | * @return int|null 67 | */ 68 | abstract public function visit(array $args, FieldConfig $fieldConfig, $childScore = 0); 69 | } -------------------------------------------------------------------------------- /src/Execution/Visitor/MaxComplexityQueryVisitor.php: -------------------------------------------------------------------------------- 1 | 9 | * created: 7/11/16 11:05 AM 10 | */ 11 | 12 | namespace Youshido\GraphQL\Execution\Visitor; 13 | 14 | 15 | use Youshido\GraphQL\Config\Field\FieldConfig; 16 | 17 | class MaxComplexityQueryVisitor extends AbstractQueryVisitor 18 | { 19 | 20 | /** 21 | * @var int max score allowed before throwing an exception (causing processing to stop) 22 | */ 23 | public $maxScore; 24 | 25 | /** 26 | * @var int default score for nodes without explicit cost functions 27 | */ 28 | protected $defaultScore = 1; 29 | 30 | /** 31 | * MaxComplexityQueryVisitor constructor. 32 | * 33 | * @param int $max max allowed complexity score 34 | */ 35 | public function __construct($max) 36 | { 37 | parent::__construct(); 38 | 39 | $this->maxScore = $max; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function visit(array $args, FieldConfig $fieldConfig, $childScore = 0) 46 | { 47 | $cost = $fieldConfig->get('cost', null); 48 | if (is_callable($cost)) { 49 | $cost = $cost($args, $fieldConfig, $childScore); 50 | } 51 | 52 | $cost = is_null($cost) ? $this->defaultScore : $cost; 53 | $this->memo += $cost; 54 | 55 | if ($this->memo > $this->maxScore) { 56 | throw new \Exception('query exceeded max allowed complexity of ' . $this->maxScore); 57 | } 58 | 59 | return $cost; 60 | } 61 | } -------------------------------------------------------------------------------- /src/Field/AbstractInputField.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Field; 9 | 10 | 11 | use Youshido\GraphQL\Config\Field\InputFieldConfig; 12 | use Youshido\GraphQL\Type\InputTypeInterface; 13 | use Youshido\GraphQL\Type\Traits\AutoNameTrait; 14 | use Youshido\GraphQL\Type\Traits\FieldsArgumentsAwareObjectTrait; 15 | use Youshido\GraphQL\Type\TypeFactory; 16 | use Youshido\GraphQL\Type\TypeService; 17 | 18 | abstract class AbstractInputField implements InputFieldInterface 19 | { 20 | 21 | use FieldsArgumentsAwareObjectTrait, AutoNameTrait; 22 | 23 | protected $isFinal = false; 24 | 25 | public function __construct(array $config = []) 26 | { 27 | if (empty($config['type'])) { 28 | $config['type'] = $this->getType(); 29 | $config['name'] = $this->getName(); 30 | } 31 | 32 | if (TypeService::isScalarType($config['type'])) { 33 | $config['type'] = TypeFactory::getScalarType($config['type']); 34 | } 35 | 36 | $this->config = new InputFieldConfig($config, $this, $this->isFinal); 37 | $this->build($this->config); 38 | } 39 | 40 | public function build(InputFieldConfig $config) 41 | { 42 | 43 | } 44 | 45 | /** 46 | * @return InputTypeInterface 47 | */ 48 | abstract public function getType(); 49 | 50 | public function getDefaultValue() 51 | { 52 | return $this->config->getDefaultValue(); 53 | } 54 | 55 | //todo: think about serialize, parseValue methods 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/Field/Field.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Field; 9 | 10 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 11 | 12 | /** 13 | * Class Field 14 | * @package Youshido\GraphQL\Type\Field 15 | * 16 | */ 17 | final class Field extends AbstractField 18 | { 19 | 20 | protected $isFinal = true; 21 | 22 | protected $_typeCache = null; 23 | protected $_nameCache = null; 24 | 25 | /** 26 | * @return AbstractObjectType 27 | */ 28 | public function getType() 29 | { 30 | return $this->_typeCache ? $this->_typeCache : ($this->_typeCache = $this->getConfigValue('type')); 31 | } 32 | 33 | public function getName() 34 | { 35 | return $this->_nameCache ? $this->_nameCache : ($this->_nameCache = $this->getConfigValue('name')); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/Field/FieldInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Field; 9 | 10 | 11 | use Youshido\GraphQL\Execution\ResolveInfo; 12 | 13 | interface FieldInterface extends InputFieldInterface 14 | { 15 | public function resolve($value, array $args, ResolveInfo $info); 16 | 17 | public function getResolveFunction(); 18 | } 19 | -------------------------------------------------------------------------------- /src/Field/InputField.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/1/15 11:26 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Field; 10 | 11 | 12 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 13 | 14 | final class InputField extends AbstractInputField 15 | { 16 | 17 | protected $isFinal = false; 18 | 19 | /** 20 | * @return AbstractObjectType 21 | */ 22 | public function getType() 23 | { 24 | return $this->getConfigValue('type'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Field/InputFieldInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 9/29/16 10:32 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Field; 10 | 11 | 12 | use Youshido\GraphQL\Type\AbstractType; 13 | 14 | interface InputFieldInterface 15 | { 16 | /** 17 | * @return AbstractType 18 | */ 19 | public function getType(); 20 | 21 | public function getName(); 22 | 23 | public function addArguments($argumentsList); 24 | 25 | public function removeArgument($argumentName); 26 | 27 | public function addArgument($argument, $ArgumentInfo = null); 28 | 29 | /** 30 | * @return AbstractType[] 31 | */ 32 | public function getArguments(); 33 | 34 | /** 35 | * @param string $argumentName 36 | * 37 | * @return AbstractType 38 | */ 39 | public function getArgument($argumentName); 40 | 41 | /** 42 | * @param string $argumentName 43 | * 44 | * @return bool 45 | */ 46 | public function hasArgument($argumentName); 47 | 48 | /** 49 | * @return boolean 50 | */ 51 | public function hasArguments(); 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/Introspection/DirectiveLocationType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Introspection; 9 | 10 | use Youshido\GraphQL\Directive\DirectiveLocation; 11 | use Youshido\GraphQL\Type\Enum\AbstractEnumType; 12 | 13 | class DirectiveLocationType extends AbstractEnumType 14 | { 15 | 16 | const QUERY = DirectiveLocation::QUERY; 17 | const MUTATION = DirectiveLocation::MUTATION; 18 | const FIELD = DirectiveLocation::FIELD; 19 | const FIELD_DEFINITION = DirectiveLocation::FIELD_DEFINITION; 20 | const FRAGMENT_DEFINITION = DirectiveLocation::FRAGMENT_DEFINITION; 21 | const FRAGMENT_SPREAD = DirectiveLocation::FRAGMENT_SPREAD; 22 | const INLINE_FRAGMENT = DirectiveLocation::INLINE_FRAGMENT; 23 | const ENUM_VALUE = DirectiveLocation::ENUM_VALUE; 24 | 25 | public function getName() 26 | { 27 | return '__DirectiveLocation'; 28 | } 29 | 30 | public function getValues() 31 | { 32 | return [ 33 | ['name' => 'QUERY', 'value' => self::QUERY], 34 | ['name' => 'MUTATION', 'value' => self::MUTATION], 35 | ['name' => 'FIELD', 'value' => self::FIELD], 36 | ['name' => 'FIELD_DEFINITION', 'value' => self::FIELD_DEFINITION], 37 | ['name' => 'FRAGMENT_DEFINITION', 'value' => self::FRAGMENT_DEFINITION], 38 | ['name' => 'FRAGMENT_SPREAD', 'value' => self::FRAGMENT_SPREAD], 39 | ['name' => 'INLINE_FRAGMENT', 'value' => self::INLINE_FRAGMENT], 40 | ['name' => 'ENUM_VALUE', 'value' => self::ENUM_VALUE], 41 | ]; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/Introspection/DirectiveType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Introspection; 9 | 10 | use Youshido\GraphQL\Config\Directive\DirectiveConfig; 11 | use Youshido\GraphQL\Directive\Directive; 12 | use Youshido\GraphQL\Directive\DirectiveInterface; 13 | use Youshido\GraphQL\Type\ListType\ListType; 14 | use Youshido\GraphQL\Type\NonNullType; 15 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 16 | use Youshido\GraphQL\Type\TypeMap; 17 | 18 | class DirectiveType extends AbstractObjectType 19 | { 20 | 21 | /** 22 | * @return String type name 23 | */ 24 | public function getName() 25 | { 26 | return '__Directive'; 27 | } 28 | 29 | public function resolveArgs(DirectiveInterface $value) 30 | { 31 | if ($value->hasArguments()) { 32 | return $value->getArguments(); 33 | } 34 | 35 | return []; 36 | } 37 | 38 | /** 39 | * @param DirectiveInterface|Directive $value 40 | * 41 | * @return mixed 42 | */ 43 | public function resolveLocations(DirectiveInterface $value) 44 | { 45 | /** @var DirectiveConfig $directiveConfig */ 46 | $directiveConfig = $value->getConfig(); 47 | 48 | $locations = $directiveConfig->getLocations(); 49 | 50 | return $locations; 51 | } 52 | 53 | public function build($config) 54 | { 55 | $config 56 | ->addField('name', new NonNullType(TypeMap::TYPE_STRING)) 57 | ->addField('description', TypeMap::TYPE_STRING) 58 | ->addField('args', [ 59 | 'type' => new NonNullType(new ListType(new NonNullType(new InputValueType()))), 60 | 'resolve' => [$this, 'resolveArgs'], 61 | ]) 62 | ->addField('locations',[ 63 | 'type' => new NonNullType(new ListType(new NonNullType(new DirectiveLocationType()))), 64 | 'resolve' => [$this, 'resolveLocations'], 65 | ]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Introspection/EnumValueType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Introspection; 9 | 10 | 11 | use Youshido\GraphQL\Type\NonNullType; 12 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 13 | use Youshido\GraphQL\Type\TypeMap; 14 | 15 | class EnumValueType extends AbstractObjectType 16 | { 17 | 18 | public function build($config) 19 | { 20 | $config 21 | ->addField('name', new NonNullType(TypeMap::TYPE_STRING)) 22 | ->addField('description', TypeMap::TYPE_STRING) 23 | ->addField('deprecationReason', TypeMap::TYPE_STRING) 24 | ->addField('isDeprecated', new NonNullType(TypeMap::TYPE_BOOLEAN)); 25 | } 26 | 27 | /** 28 | * @return String type name 29 | */ 30 | public function getName() 31 | { 32 | return '__EnumValue'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Introspection/Field/SchemaField.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Introspection\Field; 9 | 10 | 11 | use Youshido\GraphQL\Execution\ResolveInfo; 12 | use Youshido\GraphQL\Field\AbstractField; 13 | use Youshido\GraphQL\Introspection\SchemaType; 14 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 15 | 16 | class SchemaField extends AbstractField 17 | { 18 | /** 19 | * @return AbstractObjectType 20 | */ 21 | public function getType() 22 | { 23 | return new SchemaType(); 24 | } 25 | 26 | public function getName() 27 | { 28 | return '__schema'; 29 | } 30 | 31 | public function resolve($value, array $args, ResolveInfo $info) 32 | { 33 | return $info->getExecutionContext()->getSchema(); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/Introspection/Field/TypeDefinitionField.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Introspection\Field; 9 | 10 | use Youshido\GraphQL\Config\Field\FieldConfig; 11 | use Youshido\GraphQL\Execution\ResolveInfo; 12 | use Youshido\GraphQL\Field\AbstractField; 13 | use Youshido\GraphQL\Field\InputField; 14 | use Youshido\GraphQL\Introspection\QueryType; 15 | use Youshido\GraphQL\Introspection\Traits\TypeCollectorTrait; 16 | use Youshido\GraphQL\Type\NonNullType; 17 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 18 | use Youshido\GraphQL\Type\Scalar\StringType; 19 | 20 | class TypeDefinitionField extends AbstractField 21 | { 22 | 23 | use TypeCollectorTrait; 24 | 25 | public function resolve($value, array $args, ResolveInfo $info) 26 | { 27 | $schema = $info->getExecutionContext()->getSchema(); 28 | $this->collectTypes($schema->getQueryType()); 29 | $this->collectTypes($schema->getMutationType()); 30 | 31 | foreach ($schema->getTypesList()->getTypes() as $type) { 32 | $this->collectTypes($type); 33 | } 34 | 35 | foreach ($this->types as $name => $info) { 36 | if ($name == $args['name']) { 37 | return $info; 38 | } 39 | } 40 | 41 | return null; 42 | } 43 | 44 | public function build(FieldConfig $config) 45 | { 46 | $config->addArgument(new InputField([ 47 | 'name' => 'name', 48 | 'type' => new NonNullType(new StringType()) 49 | ])); 50 | } 51 | 52 | 53 | /** 54 | * @return String type name 55 | */ 56 | public function getName() 57 | { 58 | return '__type'; 59 | } 60 | 61 | /** 62 | * @return AbstractObjectType 63 | */ 64 | public function getType() 65 | { 66 | return new QueryType(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Introspection/Field/TypesField.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Introspection\Field; 9 | 10 | 11 | use Youshido\GraphQL\Execution\ResolveInfo; 12 | use Youshido\GraphQL\Field\AbstractField; 13 | use Youshido\GraphQL\Introspection\QueryType; 14 | use Youshido\GraphQL\Introspection\Traits\TypeCollectorTrait; 15 | use Youshido\GraphQL\Schema\AbstractSchema; 16 | use Youshido\GraphQL\Type\ListType\ListType; 17 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 18 | 19 | class TypesField extends AbstractField 20 | { 21 | 22 | use TypeCollectorTrait; 23 | 24 | /** 25 | * @return AbstractObjectType 26 | */ 27 | public function getType() 28 | { 29 | return new ListType(new QueryType()); 30 | } 31 | 32 | public function getName() 33 | { 34 | return 'types'; 35 | } 36 | 37 | public function resolve($value, array $args, ResolveInfo $info) 38 | { 39 | /** @var $value AbstractSchema $a */ 40 | $this->types = []; 41 | $this->collectTypes($value->getQueryType()); 42 | 43 | if ($value->getMutationType()->hasFields()) { 44 | $this->collectTypes($value->getMutationType()); 45 | } 46 | 47 | foreach ($value->getTypesList()->getTypes() as $type) { 48 | $this->collectTypes($type); 49 | } 50 | 51 | return array_values($this->types); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/Introspection/FieldType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Introspection; 9 | 10 | use Youshido\GraphQL\Field\FieldInterface; 11 | use Youshido\GraphQL\Type\ListType\ListType; 12 | use Youshido\GraphQL\Type\NonNullType; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\TypeMap; 15 | 16 | class FieldType extends AbstractObjectType 17 | { 18 | 19 | public function resolveType(FieldInterface $value) 20 | { 21 | return $value->getType(); 22 | } 23 | 24 | public function resolveArgs(FieldInterface $value) 25 | { 26 | if ($value->hasArguments()) { 27 | return $value->getArguments(); 28 | } 29 | 30 | return []; 31 | } 32 | 33 | public function build($config) 34 | { 35 | $config 36 | ->addField('name', new NonNullType(TypeMap::TYPE_STRING)) 37 | ->addField('description', TypeMap::TYPE_STRING) 38 | ->addField('isDeprecated', new NonNullType(TypeMap::TYPE_BOOLEAN)) 39 | ->addField('deprecationReason', TypeMap::TYPE_STRING) 40 | ->addField('type', [ 41 | 'type' => new NonNullType(new QueryType()), 42 | 'resolve' => [$this, 'resolveType'], 43 | ]) 44 | ->addField('args', [ 45 | 'type' => new NonNullType(new ListType(new NonNullType(new InputValueType()))), 46 | 'resolve' => [$this, 'resolveArgs'], 47 | ]); 48 | } 49 | 50 | public function isValidValue($value) 51 | { 52 | return $value instanceof FieldInterface; 53 | } 54 | 55 | /** 56 | * @return String type name 57 | */ 58 | public function getName() 59 | { 60 | return '__Field'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Introspection/InputValueType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Introspection; 9 | 10 | use Youshido\GraphQL\Field\Field; 11 | use Youshido\GraphQL\Schema\AbstractSchema; 12 | use Youshido\GraphQL\Type\NonNullType; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\TypeInterface; 15 | use Youshido\GraphQL\Type\TypeMap; 16 | 17 | class InputValueType extends AbstractObjectType 18 | { 19 | /** 20 | * @param AbstractSchema|Field $value 21 | * 22 | * @return TypeInterface 23 | */ 24 | public function resolveType($value) 25 | { 26 | return $value->getConfig()->getType(); 27 | } 28 | 29 | /** 30 | * @param AbstractSchema|Field $value 31 | * 32 | * @return string|null 33 | * 34 | * //todo implement value printer 35 | */ 36 | public function resolveDefaultValue($value) 37 | { 38 | $resolvedValue = $value->getConfig()->getDefaultValue(); 39 | return $resolvedValue === null ? $resolvedValue : str_replace('"', '', json_encode($resolvedValue)); 40 | } 41 | 42 | public function build($config) 43 | { 44 | $config 45 | ->addField('name', new NonNullType(TypeMap::TYPE_STRING)) 46 | ->addField('description', TypeMap::TYPE_STRING) 47 | ->addField('isDeprecated', new NonNullType(TypeMap::TYPE_BOOLEAN)) 48 | ->addField('deprecationReason', TypeMap::TYPE_STRING) 49 | ->addField(new Field([ 50 | 'name' => 'type', 51 | 'type' => new NonNullType(new QueryType()), 52 | 'resolve' => [$this, 'resolveType'] 53 | ])) 54 | ->addField('defaultValue', [ 55 | 'type' => TypeMap::TYPE_STRING, 56 | 'resolve' => [$this, 'resolveDefaultValue'] 57 | ]); 58 | } 59 | 60 | /** 61 | * @return string type name 62 | */ 63 | public function getName() 64 | { 65 | return '__InputValue'; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Parser/Ast/AbstractAst.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\Interfaces\LocatableInterface; 12 | use Youshido\GraphQL\Parser\Location; 13 | 14 | abstract class AbstractAst implements LocatableInterface 15 | { 16 | 17 | /** @var Location */ 18 | private $location; 19 | 20 | public function __construct(Location $location) 21 | { 22 | $this->location = $location; 23 | } 24 | 25 | public function getLocation() 26 | { 27 | return $this->location; 28 | } 29 | 30 | public function setLocation(Location $location) 31 | { 32 | $this->location = $location; 33 | } 34 | } -------------------------------------------------------------------------------- /src/Parser/Ast/Argument.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\Interfaces\ValueInterface; 12 | use Youshido\GraphQL\Parser\Location; 13 | 14 | class Argument extends AbstractAst 15 | { 16 | 17 | /** @var string */ 18 | private $name; 19 | 20 | /** @var ValueInterface */ 21 | private $value; 22 | 23 | /** 24 | * @param string $name 25 | * @param ValueInterface $value 26 | * @param Location $location 27 | */ 28 | public function __construct($name, ValueInterface $value, Location $location) 29 | { 30 | parent::__construct($location); 31 | 32 | $this->name = $name; 33 | $this->value = $value; 34 | } 35 | 36 | /** 37 | * @return mixed 38 | */ 39 | public function getName() 40 | { 41 | return $this->name; 42 | } 43 | 44 | /** 45 | * @param mixed $name 46 | */ 47 | public function setName($name) 48 | { 49 | $this->name = $name; 50 | } 51 | 52 | /** 53 | * @return \Youshido\GraphQL\Parser\Ast\Interfaces\ValueInterface 54 | */ 55 | public function getValue() 56 | { 57 | return $this->value; 58 | } 59 | 60 | /** 61 | * @param mixed $value 62 | */ 63 | public function setValue($value) 64 | { 65 | $this->value = $value; 66 | } 67 | 68 | 69 | } -------------------------------------------------------------------------------- /src/Parser/Ast/ArgumentValue/InputList.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast\ArgumentValue; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\AbstractAst; 12 | use Youshido\GraphQL\Parser\Ast\Interfaces\ValueInterface; 13 | use Youshido\GraphQL\Parser\Location; 14 | 15 | class InputList extends AbstractAst implements ValueInterface 16 | { 17 | 18 | protected $list = []; 19 | 20 | /** 21 | * @param array $list 22 | * @param Location $location 23 | */ 24 | public function __construct(array $list, Location $location) 25 | { 26 | parent::__construct($location); 27 | 28 | $this->list = $list; 29 | } 30 | 31 | /** 32 | * @return array 33 | */ 34 | public function getValue() 35 | { 36 | return $this->list; 37 | } 38 | 39 | /** 40 | * @param array $value 41 | */ 42 | public function setValue($value) 43 | { 44 | $this->list = $value; 45 | } 46 | } -------------------------------------------------------------------------------- /src/Parser/Ast/ArgumentValue/InputObject.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast\ArgumentValue; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\AbstractAst; 12 | use Youshido\GraphQL\Parser\Ast\Interfaces\ValueInterface; 13 | use Youshido\GraphQL\Parser\Location; 14 | 15 | class InputObject extends AbstractAst implements ValueInterface 16 | { 17 | 18 | protected $object = []; 19 | 20 | /** 21 | * @param array $object 22 | * @param Location $location 23 | */ 24 | public function __construct(array $object, Location $location) 25 | { 26 | parent::__construct($location); 27 | 28 | $this->object = $object; 29 | } 30 | 31 | /** 32 | * @return array 33 | */ 34 | public function getValue() 35 | { 36 | return $this->object; 37 | } 38 | 39 | /** 40 | * @param array $value 41 | */ 42 | public function setValue($value) 43 | { 44 | $this->object = $value; 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/Parser/Ast/ArgumentValue/Literal.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast\ArgumentValue; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\AbstractAst; 12 | use Youshido\GraphQL\Parser\Ast\Interfaces\ValueInterface; 13 | use Youshido\GraphQL\Parser\Location; 14 | 15 | class Literal extends AbstractAst implements ValueInterface 16 | { 17 | 18 | private $value; 19 | 20 | /** 21 | * @param mixed $value 22 | * @param Location $location 23 | */ 24 | public function __construct($value, Location $location) 25 | { 26 | parent::__construct($location); 27 | 28 | $this->value = $value; 29 | } 30 | 31 | public function getValue() 32 | { 33 | return $this->value; 34 | } 35 | 36 | /** 37 | * @param string $value 38 | */ 39 | public function setValue($value) 40 | { 41 | $this->value = $value; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Parser/Ast/ArgumentValue/VariableReference.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast\ArgumentValue; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\AbstractAst; 12 | use Youshido\GraphQL\Parser\Ast\Interfaces\ValueInterface; 13 | use Youshido\GraphQL\Parser\Location; 14 | 15 | class VariableReference extends AbstractAst implements ValueInterface 16 | { 17 | 18 | /** @var string */ 19 | private $name; 20 | 21 | /** @var Variable */ 22 | private $variable; 23 | 24 | /** @var mixed */ 25 | private $value; 26 | 27 | /** 28 | * @param string $name 29 | * @param Variable|null $variable 30 | * @param Location $location 31 | */ 32 | public function __construct($name, Variable $variable = null, Location $location) 33 | { 34 | parent::__construct($location); 35 | 36 | $this->name = $name; 37 | $this->variable = $variable; 38 | } 39 | 40 | public function getVariable() 41 | { 42 | return $this->variable; 43 | } 44 | 45 | public function getValue() 46 | { 47 | return $this->value; 48 | } 49 | 50 | public function setValue($value) 51 | { 52 | $this->value = $value; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getName() 59 | { 60 | return $this->name; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Parser/Ast/AstDirectivesTrait.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast; 9 | 10 | 11 | trait AstDirectivesTrait 12 | { 13 | 14 | /** @var Directive[] */ 15 | protected $directives; 16 | 17 | private $directivesCache = null; 18 | 19 | 20 | public function hasDirectives() 21 | { 22 | return (bool)count($this->directives); 23 | } 24 | 25 | public function hasDirective($name) 26 | { 27 | return array_key_exists($name, $this->directives); 28 | } 29 | 30 | /** 31 | * @param $name 32 | * 33 | * @return null|Directive 34 | */ 35 | public function getDirective($name) 36 | { 37 | $directive = null; 38 | if (isset($this->directives[$name])) { 39 | $directive = $this->directives[$name]; 40 | } 41 | 42 | return $directive; 43 | } 44 | 45 | /** 46 | * @return Directive[] 47 | */ 48 | public function getDirectives() 49 | { 50 | return $this->directives; 51 | } 52 | 53 | /** 54 | * @param $directives Directive[] 55 | */ 56 | public function setDirectives(array $directives) 57 | { 58 | $this->directives = []; 59 | $this->directivesCache = null; 60 | 61 | foreach ($directives as $directive) { 62 | $this->addDirective($directive); 63 | } 64 | } 65 | 66 | public function addDirective(Directive $directive) 67 | { 68 | $this->directives[$directive->getName()] = $directive; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Parser/Ast/Directive.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Location; 12 | 13 | class Directive extends AbstractAst 14 | { 15 | use AstArgumentsTrait; 16 | 17 | /** @var string */ 18 | private $name; 19 | 20 | 21 | /** 22 | * @param string $name 23 | * @param array $arguments 24 | * @param Location $location 25 | */ 26 | public function __construct($name, array $arguments, Location $location) 27 | { 28 | parent::__construct($location); 29 | 30 | $this->name = $name; 31 | $this->setArguments($arguments); 32 | } 33 | 34 | /** 35 | * @return mixed 36 | */ 37 | public function getName() 38 | { 39 | return $this->name; 40 | } 41 | 42 | /** 43 | * @param mixed $name 44 | */ 45 | public function setName($name) 46 | { 47 | $this->name = $name; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/Parser/Ast/Field.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\Interfaces\FieldInterface; 12 | use Youshido\GraphQL\Parser\Location; 13 | 14 | class Field extends AbstractAst implements FieldInterface 15 | { 16 | use AstArgumentsTrait; 17 | use AstDirectivesTrait; 18 | 19 | /** @var string */ 20 | private $name; 21 | 22 | /** @var string */ 23 | private $alias; 24 | 25 | /** 26 | * @param string $name 27 | * @param string $alias 28 | * @param array $arguments 29 | * @param array $directives 30 | * @param Location $location 31 | */ 32 | public function __construct($name, $alias, array $arguments, array $directives, Location $location) 33 | { 34 | parent::__construct($location); 35 | 36 | $this->name = $name; 37 | $this->alias = $alias; 38 | $this->setArguments($arguments); 39 | $this->setDirectives($directives); 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getName() 46 | { 47 | return $this->name; 48 | } 49 | 50 | /** 51 | * @param string $name 52 | */ 53 | public function setName($name) 54 | { 55 | $this->name = $name; 56 | } 57 | 58 | /** 59 | * @return null|string 60 | */ 61 | public function getAlias() 62 | { 63 | return $this->alias; 64 | } 65 | 66 | /** 67 | * @param null|string $alias 68 | */ 69 | public function setAlias($alias) 70 | { 71 | $this->alias = $alias; 72 | } 73 | 74 | public function hasFields() 75 | { 76 | return false; 77 | } 78 | 79 | public function getFields() 80 | { 81 | return []; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/Parser/Ast/FragmentReference.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\Interfaces\FragmentInterface; 12 | use Youshido\GraphQL\Parser\Location; 13 | 14 | class FragmentReference extends AbstractAst implements FragmentInterface 15 | { 16 | 17 | /** @var string */ 18 | protected $name; 19 | 20 | /** 21 | * @param string $name 22 | * @param Location $location 23 | */ 24 | public function __construct($name, Location $location) 25 | { 26 | parent::__construct($location); 27 | 28 | $this->name = $name; 29 | } 30 | 31 | /** 32 | * @return string 33 | */ 34 | public function getName() 35 | { 36 | return $this->name; 37 | } 38 | 39 | /** 40 | * @param string $name 41 | */ 42 | public function setName($name) 43 | { 44 | $this->name = $name; 45 | } 46 | 47 | 48 | } -------------------------------------------------------------------------------- /src/Parser/Ast/Interfaces/FieldInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast\Interfaces; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\Argument; 12 | 13 | interface FieldInterface extends LocatableInterface 14 | { 15 | 16 | /** 17 | * @return string 18 | */ 19 | public function getName(); 20 | 21 | /** 22 | * @return string 23 | */ 24 | public function getAlias(); 25 | 26 | /** 27 | * @return Argument[] 28 | */ 29 | public function getArguments(); 30 | 31 | /** 32 | * @param string $name 33 | * 34 | * @return Argument 35 | */ 36 | public function getArgument($name); 37 | 38 | /** 39 | * @return bool 40 | */ 41 | public function hasFields(); 42 | 43 | /** 44 | * @return array 45 | */ 46 | public function getFields(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/Parser/Ast/Interfaces/FragmentInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12:26 PM 5/14/16 7 | */ 8 | 9 | namespace Youshido\GraphQL\Parser\Ast\Interfaces; 10 | 11 | 12 | interface FragmentInterface 13 | { 14 | 15 | } -------------------------------------------------------------------------------- /src/Parser/Ast/Interfaces/LocatableInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast\Interfaces; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Location; 12 | 13 | interface LocatableInterface 14 | { 15 | 16 | /** 17 | * @return Location 18 | */ 19 | public function getLocation(); 20 | 21 | } -------------------------------------------------------------------------------- /src/Parser/Ast/Interfaces/ValueInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast\Interfaces; 9 | 10 | 11 | interface ValueInterface 12 | { 13 | 14 | public function getValue(); 15 | 16 | public function setValue($value); 17 | } -------------------------------------------------------------------------------- /src/Parser/Ast/Mutation.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast; 9 | 10 | 11 | class Mutation extends Query 12 | { 13 | 14 | } -------------------------------------------------------------------------------- /src/Parser/Ast/TypedFragmentReference.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser\Ast; 9 | 10 | 11 | use Youshido\GraphQL\Parser\Ast\Interfaces\FragmentInterface; 12 | use Youshido\GraphQL\Parser\Location; 13 | 14 | class TypedFragmentReference extends AbstractAst implements FragmentInterface 15 | { 16 | use AstDirectivesTrait; 17 | 18 | /** @var Field[]|Query[] */ 19 | protected $fields; 20 | 21 | /** @var string */ 22 | protected $typeName; 23 | 24 | /** 25 | * @param string $typeName 26 | * @param Field[]|Query[] $fields 27 | * @param Directive[] $directives 28 | * @param Location $location 29 | */ 30 | public function __construct($typeName, array $fields, array $directives, Location $location) 31 | { 32 | parent::__construct($location); 33 | 34 | $this->typeName = $typeName; 35 | $this->fields = $fields; 36 | $this->setDirectives($directives); 37 | } 38 | 39 | /** 40 | * @return Field[]|Query[] 41 | */ 42 | public function getFields() 43 | { 44 | return $this->fields; 45 | } 46 | 47 | /** 48 | * @param Field[]|Query[] $fields 49 | */ 50 | public function setFields($fields) 51 | { 52 | $this->fields = $fields; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getTypeName() 59 | { 60 | return $this->typeName; 61 | } 62 | 63 | /** 64 | * @param string $typeName 65 | */ 66 | public function setTypeName($typeName) 67 | { 68 | $this->typeName = $typeName; 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /src/Parser/Location.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Parser; 9 | 10 | 11 | class Location 12 | { 13 | 14 | /** @var integer */ 15 | private $line; 16 | 17 | /** @var integer */ 18 | private $column; 19 | 20 | public function __construct($line, $column) 21 | { 22 | $this->line = $line; 23 | $this->column = $column; 24 | } 25 | 26 | /** 27 | * @return int 28 | */ 29 | public function getLine() 30 | { 31 | return $this->line; 32 | } 33 | 34 | /** 35 | * @return int 36 | */ 37 | public function getColumn() 38 | { 39 | return $this->column; 40 | } 41 | 42 | 43 | public function toArray() 44 | { 45 | return [ 46 | 'line' => $this->getLine(), 47 | 'column' => $this->getColumn() 48 | ]; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/Relay/Fetcher/CallableFetcher.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Relay\Fetcher; 9 | 10 | 11 | class CallableFetcher implements FetcherInterface 12 | { 13 | 14 | /** @var callable */ 15 | protected $resolveNodeCallable; 16 | 17 | /** @var callable */ 18 | protected $resolveTypeCallable; 19 | 20 | public function __construct(callable $resolveNode, callable $resolveType) 21 | { 22 | $this->resolveNodeCallable = $resolveNode; 23 | $this->resolveTypeCallable = $resolveType; 24 | } 25 | 26 | /** 27 | * @inheritdoc 28 | */ 29 | public function resolveNode($type, $id) 30 | { 31 | $callable = $this->resolveNodeCallable; 32 | 33 | return $callable($type, $id); 34 | } 35 | 36 | /** 37 | * @inheritdoc 38 | */ 39 | public function resolveType($object) 40 | { 41 | $callable = $this->resolveTypeCallable; 42 | 43 | return $callable($object); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Relay/Fetcher/FetcherInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Relay\Fetcher; 9 | 10 | 11 | interface FetcherInterface 12 | { 13 | 14 | /** 15 | * Resolve node 16 | * 17 | * @param $type string 18 | * @param $id string 19 | * 20 | * @return mixed 21 | */ 22 | public function resolveNode($type, $id); 23 | 24 | /** 25 | * @param $object 26 | * @return mixed 27 | */ 28 | public function resolveType($object); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/Relay/Field/GlobalIdField.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/10/16 11:23 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Relay\Field; 10 | 11 | 12 | use Youshido\GraphQL\Execution\ResolveInfo; 13 | use Youshido\GraphQL\Field\AbstractField; 14 | use Youshido\GraphQL\Relay\Node; 15 | use Youshido\GraphQL\Type\NonNullType; 16 | use Youshido\GraphQL\Type\Scalar\IdType; 17 | 18 | class GlobalIdField extends AbstractField 19 | { 20 | 21 | /** @var string */ 22 | protected $typeName; 23 | 24 | /** 25 | * @param string $typeName 26 | */ 27 | public function __construct($typeName) 28 | { 29 | $this->typeName = $typeName; 30 | 31 | $config = [ 32 | 'type' => $this->getType(), 33 | 'name' => $this->getName(), 34 | 'resolve' => [$this, 'resolve'] 35 | ]; 36 | 37 | parent::__construct($config); 38 | } 39 | 40 | public function getName() 41 | { 42 | return 'id'; 43 | } 44 | 45 | public function getDescription() 46 | { 47 | return 'The ID of an object'; 48 | } 49 | 50 | public function getType() 51 | { 52 | return new NonNullType(new IdType()); 53 | } 54 | 55 | /** 56 | * @inheritdoc 57 | */ 58 | public function resolve($value, array $args, ResolveInfo $info) 59 | { 60 | return $value ? Node::toGlobalId($this->typeName, $value['id']) : null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Relay/Field/NodeField.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/10/16 11:46 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Relay\Field; 10 | 11 | 12 | use Youshido\GraphQL\Config\Field\FieldConfig; 13 | use Youshido\GraphQL\Execution\ResolveInfo; 14 | use Youshido\GraphQL\Field\AbstractField; 15 | use Youshido\GraphQL\Field\InputField; 16 | use Youshido\GraphQL\Relay\Fetcher\FetcherInterface; 17 | use Youshido\GraphQL\Relay\Node; 18 | use Youshido\GraphQL\Relay\NodeInterfaceType; 19 | use Youshido\GraphQL\Type\NonNullType; 20 | use Youshido\GraphQL\Type\Scalar\IdType; 21 | 22 | class NodeField extends AbstractField 23 | { 24 | 25 | /** @var FetcherInterface */ 26 | protected $fetcher; 27 | 28 | /** @var NodeInterfaceType */ 29 | protected $type; 30 | 31 | public function __construct(FetcherInterface $fetcher) 32 | { 33 | $this->fetcher = $fetcher; 34 | $this->type = (new NodeInterfaceType())->setFetcher($this->fetcher); 35 | 36 | parent::__construct([]); 37 | } 38 | 39 | public function getName() 40 | { 41 | return 'node'; 42 | } 43 | 44 | public function getDescription() 45 | { 46 | return 'Fetches an object given its ID'; 47 | } 48 | 49 | public function build(FieldConfig $config) 50 | { 51 | $config->addArgument(new InputField([ 52 | 'name' => 'id', 53 | 'type' => new NonNullType(new IdType()), 54 | 'description' => 'The ID of an object' 55 | ])); 56 | } 57 | 58 | public function getType() 59 | { 60 | return $this->type; 61 | } 62 | 63 | public function resolve($value, array $args, ResolveInfo $info) 64 | { 65 | list($type, $id) = Node::fromGlobalId($args['id']); 66 | 67 | return $this->fetcher->resolveNode($type, $id); 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/Relay/Node.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Relay; 9 | 10 | 11 | class Node 12 | { 13 | 14 | /** 15 | * @param $id 16 | * 17 | * @return array with type and id element 18 | */ 19 | public static function fromGlobalId($id) 20 | { 21 | $decoded = base64_decode($id, true); 22 | if (!$decoded) { 23 | throw new \InvalidArgumentException('ID must be a valid base 64 string'); 24 | } 25 | $decodedParts = explode(':', $decoded, 2); 26 | if (count($decodedParts) !== 2) { 27 | throw new \InvalidArgumentException('ID was not correctly formed'); 28 | } 29 | return $decodedParts; 30 | } 31 | 32 | /** 33 | * @param $typeName string name of type 34 | * @param $id int local id 35 | * 36 | * @return string global id 37 | */ 38 | public static function toGlobalId($typeName, $id) 39 | { 40 | return base64_encode(implode(':', [$typeName, $id])); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Relay/NodeInterfaceType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/10/16 11:32 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Relay; 10 | 11 | 12 | use Youshido\GraphQL\Relay\Fetcher\FetcherInterface; 13 | use Youshido\GraphQL\Relay\Field\GlobalIdField; 14 | use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType; 15 | 16 | class NodeInterfaceType extends AbstractInterfaceType 17 | { 18 | 19 | /** @var FetcherInterface */ //todo: maybe there are better solution 20 | protected $fetcher; 21 | 22 | public function getName() 23 | { 24 | return 'NodeInterface'; 25 | } 26 | 27 | public function build($config) 28 | { 29 | $config->addField(new GlobalIdField('NodeInterface')); 30 | } 31 | 32 | public function resolveType($object) 33 | { 34 | if ($this->fetcher) { 35 | return $this->fetcher->resolveType($object); 36 | } 37 | 38 | return null; 39 | } 40 | 41 | /** 42 | * @return FetcherInterface 43 | */ 44 | public function getFetcher() 45 | { 46 | return $this->fetcher; 47 | } 48 | 49 | /** 50 | * @param FetcherInterface $fetcher 51 | * 52 | * @return NodeInterfaceType 53 | */ 54 | public function setFetcher($fetcher) 55 | { 56 | $this->fetcher = $fetcher; 57 | 58 | return $this; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Relay/Type/PageInfoType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 2/23/17 4:19 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Relay\Type; 10 | 11 | 12 | use Youshido\GraphQL\Type\NonNullType; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\Scalar\BooleanType; 15 | use Youshido\GraphQL\Type\Scalar\StringType; 16 | 17 | class PageInfoType extends AbstractObjectType 18 | { 19 | public function build($config) 20 | { 21 | $config->addFields([ 22 | 'hasNextPage' => [ 23 | 'type' => new NonNullType(new BooleanType()), 24 | 'description' => 'When paginating forwards, are there more items?' 25 | ], 26 | 'hasPreviousPage' => [ 27 | 'type' => new NonNullType(new BooleanType()), 28 | 'description' => 'When paginating backwards, are there more items?' 29 | ], 30 | 'startCursor' => [ 31 | 'type' => new StringType(), 32 | 'description' => 'When paginating backwards, the cursor to continue.' 33 | ], 34 | 'endCursor' => [ 35 | 'type' => new StringType(), 36 | 'description' => 'When paginating forwards, the cursor to continue.' 37 | ], 38 | ]); 39 | } 40 | 41 | public function getDescription() 42 | { 43 | return "Information about pagination in a connection."; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/Schema/InternalSchemaMutationObject.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/14/16 9:28 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Schema; 10 | 11 | 12 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 13 | 14 | class InternalSchemaMutationObject extends AbstractObjectType 15 | { 16 | public function build($config) 17 | { 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Schema/InternalSchemaQueryObject.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/14/16 9:28 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Schema; 10 | 11 | 12 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 13 | 14 | class InternalSchemaQueryObject extends AbstractObjectType 15 | { 16 | public function build($config) 17 | { 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Schema/Schema.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/28/15 3:40 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Schema; 10 | 11 | use Youshido\GraphQL\Config\Schema\SchemaConfig; 12 | 13 | final class Schema extends AbstractSchema 14 | { 15 | public function build(SchemaConfig $config) 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Type/AbstractInterfaceTypeInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/14/16 11:38 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type; 10 | 11 | 12 | interface AbstractInterfaceTypeInterface 13 | { 14 | /** 15 | * @param $object object from resolve function 16 | * 17 | * @return AbstractType 18 | */ 19 | public function resolveType($object); 20 | } 21 | -------------------------------------------------------------------------------- /src/Type/AbstractType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 2:05 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type; 10 | 11 | 12 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 13 | 14 | abstract class AbstractType implements TypeInterface 15 | { 16 | 17 | protected $lastValidationError = null; 18 | 19 | public function isCompositeType() 20 | { 21 | return false; 22 | } 23 | 24 | /** 25 | * @return AbstractType 26 | */ 27 | public function getType() 28 | { 29 | return $this; 30 | } 31 | 32 | /** 33 | * @return AbstractType 34 | */ 35 | public function getNamedType() 36 | { 37 | return $this->getType(); 38 | } 39 | 40 | /** 41 | * @return AbstractType|AbstractObjectType 42 | */ 43 | public function getNullableType() 44 | { 45 | return $this; 46 | } 47 | 48 | public function getValidationError($value = null) 49 | { 50 | return $this->lastValidationError; 51 | } 52 | 53 | public function isValidValue($value) 54 | { 55 | return true; 56 | } 57 | 58 | public function parseValue($value) 59 | { 60 | return $value; 61 | } 62 | 63 | public function parseInputValue($value) 64 | { 65 | return $this->parseValue($value); 66 | } 67 | 68 | public function serialize($value) 69 | { 70 | return $value; 71 | } 72 | 73 | public function isInputType() 74 | { 75 | return false; 76 | } 77 | 78 | public function __toString() 79 | { 80 | return $this->getName(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Type/CompositeTypeInterface.php: -------------------------------------------------------------------------------- 1 | 8 | * created: 3:48 PM 4/29/16 9 | */ 10 | interface CompositeTypeInterface 11 | { 12 | 13 | /** 14 | * @return AbstractType 15 | */ 16 | public function getTypeOf(); 17 | } 18 | -------------------------------------------------------------------------------- /src/Type/Enum/EnumType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Type\Enum; 9 | 10 | use Youshido\GraphQL\Config\Object\EnumTypeConfig; 11 | 12 | final class EnumType extends AbstractEnumType 13 | { 14 | 15 | public function __construct(array $config) 16 | { 17 | $this->config = new EnumTypeConfig($config, $this, true); 18 | } 19 | 20 | public function getValues() 21 | { 22 | return $this->getConfig()->getValues(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/Type/InputObject/InputObjectType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/1/15 10:25 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\InputObject; 10 | 11 | use Youshido\GraphQL\Config\Object\InputObjectTypeConfig; 12 | 13 | final class InputObjectType extends AbstractInputObjectType 14 | { 15 | 16 | public function __construct($config) 17 | { 18 | $this->config = new InputObjectTypeConfig($config, $this, true); 19 | } 20 | 21 | /** 22 | * @inheritdoc 23 | * 24 | * @codeCoverageIgnore 25 | */ 26 | public function build($config) 27 | { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Type/InputTypeInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 9/29/16 10:41 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type; 10 | 11 | 12 | use Youshido\GraphQL\Config\AbstractConfig; 13 | 14 | interface InputTypeInterface 15 | { 16 | /** 17 | * @return String type name 18 | */ 19 | public function getName(); 20 | 21 | /** 22 | * @return String predefined type kind 23 | */ 24 | public function getKind(); 25 | 26 | /** 27 | * @return String type description 28 | */ 29 | public function getDescription(); 30 | 31 | /** 32 | * Coercing value received as input to current type 33 | * 34 | * @param $value 35 | * @return mixed 36 | */ 37 | public function parseValue($value); 38 | 39 | /** 40 | * Coercing result to current type 41 | * 42 | * @param $value 43 | * @return mixed 44 | */ 45 | public function serialize($value); 46 | 47 | /** 48 | * @param $value mixed 49 | * 50 | * @return bool 51 | */ 52 | public function isValidValue($value); 53 | 54 | /** 55 | * @return AbstractConfig 56 | */ 57 | public function getConfig(); 58 | } -------------------------------------------------------------------------------- /src/Type/InterfaceType/InterfaceType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Type\InterfaceType; 9 | 10 | 11 | use Youshido\GraphQL\Config\Object\InterfaceTypeConfig; 12 | 13 | final class InterfaceType extends AbstractInterfaceType 14 | { 15 | 16 | public function __construct($config = []) 17 | { 18 | $this->config = new InterfaceTypeConfig($config, $this, true); 19 | } 20 | 21 | /** 22 | * @inheritdoc 23 | * 24 | * @codeCoverageIgnore 25 | */ 26 | public function build($config) 27 | { 28 | } 29 | 30 | public function resolveType($object) 31 | { 32 | return $this->getConfig()->resolveType($object); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/Type/ListType/ListType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/1/15 1:22 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\ListType; 10 | 11 | 12 | use Youshido\GraphQL\Config\Object\ListTypeConfig; 13 | 14 | final class ListType extends AbstractListType 15 | { 16 | 17 | public function __construct($itemType) 18 | { 19 | $this->config = new ListTypeConfig(['itemType' => $itemType], $this, true); 20 | } 21 | 22 | public function getItemType() 23 | { 24 | return $this->getConfig()->get('itemType'); 25 | } 26 | 27 | public function getName() 28 | { 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Type/Object/AbstractMutationObjectType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Type\Object; 9 | 10 | abstract class AbstractMutationObjectType extends AbstractObjectType 11 | { 12 | 13 | public function getType() 14 | { 15 | return $this->getOutputType(); 16 | } 17 | 18 | abstract public function getOutputType(); 19 | } 20 | -------------------------------------------------------------------------------- /src/Type/Object/ObjectType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:24 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Object; 10 | 11 | use Youshido\GraphQL\Config\Object\ObjectTypeConfig; 12 | 13 | final class ObjectType extends AbstractObjectType 14 | { 15 | 16 | public function __construct(array $config) 17 | { 18 | $this->config = new ObjectTypeConfig($config, $this, true); 19 | } 20 | 21 | /** 22 | * @inheritdoc 23 | * 24 | * @codeCoverageIgnore 25 | */ 26 | public function build($config) { } 27 | 28 | public function getName() 29 | { 30 | return $this->getConfigValue('name'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Type/Scalar/AbstractScalarType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:00 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | use Youshido\GraphQL\Config\Traits\ConfigAwareTrait; 12 | use Youshido\GraphQL\Type\AbstractType; 13 | use Youshido\GraphQL\Type\TypeMap; 14 | 15 | abstract class AbstractScalarType extends AbstractType 16 | { 17 | use ConfigAwareTrait; 18 | 19 | public function getName() 20 | { 21 | $className = get_class($this); 22 | 23 | return substr($className, strrpos($className, '\\') + 1, -4); 24 | } 25 | 26 | final public function getKind() 27 | { 28 | return TypeMap::KIND_SCALAR; 29 | } 30 | 31 | public function parseValue($value) 32 | { 33 | return $this->serialize($value); 34 | } 35 | 36 | public function isInputType() 37 | { 38 | return true; 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Type/Scalar/BooleanType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:22 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | 12 | class BooleanType extends AbstractScalarType 13 | { 14 | public function getName() 15 | { 16 | return 'Boolean'; 17 | } 18 | 19 | public function serialize($value) 20 | { 21 | if ($value === null) { 22 | return null; 23 | } 24 | if ($value === 'true') { 25 | return true; 26 | } 27 | if ($value === 'false') { 28 | return false; 29 | } 30 | 31 | return (bool)$value; 32 | } 33 | 34 | public function isValidValue($value) 35 | { 36 | return is_null($value) || is_bool($value); 37 | } 38 | 39 | public function getDescription() 40 | { 41 | return 'The `Boolean` scalar type represents `true` or `false`.'; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/Type/Scalar/DateTimeType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:22 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | class DateTimeType extends AbstractScalarType 12 | { 13 | 14 | private $format; 15 | 16 | public function __construct($format = 'Y-m-d H:i:s') 17 | { 18 | $this->format = $format; 19 | } 20 | 21 | public function getName() 22 | { 23 | return 'DateTime'; 24 | } 25 | 26 | public function isValidValue($value) 27 | { 28 | if ((is_object($value) && $value instanceof \DateTimeInterface) || is_null($value)) { 29 | return true; 30 | } else if (is_string($value)) { 31 | $date = $this->createFromFormat($value); 32 | } else { 33 | $date = null; 34 | } 35 | 36 | return $date ? true : false; 37 | } 38 | 39 | public function serialize($value) 40 | { 41 | $date = null; 42 | 43 | if (is_string($value)) { 44 | $date = $this->createFromFormat($value); 45 | } elseif ($value instanceof \DateTimeInterface) { 46 | $date = $value; 47 | } 48 | 49 | return $date ? $date->format($this->format) : null; 50 | } 51 | 52 | public function parseValue($value) 53 | { 54 | if (is_string($value)) { 55 | $date = $this->createFromFormat($value); 56 | } elseif ($value instanceof \DateTimeInterface) { 57 | $date = $value; 58 | } else { 59 | $date = false; 60 | } 61 | 62 | return $date ?: null; 63 | } 64 | 65 | private function createFromFormat($value) 66 | { 67 | return \DateTime::createFromFormat($this->format, $value); 68 | } 69 | 70 | public function getDescription() 71 | { 72 | return sprintf('Representation of date and time in "%s" format', $this->format); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/Type/Scalar/DateTimeTzType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:22 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | class DateTimeTzType extends AbstractScalarType 12 | { 13 | private $format = 'D, d M Y H:i:s O'; 14 | 15 | public function getName() 16 | { 17 | return 'DateTimeTz'; 18 | } 19 | public function isValidValue($value) 20 | { 21 | if ((is_object($value) && $value instanceof \DateTimeInterface) || is_null($value)) { 22 | return true; 23 | } else if (is_string($value)) { 24 | $date = $this->createFromFormat($value); 25 | } else { 26 | $date = null; 27 | } 28 | 29 | return $date ? true : false; 30 | } 31 | 32 | public function serialize($value) 33 | { 34 | $date = null; 35 | 36 | if (is_string($value)) { 37 | $date = $this->createFromFormat($value); 38 | } elseif ($value instanceof \DateTimeInterface) { 39 | $date = $value; 40 | } 41 | 42 | return $date ? $date->format($this->format) : null; 43 | } 44 | 45 | public function parseValue($value) 46 | { 47 | if (is_string($value)) { 48 | $date = $this->createFromFormat($value); 49 | } elseif ($value instanceof \DateTimeInterface) { 50 | $date = $value; 51 | } else { 52 | $date = false; 53 | } 54 | 55 | return $date ?: null; 56 | } 57 | 58 | private function createFromFormat($value) 59 | { 60 | return \DateTime::createFromFormat($this->format, $value); 61 | } 62 | 63 | public function getDescription() 64 | { 65 | return 'Representation of date and time in "r" format'; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/Type/Scalar/DateType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:22 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | /** 12 | * @deprecated USE DateTime type instead. To be removed in 1.4. 13 | * 14 | * Class DateType 15 | * @package Youshido\GraphQL\Type\Scalar 16 | */ 17 | class DateType extends AbstractScalarType 18 | { 19 | 20 | public function getName() 21 | { 22 | return 'Date'; 23 | } 24 | 25 | /** 26 | * @param $value \DateTime 27 | * @return null|string 28 | */ 29 | public function serialize($value) 30 | { 31 | if ($value === null) { 32 | return null; 33 | } 34 | 35 | return $value->format('Y-m-d'); 36 | } 37 | 38 | public function isValidValue($value) 39 | { 40 | if (is_null($value) || is_object($value)) { 41 | return true; 42 | } 43 | 44 | $d = \DateTime::createFromFormat('Y-m-d', $value); 45 | 46 | return $d && $d->format('Y-m-d') == $value; 47 | } 48 | 49 | public function getDescription() 50 | { 51 | return 'DEPRECATED. Use DateTime instead'; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/Type/Scalar/FloatType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/3/15 10:10 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | class FloatType extends AbstractScalarType 12 | { 13 | 14 | public function getName() 15 | { 16 | return 'Float'; 17 | } 18 | 19 | public function serialize($value) 20 | { 21 | if ($value === null) { 22 | return null; 23 | } else { 24 | return floatval($value); 25 | } 26 | } 27 | 28 | public function isValidValue($value) 29 | { 30 | return is_null($value) || is_float($value) || is_int($value); 31 | } 32 | 33 | public function getDescription() 34 | { 35 | return 'The `Float` scalar type represents signed double-precision fractional ' . 36 | 'values as specified by ' . 37 | '[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).'; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Type/Scalar/IdType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/4/15 12:41 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | 12 | class IdType extends AbstractScalarType 13 | { 14 | 15 | public function getName() 16 | { 17 | return 'ID'; 18 | } 19 | 20 | public function serialize($value) 21 | { 22 | if (null === $value) { 23 | return null; 24 | } 25 | 26 | return (string)$value; 27 | } 28 | 29 | public function getDescription() 30 | { 31 | return 'The `ID` scalar type represents a unique identifier, often used to ' . 32 | 'refetch an object or as key for a cache. The ID type appears in a JSON ' . 33 | 'response as a String; however, it is not intended to be human-readable. ' . 34 | 'When expected as an input type, any string (such as `"4"`) or integer ' . 35 | '(such as `4`) input value will be accepted as an ID.'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Type/Scalar/IntType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:22 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | 12 | class IntType extends AbstractScalarType 13 | { 14 | 15 | public function getName() 16 | { 17 | return 'Int'; 18 | } 19 | 20 | public function serialize($value) 21 | { 22 | if ($value === null) { 23 | return null; 24 | } else { 25 | if (is_int($value)) { 26 | return $value; 27 | } else { 28 | $value = (int)$value; 29 | 30 | return $value != 0 ? $value : null; 31 | } 32 | } 33 | } 34 | 35 | public function isValidValue($value) 36 | { 37 | return is_null($value) || is_int($value); 38 | } 39 | 40 | public function getDescription() 41 | { 42 | return 'The `Int` scalar type represents non-fractional signed whole numeric ' . 43 | 'values. Int can represent values between -(2^53 - 1) and 2^53 - 1 since ' . 44 | 'represented in JSON as double-precision floating point numbers specified' . 45 | 'by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).'; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/Type/Scalar/StringType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:05 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | 12 | class StringType extends AbstractScalarType 13 | { 14 | 15 | public function getName() 16 | { 17 | return 'String'; 18 | } 19 | 20 | public function serialize($value) 21 | { 22 | if ($value === true) { 23 | return 'true'; 24 | } elseif ($value === false) { 25 | return 'false'; 26 | } elseif ($value === null) { 27 | return null; 28 | } 29 | 30 | if(is_array($value)) { 31 | return ''; 32 | } 33 | 34 | return (string) $value; 35 | } 36 | 37 | public function isValidValue($value) 38 | { 39 | return is_null($value) || is_scalar($value) || ((is_object($value) && method_exists($value, '__toString'))); 40 | } 41 | 42 | public function getDescription() 43 | { 44 | return 'The `String` scalar type represents textual data, represented as UTF-8 ' . 45 | 'character sequences. The String type is most often used by GraphQL to ' . 46 | 'represent free-form human-readable text.'; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/Type/Scalar/TimestampType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 1:22 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Scalar; 10 | 11 | 12 | /** 13 | * Class TimestampType 14 | * @package Youshido\GraphQL\Type\Scalar 15 | * @deprecated Should not be used, to be removed in 1.5 16 | */ 17 | class TimestampType extends AbstractScalarType 18 | { 19 | 20 | public function getName() 21 | { 22 | return 'Timestamp'; 23 | } 24 | 25 | /** 26 | * @param $value \DateTime 27 | * @return null|string 28 | */ 29 | public function serialize($value) 30 | { 31 | if ($value === null || !is_object($value)) { 32 | return null; 33 | } 34 | 35 | return $value->getTimestamp(); 36 | } 37 | 38 | public function isValidValue($value) 39 | { 40 | if (is_null($value) || is_object($value)) { 41 | return true; 42 | } 43 | 44 | return is_int($value); 45 | } 46 | 47 | public function getDescription() 48 | { 49 | return 'DEPRECATED. Will be converted to a real timestamp'; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/Type/SchemaDirectivesList.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Type; 9 | 10 | use Youshido\GraphQL\Directive\DirectiveInterface; 11 | 12 | 13 | class SchemaDirectivesList 14 | { 15 | 16 | private $directivesList = []; 17 | 18 | /** 19 | * @param array $directives 20 | * 21 | * @throws 22 | * @return $this 23 | */ 24 | public function addDirectives($directives) 25 | { 26 | if (!is_array($directives)) { 27 | throw new \Exception('addDirectives accept only array of directives'); 28 | } 29 | foreach ($directives as $directive) { 30 | $this->addDirective($directive); 31 | } 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * @param DirectiveInterface $directive 38 | * 39 | * @return $this 40 | */ 41 | public function addDirective(DirectiveInterface $directive) 42 | { 43 | $directiveName = $this->getDirectiveName($directive); 44 | if ($this->isDirectiveNameRegistered($directiveName)) return $this; 45 | 46 | $this->directivesList[$directiveName] = $directive; 47 | 48 | return $this; 49 | } 50 | 51 | private function getDirectiveName($directive) 52 | { 53 | if (is_string($directive)) return $directive; 54 | if (is_object($directive) && $directive instanceof DirectiveInterface) { 55 | return $directive->getName(); 56 | } 57 | throw new \Exception('Invalid directive passed to Schema'); 58 | } 59 | 60 | public function isDirectiveNameRegistered($directiveName) 61 | { 62 | return (isset($this->directivesList[$directiveName])); 63 | } 64 | 65 | public function getDirectives() 66 | { 67 | return $this->directivesList; 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/Type/SchemaTypesList.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 10/7/16 3:36 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type; 10 | 11 | 12 | class SchemaTypesList 13 | { 14 | 15 | private $typesList = []; 16 | 17 | /** 18 | * @param array $types 19 | * @throws 20 | * @return $this 21 | */ 22 | public function addTypes($types) 23 | { 24 | if (!is_array($types)) { 25 | throw new \Exception('addTypes accept only array of types'); 26 | } 27 | foreach($types as $type) { 28 | $this->addType($type); 29 | } 30 | return $this; 31 | } 32 | 33 | public function getTypes() 34 | { 35 | return $this->typesList; 36 | } 37 | 38 | /** 39 | * @param TypeInterface $type 40 | * @return $this 41 | */ 42 | public function addType(TypeInterface $type) 43 | { 44 | $typeName = $this->getTypeName($type); 45 | if ($this->isTypeNameRegistered($typeName)) return $this; 46 | 47 | $this->typesList[$typeName] = $type; 48 | return $this; 49 | } 50 | 51 | public function isTypeNameRegistered($typeName) 52 | { 53 | return (isset($this->typesList[$typeName])); 54 | } 55 | 56 | private function getTypeName($type) { 57 | if (is_string($type)) return $type; 58 | if (is_object($type) && $type instanceof AbstractType) { 59 | return $type->getName(); 60 | } 61 | throw new \Exception('Invalid type passed to Schema'); 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /src/Type/Traits/ArgumentsAwareObjectTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5:07 PM 5/14/16 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Traits; 10 | 11 | 12 | use Youshido\GraphQL\Config\Traits\ConfigAwareTrait; 13 | 14 | /** 15 | * Class ArgumentsAwareObjectTrait 16 | * @package Youshido\GraphQL\Type\Traits 17 | * @codeCoverageIgnore 18 | * 19 | * @deprecated To be removed during the release optimization 20 | */ 21 | trait ArgumentsAwareObjectTrait 22 | { 23 | use ConfigAwareTrait; 24 | 25 | public function addArgument($argument, $argumentInfo = null) 26 | { 27 | return $this->getConfig()->addArgument($argument, $argumentInfo); 28 | } 29 | 30 | public function removeArgument($argumentName) 31 | { 32 | return $this->getConfig()->removeArgument($argumentName); 33 | } 34 | 35 | public function getArguments() 36 | { 37 | return $this->getConfig()->getArguments(); 38 | } 39 | 40 | public function getArgument($argumentName) 41 | { 42 | return $this->getConfig()->getArgument($argumentName); 43 | } 44 | 45 | public function hasArgument($argumentName) 46 | { 47 | return $this->getConfig()->hasArgument($argumentName); 48 | } 49 | 50 | public function hasArguments() 51 | { 52 | return $this->getConfig()->hasArguments(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/Type/Traits/AutoNameTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/4/16 9:18 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Traits; 10 | use Youshido\GraphQL\Field\FieldInterface; 11 | 12 | /** 13 | * Class AutoNameTrait 14 | * @package Youshido\GraphQL\Type\Traits 15 | */ 16 | trait AutoNameTrait 17 | { 18 | 19 | public function getName() 20 | { 21 | if (!empty($this->config)) { 22 | return $this->config->getName(); 23 | } 24 | 25 | $className = get_called_class(); 26 | 27 | if ($prevPos = strrpos($className, '\\')) { 28 | $className = substr($className, $prevPos + 1); 29 | } 30 | if (substr($className, -5) == 'Field') { 31 | $className = lcfirst(substr($className, 0, -5)); 32 | } elseif (substr($className, -4) == 'Type') { 33 | $className = substr($className, 0, -4); 34 | } 35 | 36 | if ($this instanceof FieldInterface) { 37 | $className = lcfirst($className); 38 | } 39 | 40 | 41 | return $className; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/Type/Traits/FieldsArgumentsAwareObjectTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5:12 PM 5/14/16 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Traits; 10 | 11 | 12 | trait FieldsArgumentsAwareObjectTrait 13 | { 14 | use FieldsAwareObjectTrait; 15 | 16 | protected $hasArgumentCache = null; 17 | 18 | public function addArguments($argumentsList) 19 | { 20 | return $this->getConfig()->addArguments($argumentsList); 21 | } 22 | 23 | public function removeArgument($argumentName) 24 | { 25 | return $this->getConfig()->removeArgument($argumentName); 26 | } 27 | 28 | public function addArgument($argument, $ArgumentInfo = null) 29 | { 30 | return $this->getConfig()->addArgument($argument, $ArgumentInfo); 31 | } 32 | 33 | public function getArguments() 34 | { 35 | return $this->getConfig()->getArguments(); 36 | } 37 | 38 | public function getArgument($argumentName) 39 | { 40 | return $this->getConfig()->getArgument($argumentName); 41 | } 42 | 43 | public function hasArgument($argumentName) 44 | { 45 | return $this->getConfig()->hasArgument($argumentName); 46 | } 47 | 48 | public function hasArguments() 49 | { 50 | return $this->hasArgumentCache === null ? ($this->hasArgumentCache = $this->getConfig()->hasArguments()) : $this->hasArgumentCache; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Type/Traits/FieldsAwareObjectTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5:07 PM 5/14/16 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Traits; 10 | 11 | 12 | use Youshido\GraphQL\Config\Traits\ConfigAwareTrait; 13 | 14 | trait FieldsAwareObjectTrait 15 | { 16 | use ConfigAwareTrait; 17 | 18 | public function addFields($fieldsList) 19 | { 20 | $this->getConfig()->addFields($fieldsList); 21 | 22 | return $this; 23 | } 24 | 25 | public function addField($field, $fieldInfo = null) 26 | { 27 | $this->getConfig()->addField($field, $fieldInfo); 28 | 29 | return $this; 30 | } 31 | 32 | public function getFields() 33 | { 34 | return $this->getConfig()->getFields(); 35 | } 36 | 37 | public function getField($fieldName) 38 | { 39 | return $this->getConfig()->getField($fieldName); 40 | } 41 | 42 | public function hasField($fieldName) 43 | { 44 | return $this->getConfig()->hasField($fieldName); 45 | } 46 | 47 | public function hasFields() 48 | { 49 | return $this->getConfig()->hasFields(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/Type/TypeFactory.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/11/16 9:19 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type; 10 | 11 | 12 | use Youshido\GraphQL\Exception\ConfigurationException; 13 | use Youshido\GraphQL\Type\Scalar\AbstractScalarType; 14 | 15 | class TypeFactory 16 | { 17 | private static $objectsHash = []; 18 | 19 | /** 20 | * @param string $type 21 | * 22 | * @throws ConfigurationException 23 | * @return AbstractScalarType 24 | */ 25 | public static function getScalarType($type) 26 | { 27 | if (TypeService::isScalarType($type)) { 28 | if (is_object($type)) { 29 | return $type; 30 | } 31 | if (empty(self::$objectsHash[$type])) { 32 | $name = ucfirst($type); 33 | 34 | $name = $name == 'Datetime' ? 'DateTime' : $name; 35 | $name = $name == 'Datetimetz' ? 'DateTimeTz' : $name; 36 | 37 | $className = 'Youshido\GraphQL\Type\Scalar\\' . $name . 'Type'; 38 | self::$objectsHash[$type] = new $className(); 39 | } 40 | 41 | return self::$objectsHash[$type]; 42 | } else { 43 | throw new ConfigurationException('Configuration problem with type ' . $type); 44 | } 45 | } 46 | 47 | /** 48 | * @return string[] 49 | */ 50 | public static function getScalarTypesNames() 51 | { 52 | return [ 53 | TypeMap::TYPE_INT, 54 | TypeMap::TYPE_FLOAT, 55 | TypeMap::TYPE_STRING, 56 | TypeMap::TYPE_BOOLEAN, 57 | TypeMap::TYPE_ID, 58 | TypeMap::TYPE_DATETIME, 59 | TypeMap::TYPE_DATE, 60 | TypeMap::TYPE_TIMESTAMP, 61 | TypeMap::TYPE_DATETIMETZ, 62 | ]; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Type/TypeInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/27/15 12:51 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type; 10 | 11 | Interface TypeInterface extends InputTypeInterface 12 | { 13 | 14 | } -------------------------------------------------------------------------------- /src/Type/TypeMap.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/30/15 12:36 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type; 10 | 11 | class TypeMap 12 | { 13 | 14 | const KIND_SCALAR = 'SCALAR'; 15 | const KIND_OBJECT = 'OBJECT'; 16 | const KIND_INTERFACE = 'INTERFACE'; 17 | const KIND_UNION = 'UNION'; 18 | const KIND_ENUM = 'ENUM'; 19 | const KIND_INPUT_OBJECT = 'INPUT_OBJECT'; 20 | const KIND_LIST = 'LIST'; 21 | const KIND_NON_NULL = 'NON_NULL'; 22 | 23 | const TYPE_INT = 'int'; 24 | const TYPE_FLOAT = 'float'; 25 | const TYPE_STRING = 'string'; 26 | const TYPE_BOOLEAN = 'boolean'; 27 | const TYPE_ID = 'id'; 28 | const TYPE_DATETIME = 'datetime'; 29 | const TYPE_DATETIMETZ = 'datetimetz'; 30 | const TYPE_DATE = 'date'; 31 | const TYPE_TIMESTAMP = 'timestamp'; 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Type/Union/AbstractUnionType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/5/15 12:12 AM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Union; 10 | 11 | 12 | use Youshido\GraphQL\Config\Object\UnionTypeConfig; 13 | use Youshido\GraphQL\Config\Traits\ConfigAwareTrait; 14 | use Youshido\GraphQL\Type\AbstractInterfaceTypeInterface; 15 | use Youshido\GraphQL\Type\AbstractType; 16 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 17 | use Youshido\GraphQL\Type\Scalar\AbstractScalarType; 18 | use Youshido\GraphQL\Type\Traits\AutoNameTrait; 19 | use Youshido\GraphQL\Type\TypeMap; 20 | 21 | abstract class AbstractUnionType extends AbstractType implements AbstractInterfaceTypeInterface 22 | { 23 | 24 | use ConfigAwareTrait, AutoNameTrait; 25 | 26 | protected $isFinal = false; 27 | 28 | /** 29 | * ObjectType constructor. 30 | * @param $config 31 | */ 32 | public function __construct($config = []) 33 | { 34 | if (empty($config)) { 35 | $config['name'] = $this->getName(); 36 | $config['types'] = $this->getTypes(); 37 | } 38 | 39 | $this->config = new UnionTypeConfig($config, $this, $this->isFinal); 40 | } 41 | 42 | /** 43 | * @return AbstractObjectType[]|AbstractScalarType[] 44 | */ 45 | abstract public function getTypes(); 46 | 47 | public function getKind() 48 | { 49 | return TypeMap::KIND_UNION; 50 | } 51 | 52 | public function getNamedType() 53 | { 54 | return $this; 55 | } 56 | 57 | public function isValidValue($value) 58 | { 59 | return true; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/Type/Union/UnionType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11:54 AM 5/5/16 7 | */ 8 | 9 | namespace Youshido\GraphQL\Type\Union; 10 | 11 | final class UnionType extends AbstractUnionType 12 | { 13 | 14 | protected $isFinal = true; 15 | 16 | public function resolveType($object) 17 | { 18 | $callable = $this->getConfigValue('resolveType'); 19 | 20 | return $callable($object); 21 | } 22 | 23 | public function getTypes() 24 | { 25 | return $this->getConfig()->get('types', []); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/Validator/ConfigValidator/ConfigValidatorInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Validator\ConfigValidator; 9 | 10 | 11 | interface ConfigValidatorInterface 12 | { 13 | 14 | public function validate($data, $rules = [], $allowExtraFields = null); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/Validator/ConfigValidator/Rules/ValidationRuleInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 11/28/15 6:11 PM 7 | */ 8 | 9 | namespace Youshido\GraphQL\Validator\ConfigValidator\Rules; 10 | 11 | 12 | interface ValidationRuleInterface 13 | { 14 | public function validate($data, $ruleInfo); 15 | } -------------------------------------------------------------------------------- /src/Validator/ErrorContainer/ErrorContainerInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Validator\ErrorContainer; 9 | 10 | 11 | interface ErrorContainerInterface 12 | { 13 | 14 | public function addError(\Exception $exception); 15 | 16 | public function mergeErrors(ErrorContainerInterface $errorContainer); 17 | 18 | public function hasErrors(); 19 | 20 | public function getErrors(); 21 | 22 | public function getErrorsArray(); 23 | 24 | public function clearErrors(); 25 | 26 | } -------------------------------------------------------------------------------- /src/Validator/RequestValidator/RequestValidatorInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Validator\RequestValidator; 9 | 10 | 11 | use Youshido\GraphQL\Execution\Request; 12 | 13 | interface RequestValidatorInterface 14 | { 15 | 16 | public function validate(Request $request); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/Validator/ResolveValidator/ResolveValidatorInterface.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\GraphQL\Validator\ResolveValidator; 9 | 10 | 11 | use Youshido\GraphQL\Execution\Request; 12 | use Youshido\GraphQL\Field\FieldInterface; 13 | use Youshido\GraphQL\Parser\Ast\Interfaces\FieldInterface as AstFieldInterface; 14 | use Youshido\GraphQL\Type\AbstractType; 15 | use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType; 16 | use Youshido\GraphQL\Type\Union\AbstractUnionType; 17 | 18 | interface ResolveValidatorInterface 19 | { 20 | 21 | public function assetTypeHasField(AbstractType $objectType, AstFieldInterface $ast); 22 | 23 | public function assertValidArguments(FieldInterface $field, AstFieldInterface $query, Request $request); 24 | 25 | public function assertValidResolvedValueForField(FieldInterface $field, $resolvedValue); 26 | 27 | public function assertTypeImplementsInterface(AbstractType $type, AbstractInterfaceType $interface); 28 | 29 | public function assertTypeInUnionTypes(AbstractType $type, AbstractUnionType $unionType); 30 | } 31 | -------------------------------------------------------------------------------- /tests/DataProvider/TestConfig.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/11/16 10:45 PM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Config\AbstractConfig; 13 | use Youshido\GraphQL\Type\TypeService; 14 | 15 | class TestConfig extends AbstractConfig 16 | { 17 | public function getRules() 18 | { 19 | return [ 20 | 'name' => ['type' => TypeService::TYPE_ANY, 'required' => true], 21 | 'resolve' => ['type' => TypeService::TYPE_CALLABLE, 'final' => true], 22 | ]; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /tests/DataProvider/TestConfigExtraFields.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/12/16 9:27 PM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Config\AbstractConfig; 13 | use Youshido\GraphQL\Type\TypeService; 14 | 15 | class TestConfigExtraFields extends AbstractConfig 16 | { 17 | 18 | protected $extraFieldsAllowed = true; 19 | 20 | public function getRules() 21 | { 22 | return [ 23 | 'name' => ['type' => TypeService::TYPE_ANY, 'required' => true] 24 | ]; 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /tests/DataProvider/TestConfigInvalidRule.php: -------------------------------------------------------------------------------- 1 | ['type' => TypeService::TYPE_ANY, 'required' => true], 15 | 'invalidRuleField' => ['type' => TypeService::TYPE_ANY, 'invalid rule' => true] 16 | ]; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tests/DataProvider/TestEmptySchema.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/3/15 11:28 PM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | use Youshido\GraphQL\Config\Schema\SchemaConfig; 12 | use Youshido\GraphQL\Schema\AbstractSchema; 13 | 14 | 15 | class TestEmptySchema extends AbstractSchema 16 | { 17 | public function build(SchemaConfig $config) 18 | { 19 | } 20 | 21 | 22 | public function getName($config) 23 | { 24 | return 'TestSchema'; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/DataProvider/TestEnumType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/12/16 6:42 PM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Type\Enum\AbstractEnumType; 13 | 14 | class TestEnumType extends AbstractEnumType 15 | { 16 | public function getValues() 17 | { 18 | return [ 19 | [ 20 | 'name' => 'FINISHED', 21 | 'value' => 1, 22 | ], 23 | [ 24 | 'name' => 'NEW', 25 | 'value' => 0, 26 | ] 27 | ]; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /tests/DataProvider/TestExtendedType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 8/14/16 1:19 PM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Config\Object\ObjectTypeConfig; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\Scalar\StringType; 15 | 16 | class TestExtendedType extends AbstractObjectType 17 | { 18 | public function build($config) 19 | { 20 | $config->applyInterface(new TestInterfaceType()) 21 | ->addField('ownField', new StringType()); 22 | } 23 | 24 | 25 | } -------------------------------------------------------------------------------- /tests/DataProvider/TestField.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\Tests\DataProvider; 9 | 10 | use Youshido\GraphQL\Execution\ResolveInfo; 11 | use Youshido\GraphQL\Field\AbstractField; 12 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 13 | use Youshido\GraphQL\Type\Scalar\IntType; 14 | 15 | class TestField extends AbstractField 16 | { 17 | 18 | /** 19 | * @return AbstractObjectType 20 | */ 21 | public function getType() 22 | { 23 | return new IntType(); 24 | } 25 | 26 | public function resolve($value, array $args, ResolveInfo $info) 27 | { 28 | return $value; 29 | } 30 | 31 | public function getDescription() 32 | { 33 | return 'description'; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/DataProvider/TestInputField.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\Tests\DataProvider; 9 | 10 | 11 | use Youshido\GraphQL\Field\AbstractInputField; 12 | use Youshido\GraphQL\Type\InputTypeInterface; 13 | use Youshido\GraphQL\Type\Scalar\IntType; 14 | 15 | class TestInputField extends AbstractInputField 16 | { 17 | 18 | /** 19 | * @return InputTypeInterface 20 | */ 21 | public function getType() 22 | { 23 | return new IntType(); 24 | } 25 | 26 | public function getDescription() 27 | { 28 | return 'description'; 29 | } 30 | 31 | public function getDefaultValue() 32 | { 33 | return 'default'; 34 | } 35 | } -------------------------------------------------------------------------------- /tests/DataProvider/TestInputObjectType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 4:16 PM 5/13/16 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Type\InputObject\AbstractInputObjectType; 13 | use Youshido\GraphQL\Type\NonNullType; 14 | use Youshido\GraphQL\Type\Scalar\StringType; 15 | 16 | class TestInputObjectType extends AbstractInputObjectType 17 | { 18 | public function build($config) 19 | { 20 | $config->addField('name', new NonNullType(new StringType())); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tests/DataProvider/TestInterfaceType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/12/16 4:24 PM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType; 13 | use Youshido\GraphQL\Type\Scalar\StringType; 14 | 15 | class TestInterfaceType extends AbstractInterfaceType 16 | { 17 | 18 | public function resolveType($object) 19 | { 20 | return is_object($object) ? $object : new TestObjectType(); 21 | } 22 | 23 | public function build($config) 24 | { 25 | $config->addField('name', new StringType()); 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /tests/DataProvider/TestListType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/15/16 3:19 PM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Type\ListType\AbstractListType; 13 | use Youshido\GraphQL\Type\Scalar\StringType; 14 | 15 | class TestListType extends AbstractListType 16 | { 17 | public function getItemType() 18 | { 19 | return new StringType(); 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tests/DataProvider/TestMutationObjectType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 2:00 PM 5/15/16 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Type\Object\AbstractMutationObjectType; 13 | use Youshido\GraphQL\Type\Scalar\IntType; 14 | use Youshido\GraphQL\Type\Scalar\StringType; 15 | 16 | class TestMutationObjectType extends AbstractMutationObjectType 17 | { 18 | public function getOutputType() 19 | { 20 | return new StringType(); 21 | } 22 | 23 | public function build($config) 24 | { 25 | $this->addArgument('increment', new IntType()); 26 | } 27 | 28 | 29 | } -------------------------------------------------------------------------------- /tests/DataProvider/TestResolveInfo.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/20/16 12:41 AM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Execution\Context\ExecutionContext; 13 | use Youshido\GraphQL\Execution\ResolveInfo; 14 | 15 | class TestResolveInfo 16 | { 17 | public static function createTestResolveInfo($field = null) 18 | { 19 | if (empty($field)) { 20 | $field = new TestField(); 21 | } 22 | 23 | return new ResolveInfo($field, [], new ExecutionContext(new TestSchema())); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/DataProvider/TestSchema.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 10:48 PM 5/14/16 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Config\Schema\SchemaConfig; 13 | use Youshido\GraphQL\Execution\ResolveInfo; 14 | use Youshido\GraphQL\Schema\AbstractSchema; 15 | use Youshido\GraphQL\Type\ListType\ListType; 16 | use Youshido\GraphQL\Type\Scalar\IntType; 17 | 18 | class TestSchema extends AbstractSchema 19 | { 20 | private $testStatusValue = 0; 21 | 22 | public function build(SchemaConfig $config) 23 | { 24 | $config->getQuery()->addFields([ 25 | 'me' => [ 26 | 'type' => new TestObjectType(), 27 | 'resolve' => function ($value, $args, ResolveInfo $info) { 28 | return $info->getReturnType()->getData(); 29 | } 30 | ], 31 | 'status' => [ 32 | 'type' => new TestEnumType(), 33 | 'resolve' => function () { 34 | return $this->testStatusValue; 35 | } 36 | ], 37 | ]); 38 | $config->getMutation()->addFields([ 39 | 'updateStatus' => [ 40 | 'type' => new TestEnumType(), 41 | 'resolve' => function () { 42 | return $this->testStatusValue; 43 | }, 44 | 'args' => [ 45 | 'newStatus' => new TestEnumType(), 46 | 'list' => new ListType(new IntType()) 47 | ] 48 | ] 49 | ]); 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /tests/DataProvider/TestTimeType.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 8/14/16 12:26 PM 7 | */ 8 | 9 | namespace Youshido\Tests\DataProvider; 10 | 11 | 12 | use Youshido\GraphQL\Type\Scalar\AbstractScalarType; 13 | 14 | class TestTimeType extends AbstractScalarType 15 | { 16 | 17 | public function getName() 18 | { 19 | return 'TestTime'; 20 | } 21 | 22 | /** 23 | * @param $value \DateTime 24 | * @return null|string 25 | */ 26 | public function serialize($value) 27 | { 28 | if ($value === null) { 29 | return null; 30 | } 31 | 32 | return $value instanceof \DateTime ? $value->format('H:i:s') : $value; 33 | } 34 | 35 | public function isValidValue($value) 36 | { 37 | if (is_object($value)) { 38 | return true; 39 | } 40 | 41 | $d = \DateTime::createFromFormat('H:i:s', $value); 42 | 43 | return $d && $d->format('H:i:s') == $value; 44 | } 45 | 46 | public function getDescription() 47 | { 48 | return 'Representation time in "H:i:s" format'; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /tests/DataProvider/TestUnionType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\Tests\DataProvider; 9 | 10 | 11 | use Youshido\GraphQL\Type\Union\AbstractUnionType; 12 | 13 | class TestUnionType extends AbstractUnionType 14 | { 15 | 16 | public function getTypes() 17 | { 18 | return [ 19 | new TestObjectType() 20 | ]; 21 | } 22 | 23 | public function resolveType($object) 24 | { 25 | return $object; 26 | } 27 | 28 | public function getDescription() 29 | { 30 | return 'Union collect cars types'; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tests/Issues/Issue109/Issue109Test.php: -------------------------------------------------------------------------------- 1 | processPayload(' 15 | query ($postId: Int, $commentId: Int) { 16 | latestPost(id: $postId) { 17 | id(comment_id: $commentId), 18 | comments(comment_id: $commentId) { 19 | comment_id 20 | } 21 | } 22 | }', 23 | [ 24 | 'postId' => 1, 25 | 'commentId' => 100 26 | ])->getResponseData(); 27 | $this->assertEquals(['data' => ['latestPost' => [ 28 | 'id' => 1, 29 | 'comments' => [ 30 | ['comment_id' => 100] 31 | ] 32 | ]]], $response); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Issues/Issue171/Issue171Schema.php: -------------------------------------------------------------------------------- 1 | getQuery()->addField( 14 | 'plan', 15 | [ 16 | 'type' => new PlanType(), 17 | ] 18 | ); 19 | } 20 | } 21 | 22 | class PlanType extends AbstractObjectType 23 | { 24 | public function build($config) 25 | { 26 | $config->addField('kpi_status', [ 27 | 'type' => new KpiStatusType(), 28 | ]); 29 | } 30 | } 31 | 32 | class KpiStatusType extends AbstractEnumType 33 | { 34 | public function getValues() 35 | { 36 | return [ 37 | [ 38 | 'name' => 'BAD', 39 | 'value' => 'Bad', 40 | ], 41 | [ 42 | 'name' => 'GOOD', 43 | 'value' => 'Good', 44 | ], 45 | [ 46 | 'name' => 'WARNING', 47 | 'value' => 'Warning', 48 | ] 49 | ]; 50 | } 51 | } -------------------------------------------------------------------------------- /tests/Issues/Issue201/Issue201Test.php: -------------------------------------------------------------------------------- 1 | new ObjectType([ 23 | 'name' => 'RootQuery', 24 | 'fields' => [ 25 | 'user' => [ 26 | 'type' => new StringType(), 27 | ], 28 | ], 29 | ]), 30 | ]); 31 | 32 | $schema->getQueryType()->addFields([ 33 | 'user' => new StringType(), 34 | ]); 35 | 36 | $schemaValidator = new SchemaValidator(); 37 | $schemaValidator->validate($schema); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Issues/Issue210/TypeServiceTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('Foo', TypeService::getPropertyValue($object, 'issuer')); 16 | $this->assertEquals('something', TypeService::getPropertyValue($object, 'something')); 17 | $this->assertEquals('Bar', TypeService::getPropertyValue($object, 'issuerName')); 18 | } 19 | } 20 | 21 | class DummyObjectWithTrickyGetters 22 | { 23 | public function getIssuer() 24 | { 25 | return 'Foo'; 26 | } 27 | 28 | public function something() 29 | { 30 | return 'something'; 31 | } 32 | 33 | public function issuerName() 34 | { 35 | return 'Bar'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Issues/Issue220/ResolvableObjectTraitTest.php: -------------------------------------------------------------------------------- 1 | 'scalarField', 18 | 'type' => new StringType(), 19 | ]); 20 | 21 | $resolveInfo = TestResolveInfo::createTestResolveInfo($fieldWithResolve); 22 | 23 | $this->assertEquals(null, $fieldWithResolve->resolve([], [], $resolveInfo)); 24 | } 25 | 26 | public function testValueNotFoundInResolveObjectType() 27 | { 28 | $fieldWithResolve = new Field([ 29 | 'name' => 'scalarField', 30 | 'type' => new ArticleType(), 31 | ]); 32 | 33 | $resolveInfo = TestResolveInfo::createTestResolveInfo($fieldWithResolve); 34 | 35 | $this->assertEquals(null, $fieldWithResolve->resolve([], [], $resolveInfo)); 36 | } 37 | 38 | public function testValueFoundInResolve() 39 | { 40 | $fieldWithResolve = new Field([ 41 | 'name' => 'scalarField', 42 | 'type' => new StringType(), 43 | ]); 44 | 45 | $resolveInfo = TestResolveInfo::createTestResolveInfo($fieldWithResolve); 46 | 47 | $this->assertEquals('foo', $fieldWithResolve->resolve(['scalarField' => 'foo'], [], $resolveInfo)); 48 | } 49 | } 50 | 51 | class ArticleType extends AbstractObjectType 52 | { 53 | public function build($config) 54 | { 55 | $config->addFields([ 56 | 'title' => new StringType(), 57 | ]); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Issues/Issue90/Issue90Schema.php: -------------------------------------------------------------------------------- 1 | setQuery( 15 | new ObjectType([ 16 | 'name' => 'QueryType', 17 | 'fields' => [ 18 | 'echo' => [ 19 | 'type' => new DateTimeType('Y-m-d H:ia'), 20 | 'args' => [ 21 | 'date' => new DateTimeType('Y-m-d H:ia') 22 | ], 23 | 'resolve' => function ($value, $args, $info) { 24 | 25 | if (isset($args['date'])) { 26 | return $args['date']; 27 | } 28 | 29 | return null; 30 | } 31 | ] 32 | ] 33 | ]) 34 | ); 35 | 36 | $config->setMutation( 37 | new ObjectType([ 38 | 'name' => 'MutationType', 39 | 'fields' => [ 40 | 'echo' => [ 41 | 'type' => new DateTimeType('Y-m-d H:ia'), 42 | 'args' => [ 43 | 'date' => new DateTimeType('Y-m-d H:ia') 44 | ], 45 | 'resolve' => function ($value, $args, $info) { 46 | 47 | if (isset($args['date'])) { 48 | return $args['date']; 49 | } 50 | 51 | return null; 52 | } 53 | ] 54 | ] 55 | ]) 56 | ); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /tests/Issues/Issue98/TypeServiceTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('getfoo', TypeService::getPropertyValue($object, 'foo')); 15 | $this->assertEquals('getbar', TypeService::getPropertyValue($object, 'anything')); 16 | } 17 | 18 | public function testPropertyGetValueWithMagicCall() 19 | { 20 | $object = new DummyObjectWithMagicCall(); 21 | 22 | // __call() should not be considered by default 23 | $this->assertNull(TypeService::getPropertyValue($object, 'foo')); 24 | $this->assertNull(TypeService::getPropertyValue($object, 'anything')); 25 | 26 | //$this->assertEquals('callfoo', TypeService::getPropertyValue($object, 'foo')); 27 | //$this->assertEquals('callbar', TypeService::getPropertyValue($object, 'anything')); 28 | } 29 | } 30 | 31 | class DummyObjectWithMagicGet 32 | { 33 | public function __get($name) 34 | { 35 | if ($name === 'foo') { 36 | return 'getfoo'; 37 | } 38 | 39 | return 'getbar'; 40 | } 41 | 42 | } 43 | 44 | class DummyObjectWithMagicCall 45 | { 46 | public function __call($name, $arguments) 47 | { 48 | if ($name === 'foo') { 49 | return 'callfoo'; 50 | } 51 | 52 | return 'callbar'; 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /tests/Issues/Issue99/Issue99Test.php: -------------------------------------------------------------------------------- 1 | processPayload(sprintf("{ items{id, custom(argX: {x: \"%s\"}){ value } } }", self::BUG_NOT_EXISTS_VALUE)); 21 | $res = $processor->getResponseData(); 22 | 23 | self::assertTrue(isset($res['data']['items'])); 24 | 25 | foreach($res['data']['items'] as $item) { 26 | self::assertTrue(isset($item['custom']['value'])); 27 | self::assertEquals(self::BUG_NOT_EXISTS_VALUE, $item['custom']['value']); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tests/Library/Config/FieldConfigTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/12/16 7:08 PM 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Config; 10 | 11 | 12 | use Youshido\GraphQL\Config\Field\FieldConfig; 13 | use Youshido\GraphQL\Type\Scalar\StringType; 14 | 15 | class FieldConfigTest extends \PHPUnit_Framework_TestCase 16 | { 17 | 18 | public function testInvalidParams() 19 | { 20 | $fieldConfig = new FieldConfig([ 21 | 'name' => 'FirstName', 22 | 'type' => new StringType(), 23 | 'resolve' => function ($value, $args = [], $type = null) { 24 | return 'John'; 25 | } 26 | ]); 27 | 28 | $this->assertEquals('FirstName', $fieldConfig->getName()); 29 | $this->assertEquals(new StringType(), $fieldConfig->getType()); 30 | 31 | $resolveFunction = $fieldConfig->getResolveFunction(); 32 | $this->assertEquals('John', $resolveFunction([])); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /tests/Library/Config/ObjectTypeConfigTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/12/16 4:17 PM 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Config; 10 | 11 | 12 | use Youshido\GraphQL\Config\Object\ObjectTypeConfig; 13 | use Youshido\GraphQL\Validator\ConfigValidator\ConfigValidator; 14 | use Youshido\Tests\DataProvider\TestInterfaceType; 15 | 16 | class ObjectTypeConfigTest extends \PHPUnit_Framework_TestCase 17 | { 18 | 19 | public function testCreation() 20 | { 21 | $config = new ObjectTypeConfig(['name' => 'Test'], null, false); 22 | $this->assertEquals($config->getName(), 'Test', 'Normal creation'); 23 | } 24 | 25 | /** 26 | * @expectedException Youshido\GraphQL\Exception\ConfigurationException 27 | */ 28 | public function testInvalidConfigNoFields() 29 | { 30 | ConfigValidator::getInstance()->assertValidConfig( 31 | new ObjectTypeConfig(['name' => 'Test'], null, true) 32 | ); 33 | } 34 | 35 | /** 36 | * @expectedException Youshido\GraphQL\Exception\ConfigurationException 37 | */ 38 | public function testInvalidConfigInvalidInterface() 39 | { 40 | ConfigValidator::getInstance()->assertValidConfig( 41 | new ObjectTypeConfig(['name' => 'Test', 'interfaces' => ['Invalid interface']], null, false) 42 | ); 43 | } 44 | 45 | public function testInterfaces() 46 | { 47 | $testInterfaceType = new TestInterfaceType(); 48 | $config = new ObjectTypeConfig(['name' => 'Test', 'interfaces' => [$testInterfaceType]], null, false); 49 | $this->assertEquals($config->getInterfaces(), [$testInterfaceType]); 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /tests/Library/Relay/CallableFetcherTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/17/16 11:56 AM 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Relay; 10 | 11 | 12 | use Youshido\GraphQL\Relay\Fetcher\CallableFetcher; 13 | use Youshido\Tests\DataProvider\TestObjectType; 14 | 15 | class CallableFetcherTest extends \PHPUnit_Framework_TestCase 16 | { 17 | public function testMethods() 18 | { 19 | $fetcher = new CallableFetcher(function ($type, $id) { return ['name' => $type . ' Name', 'id' => $id]; }, function ($object) { return $object; }); 20 | $this->assertEquals([ 21 | 'name' => 'User Name', 22 | 'id' => 12 23 | ], $fetcher->resolveNode('User', 12)); 24 | 25 | $object = new TestObjectType(); 26 | $this->assertEquals($object, $fetcher->resolveType($object)); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /tests/Library/Relay/GlobalIdFieldTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 10:51 PM 5/18/16 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Relay; 10 | 11 | 12 | use Youshido\GraphQL\Relay\Field\GlobalIdField; 13 | use Youshido\GraphQL\Type\NonNullType; 14 | use Youshido\GraphQL\Type\Scalar\IdType; 15 | 16 | class GlobalIdFieldTest extends \PHPUnit_Framework_TestCase 17 | { 18 | 19 | public function testSimpleMethods() 20 | { 21 | $typeName = 'user'; 22 | $field = new GlobalIdField($typeName); 23 | $this->assertEquals('id', $field->getName()); 24 | $this->assertEquals('The ID of an object', $field->getDescription()); 25 | $this->assertEquals(new NonNullType(new IdType()), $field->getType()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Library/Relay/MutationTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 2:11 PM 5/19/16 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Relay; 10 | 11 | 12 | use Youshido\GraphQL\Relay\RelayMutation; 13 | use Youshido\GraphQL\Type\Scalar\IdType; 14 | use Youshido\GraphQL\Type\Scalar\IntType; 15 | use Youshido\GraphQL\Type\Scalar\StringType; 16 | 17 | class MutationTest extends \PHPUnit_Framework_TestCase 18 | { 19 | 20 | public function testCreation() 21 | { 22 | $mutation = RelayMutation::buildMutation('ship', [ 23 | 'name' => new StringType() 24 | ],[ 25 | 'id' => new IdType(), 26 | 'name' => new StringType() 27 | ], function($source, $args, $info) { 28 | 29 | }); 30 | $this->assertEquals('ship', $mutation->getName()); 31 | } 32 | 33 | /** 34 | * @expectedException \Exception 35 | */ 36 | public function testInvalidType() 37 | { 38 | RelayMutation::buildMutation('ship', [ 39 | 'name' => new StringType() 40 | ], new IntType(), function($source, $args, $info) {}); 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /tests/Library/Relay/NodeFieldTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/17/16 10:29 PM 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Relay; 10 | 11 | 12 | use Youshido\GraphQL\Relay\Fetcher\CallableFetcher; 13 | use Youshido\GraphQL\Relay\Field\NodeField; 14 | 15 | class NodeFieldTest extends \PHPUnit_Framework_TestCase 16 | { 17 | 18 | public function testMethods() 19 | { 20 | $fetcher = new CallableFetcher(function () { }, function () { }); 21 | $field = new NodeField($fetcher); 22 | 23 | $this->assertEquals('Fetches an object given its ID', $field->getDescription()); 24 | $this->assertEquals('node', $field->getName()); 25 | $this->assertEquals($fetcher, $field->getType()->getFetcher()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Library/Relay/NodeInterfaceTypeTest.php: -------------------------------------------------------------------------------- 1 | 8 | * created: 5/17/16 11:49 AM 9 | */ 10 | 11 | use Youshido\GraphQL\Relay\Fetcher\CallableFetcher; 12 | use Youshido\GraphQL\Relay\NodeInterfaceType; 13 | use Youshido\Tests\DataProvider\TestObjectType; 14 | 15 | class NodeInterfaceTypeTest extends \PHPUnit_Framework_TestCase 16 | { 17 | 18 | public function testMethods() 19 | { 20 | $type = new NodeInterfaceType(); 21 | $testObject = new TestObjectType(); 22 | 23 | 24 | $this->assertEquals('NodeInterface', $type->getName()); 25 | $this->assertNull($type->getFetcher()); 26 | $this->assertNull($type->resolveType($testObject)); 27 | 28 | $fetcher = new CallableFetcher(function () { }, function () { return new TestObjectType(); }); 29 | $type->setFetcher($fetcher); 30 | $this->assertEquals($fetcher, $type->getFetcher()); 31 | 32 | $this->assertEquals($testObject, $type->resolveType($testObject)); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /tests/Library/Relay/NodeTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 3:59 PM 5/17/16 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Relay; 10 | 11 | 12 | use InvalidArgumentException; 13 | use Youshido\GraphQL\Relay\Node; 14 | 15 | class NodeTest extends \PHPUnit_Framework_TestCase 16 | { 17 | public function testMethods() 18 | { 19 | $global = Node::toGlobalId('user', 1); 20 | $fromGlobal = Node::fromGlobalId($global); 21 | 22 | $this->assertEquals('user', $fromGlobal[0]); 23 | $this->assertEquals(1, $fromGlobal[1]); 24 | } 25 | 26 | public function malformedIdProvider() 27 | { 28 | return [ 29 | [''], 30 | [base64_encode('I have no colon')], 31 | [null], 32 | ]; 33 | } 34 | 35 | /** 36 | * @dataProvider malformedIdProvider 37 | */ 38 | public function testFromGlobalIdThrowsExceptionIfGivenMalformedId($idToCheck) 39 | { 40 | $this->setExpectedException(InvalidArgumentException::class); 41 | Node::fromGlobalId($idToCheck); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Library/Type/ListTypeTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/15/16 2:46 PM 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Type; 10 | 11 | 12 | use Youshido\GraphQL\Type\ListType\ListType; 13 | use Youshido\GraphQL\Type\Scalar\StringType; 14 | use Youshido\Tests\DataProvider\TestListType; 15 | 16 | 17 | class ListTypeTest extends \PHPUnit_Framework_TestCase 18 | { 19 | 20 | public function testInline() 21 | { 22 | $listType = new ListType(new StringType()); 23 | $this->assertEquals(new StringType(), $listType->getNamedType()); 24 | $this->assertEquals(new StringType(), $listType->getTypeOf()); 25 | $this->assertTrue($listType->isCompositeType()); 26 | $this->assertTrue($listType->isValidValue(['Test', 'Value'])); 27 | $this->assertFalse($listType->isValidValue('invalid value')); 28 | } 29 | 30 | public function testStandaloneClass() 31 | { 32 | $listType = new TestListType(); 33 | $this->assertEquals(new StringType(), $listType->getNamedType()); 34 | } 35 | 36 | public function testListOfInputsWithArguments() 37 | { 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /tests/Library/Type/ScalarExtendTypeTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 8/14/16 12:16 PM 7 | */ 8 | 9 | namespace Youshido\Tests\Library\Type; 10 | 11 | 12 | use Youshido\GraphQL\Execution\Processor; 13 | use Youshido\GraphQL\Schema\Schema; 14 | use Youshido\GraphQL\Type\Object\ObjectType; 15 | use Youshido\GraphQL\Type\Scalar\StringType; 16 | use Youshido\Tests\DataProvider\TestTimeType; 17 | 18 | class ScalarExtendTypeTest extends \PHPUnit_Framework_TestCase 19 | { 20 | 21 | public function testType() 22 | { 23 | $reportType = new ObjectType([ 24 | 'name' => 'Report', 25 | 'fields' => [ 26 | 'time' => new TestTimeType(), 27 | 'title' => new StringType(), 28 | ] 29 | ]); 30 | $processor = new Processor(new Schema([ 31 | 'query' => new ObjectType([ 32 | 'name' => 'RootQueryType', 33 | 'fields' => [ 34 | 'latestReport' => [ 35 | 'type' => $reportType, 36 | 'resolve' => function () { 37 | return [ 38 | 'title' => 'Accident #1', 39 | 'time' => '13:30:12', 40 | ]; 41 | } 42 | ], 43 | ] 44 | ]) 45 | ]) 46 | ); 47 | 48 | $processor->processPayload('{ latestReport { title, time} }'); 49 | $this->assertEquals(['data' => ['latestReport' => ['title' => 'Accident #1', 'time' => '13:30:12']]], $processor->getResponseData()); 50 | 51 | 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /tests/Library/Type/SchemaDirectivesListTest.php: -------------------------------------------------------------------------------- 1 | addDirective( 14 | new Directive([ 15 | 'name' => 'testDirective' 16 | ]) 17 | ); 18 | $this->assertTrue($directiveList->isDirectiveNameRegistered('testDirective')); 19 | } 20 | 21 | public function testCanAddMultipleDirectives() 22 | { 23 | $directiveList = new SchemaDirectivesList(); 24 | $directiveList->addDirectives([ 25 | new Directive([ 26 | 'name' => 'testDirectiveOne' 27 | ]), 28 | new Directive([ 29 | 'name' => 'testDirectiveTwo' 30 | ]), 31 | ]); 32 | $this->assertTrue($directiveList->isDirectiveNameRegistered('testDirectiveOne')); 33 | $this->assertTrue($directiveList->isDirectiveNameRegistered('testDirectiveTwo')); 34 | } 35 | 36 | public function testItThrowsExceptionWhenAddingInvalidDirectives() 37 | { 38 | $this->setExpectedException(\Exception::class, "addDirectives accept only array of directives"); 39 | $directiveList = new SchemaDirectivesList(); 40 | $directiveList->addDirectives("foobar"); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /tests/Schema/ResolveInfoTest.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 5/19/16 1:28 PM 7 | */ 8 | 9 | namespace Youshido\Tests\Schema; 10 | 11 | 12 | use Youshido\GraphQL\Execution\Context\ExecutionContext; 13 | use Youshido\GraphQL\Execution\ResolveInfo; 14 | use Youshido\GraphQL\Field\Field; 15 | use Youshido\GraphQL\Parser\Ast\Field as FieldAST; 16 | use Youshido\GraphQL\Parser\Location; 17 | use Youshido\GraphQL\Type\Scalar\IntType; 18 | use Youshido\Tests\DataProvider\TestSchema; 19 | 20 | class ResolveInfoTest extends \PHPUnit_Framework_TestCase 21 | { 22 | public function testMethods() 23 | { 24 | $fieldAst = new FieldAST('name', null, [], [], new Location(1,1)); 25 | $field = new Field(['name' => 'id', 'type' => new IntType()]); 26 | $returnType = new IntType(); 27 | $executionContext = new ExecutionContext(new TestSchema()); 28 | $info = new ResolveInfo($field, [$fieldAst], $executionContext); 29 | 30 | $this->assertEquals($field, $info->getField()); 31 | $this->assertEquals([$fieldAst], $info->getFieldASTList()); 32 | $this->assertEquals($returnType, $info->getReturnType()); 33 | $this->assertEquals($executionContext, $info->getExecutionContext()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/StarWars/Schema/CharacterInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * created: 12/6/15 11:15 PM 7 | */ 8 | 9 | namespace Youshido\Tests\StarWars\Schema; 10 | 11 | use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType; 12 | use Youshido\GraphQL\Type\ListType\ListType; 13 | use Youshido\GraphQL\Type\NonNullType; 14 | use Youshido\GraphQL\Type\Scalar\IdType; 15 | use Youshido\GraphQL\Type\Scalar\StringType; 16 | 17 | class CharacterInterface extends AbstractInterfaceType 18 | { 19 | public function build($config) 20 | { 21 | $config 22 | ->addField('id', new NonNullType(new IdType())) 23 | ->addField('name', new NonNullType(new StringType())) 24 | ->addField('friends', [ 25 | 'type' => new ListType(new CharacterInterface()), 26 | 'resolve' => function ($value) { 27 | return $value['friends']; 28 | } 29 | ]) 30 | ->addField('appearsIn', new ListType(new EpisodeEnum())); 31 | } 32 | 33 | public function getDescription() 34 | { 35 | return 'A character in the Star Wars Trilogy'; 36 | } 37 | 38 | public function getName() 39 | { 40 | return 'Character'; 41 | } 42 | 43 | public function resolveType($object) 44 | { 45 | $humans = StarWarsData::humans(); 46 | $droids = StarWarsData::droids(); 47 | 48 | $id = isset($object['id']) ? $object['id'] : $object; 49 | 50 | if (isset($humans[$id])) { 51 | return new HumanType(); 52 | } 53 | 54 | if (isset($droids[$id])) { 55 | return new DroidType(); 56 | } 57 | 58 | return null; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /tests/StarWars/Schema/DroidType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\Tests\StarWars\Schema; 9 | 10 | 11 | use Youshido\GraphQL\Type\TypeMap; 12 | 13 | class DroidType extends HumanType 14 | { 15 | 16 | /** 17 | * @return String type name 18 | */ 19 | public function getName() 20 | { 21 | return 'Droid'; 22 | } 23 | 24 | public function build($config) 25 | { 26 | parent::build($config); 27 | 28 | $config->getField('friends')->getConfig()->set('resolve', function ($droid) { 29 | return StarWarsData::getFriends($droid); 30 | }); 31 | 32 | $config 33 | ->addField('primaryFunction', TypeMap::TYPE_STRING); 34 | } 35 | 36 | public function getInterfaces() 37 | { 38 | return [new CharacterInterface()]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/StarWars/Schema/EpisodeEnum.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\Tests\StarWars\Schema; 9 | 10 | 11 | use Youshido\GraphQL\Type\Enum\AbstractEnumType; 12 | 13 | class EpisodeEnum extends AbstractEnumType 14 | { 15 | 16 | public function getValues() 17 | { 18 | return [ 19 | [ 20 | 'value' => 4, 21 | 'name' => 'NEWHOPE', 22 | 'description' => 'Released in 1977.' 23 | ], 24 | [ 25 | 'value' => 5, 26 | 'name' => 'EMPIRE', 27 | 'description' => 'Released in 1980.' 28 | ], 29 | [ 30 | 'value' => 6, 31 | 'name' => 'JEDI', 32 | 'description' => 'Released in 1983.' 33 | ], 34 | ]; 35 | } 36 | 37 | /** 38 | * @return String type name 39 | */ 40 | public function getName() 41 | { 42 | return 'Episode'; 43 | } 44 | } -------------------------------------------------------------------------------- /tests/StarWars/Schema/HumanType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\Tests\StarWars\Schema; 9 | 10 | 11 | use Youshido\GraphQL\Type\ListType\ListType; 12 | use Youshido\GraphQL\Type\NonNullType; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\Scalar\IdType; 15 | use Youshido\GraphQL\Type\Scalar\StringType; 16 | use Youshido\GraphQL\Type\TypeMap; 17 | 18 | class HumanType extends AbstractObjectType 19 | { 20 | 21 | public function build($config) 22 | { 23 | $config 24 | ->addField('id', new NonNullType(new IdType())) 25 | ->addField('name', new NonNullType(new StringType())) 26 | ->addField('friends', [ 27 | 'type' => new ListType(new CharacterInterface()), 28 | 'resolve' => function ($droid) { 29 | return StarWarsData::getFriends($droid); 30 | }, 31 | ]) 32 | ->addField('appearsIn', new ListType(new EpisodeEnum())) 33 | ->addField('homePlanet', TypeMap::TYPE_STRING); 34 | } 35 | 36 | public function getInterfaces() 37 | { 38 | return [new CharacterInterface()]; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /tests/StarWars/Schema/StarWarsQueryType.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\Tests\StarWars\Schema; 9 | 10 | 11 | use Youshido\GraphQL\Field\Field; 12 | use Youshido\GraphQL\Field\FieldFactory; 13 | use Youshido\GraphQL\Type\Object\AbstractObjectType; 14 | use Youshido\GraphQL\Type\Scalar\IdType; 15 | 16 | class StarWarsQueryType extends AbstractObjectType 17 | { 18 | 19 | /** 20 | * @return String type name 21 | */ 22 | public function getName() 23 | { 24 | return 'Query'; 25 | } 26 | 27 | public function build($config) 28 | { 29 | $config 30 | ->addField('hero', [ 31 | 'type' => new CharacterInterface(), 32 | 'args' => [ 33 | 'episode' => ['type' => new EpisodeEnum()] 34 | ], 35 | 'resolve' => function ($root, $args) { 36 | return StarWarsData::getHero(isset($args['episode']) ? $args['episode'] : null); 37 | }, 38 | ]) 39 | ->addField(new Field([ 40 | 'name' => 'human', 41 | 'type' => new HumanType(), 42 | 'args' => [ 43 | 'id' => new IdType() 44 | ], 45 | 'resolve' => function ($value = null, $args = []) { 46 | $humans = StarWarsData::humans(); 47 | 48 | return isset($humans[$args['id']]) ? $humans[$args['id']] : null; 49 | } 50 | ])) 51 | ->addField(new Field([ 52 | 'name' => 'droid', 53 | 'type' => new DroidType(), 54 | 'args' => [ 55 | 'id' => new IdType() 56 | ], 57 | 'resolve' => function ($value = null, $args = []) { 58 | $droids = StarWarsData::droids(); 59 | 60 | return isset($droids[$args['id']]) ? $droids[$args['id']] : null; 61 | } 62 | ])); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/StarWars/Schema/StarWarsSchema.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Youshido\Tests\StarWars\Schema; 9 | 10 | 11 | use Youshido\GraphQL\Config\Schema\SchemaConfig; 12 | use Youshido\GraphQL\Schema\AbstractSchema; 13 | 14 | class StarWarsSchema extends AbstractSchema 15 | { 16 | 17 | public function build(SchemaConfig $config) 18 | { 19 | $config->setQuery(new StarWarsQueryType()); 20 | } 21 | 22 | } 23 | --------------------------------------------------------------------------------