SlideShare ist ein Scribd-Unternehmen logo
1 von 25
GraphQL
Consistency through code generation
by Aleksandr Obukhov
Aleksandr Obukhov
Lead Backend Engineer at AMBOSS GmbH
https://github.com/obukhov
@dclg_en
GraphQL
Consistency through code generation
by Aleksandr Obukhov
What is GraphQL?
Rise-your-hands game
Who is well versed with the GraphQL specification?
Who tried to implement GraphQL server with PHP?
Who is supporting GraphQL server in a production environment?
GraphQL
Server implementations are available for multiple languages, including Haskell,
JavaScript, Python, Ruby, Java, C#, Scala, Go, Elixir, Erlang, PHP, and Clojure
- API specification and runtime
- developed by Facebook
- published in 2015
RPC
API Interaction is just a remote
procedure call:
- It has “function name”
- It has arguments
- It has return value (response)
REST
All data is a resource:
- It can be created / read / updated /
deleted
- Resource is identified by URL
- It can be connected to another
resources through relation
Concepts of API
RPC REST
- Versioning of resource schema
- Limited set of operations with
resources
Limitations
- Versioning of endpoints and
response data
- Pre-designed structure of input
and output
Why do we like GraphQL?
Validation of input / output data
Tools: automatic documentation based on schema
Ready-to-be-used specification
Schema defines: data types, abstractions, relations
Operations defined by root Query/Mutation objects
All this can be found in other specifications
Why do we like GraphQL?
Query is disambiguous:
- no wildcard fields – you don’t need resource versions,
- type assertions, interfaces and unions – easy to extend with new types
Nice:
- built-in deprecation mechanism
Instead of implementing complete endpoints, developer defines the way to
resolve relations – easier to reuse code.
Schema definition language is simple and robust
Transport layer
How do we GraphQL?
webonyx/graphql-php
Request
Query
validation
Mapping to
resolvers
Assembling
result
Response
validation &
formatting
Response
Your app API layer
?
Global field resolver to resolve object properties
class DefaultFieldResolver
{
// ...
public function __invoke($source, $args, $context, ResolveInfo $info)
{
$fieldName = $info->fieldName;
$property = null;
if ($source instanceof TypeInterface) {
$method = 'get' . ucfirst($fieldName);
if (!method_exists($source, $method)) {
throw new FieldNotImplemented('Field <' . $fieldName . '> is not implemented for type <' . $info->parentType . '>');
}
$property = call_user_func_array([$source, $method], $args);
}
$fieldValue = $property instanceof Closure ? $property($source, $args, $context, $info) : $property;
return $fieldValue;
}
}
Adapters to represent API objectTypes
class TaxonomyType extends AbstractTaxonomyType
{
/** @var TaxonomyDTO */
private $taxonomy;
// ...
/** @return int */
public function getTreeDepth()
{
return $this->taxonomy->getTreeDepth();
}
/** @return TaxonType[] */
public function getTaxa()
{
return $this->dataLoaderRegistry->get('taxonByParentTaxon')->load($this->taxonomy->getRootTaxonId());
}
}
What are challenges there
Return values is up to your resolver implementation, but still duplicates schema
Resolvers receive validated data, but it is still presented as associative array
All inconsistencies can be spotted in the runtime only
Enum values needs to be duplicated
Input and output types field names needs to be duplicated
You define a set of resolver functions that implement application BL
GraphQL schema: code generation
Tom Landvoigt & Alex Obukhov
What schema defines
Input Object Type
Query Type Mutation Type
EnumInterface Object Type Unions
What schema defines
Input Object Type
input FeedbackInput {
message: String!
type: FeedbackType!
source: FeedbackSource!
}
Query Type
type Query {
user(eid: ID!): User
}
Mutation Type
type Mutation {
submitFeedback(feedback: FeedbackInput!): Boolean
}
Enum
enum Stage {
preclinic
clinic
doctor
}
Interface
interface Entity {
eid: ID!
}
Object Type
type User implements Entity {
eid: ID!
stage: Stage!
firstName: String
lastName: String
}
Code generation: Object Type
type User implements Entity {
eid: ID!
stage: Stage!
firstName: String
lastName: String
}
abstract class AbstractUserType implements EntityInterface
{
/** @return string */
abstract public function getEid();
/** @return string */
abstract public function getStage();
/** @return null|string */
abstract public function getFirstName();
/** @return null|string */
abstract public function getLastName();
}
Abstract class can be extended
by multiple different classes
Code generation: Object Type
class UserType extends AbstractUserType
{
private $user;
/** @return string */
public function getEid()
{
return $this->user->getUuid();
}
...
}
class AdminType extends AbstractUserType
{
private $admin;
/** @return string */
public function getEid()
{
return $this->admin>getExternalId();
}
...
}
Code generation: Interface
interface Entity {
eid: ID!
}
interface EntityInterface
{
/**
* @return string | int
*/
public function getEid();
}
Abstract class implements this
interface automatically
Code generation: Input Object Type
input FeedbackInput {
message: String!
type: FeedbackType!
source: FeedbackSource!
}
class FeedbackInputType
{
/** @var string */
private $message;
/** @var string */
private $type;
/** @var FeedbackSourceType */
private $source;
public function __construct(array $inputValues)
{
$this->message = $inputValues['message'];
$this->type = $inputValues['type'];
$this->source = new FeedbackSourceType(
$inputValues['source']
);
}
Input is wrapped to value object
recursively
Code generation: Enum
enum Stage {
preclinic
clinic
doctor
}
class StageEnum extends Enum
{
const PRECLINIC = 'preclinic';
const CLINIC = 'clinic';
const DOCTOR = 'doctor';
/** @inheritdoc */
public function getValues()
{
return [
self::PRECLINIC,
self::CLINIC,
self::DOCTOR,
];
}
}
Constants can be used to
guarantee the consistency
of schema via static analysis
Code generation: Union
union NodeContent = File | Folder
type Node {
eid: ID!
firstName: String
content: NodeContent
}
class NodeType extends AbstractNodeType
{
/** @return FileType|FolderType */
public function getContents()
{
...
}
}
Union definitions are used to
declare @return in the docblcok
Benefits
- Easier to kick-off new type – just extend and use IDE to create stubs
- Ready-to-be-used value objects for Enum and Input
- Automatic interfaces implementation
- Docblock support for types defined by schema
- Schema inconsistencies can be detected with static code analysis
Thank you! Questions?

Weitere ähnliche Inhalte

Was ist angesagt?

PHP Unit 3 functions_in_php_2
PHP Unit 3 functions_in_php_2PHP Unit 3 functions_in_php_2
PHP Unit 3 functions_in_php_2
Kumar
 

Was ist angesagt? (20)

What You Need to Know about Lambdas
What You Need to Know about LambdasWhat You Need to Know about Lambdas
What You Need to Know about Lambdas
 
Php Chapter 2 3 Training
Php Chapter 2 3 TrainingPhp Chapter 2 3 Training
Php Chapter 2 3 Training
 
PHP Unit 3 functions_in_php_2
PHP Unit 3 functions_in_php_2PHP Unit 3 functions_in_php_2
PHP Unit 3 functions_in_php_2
 
Functional Programming in JavaScript
Functional Programming in JavaScriptFunctional Programming in JavaScript
Functional Programming in JavaScript
 
JavaScript Core
JavaScript CoreJavaScript Core
JavaScript Core
 
Client sidescripting javascript
Client sidescripting javascriptClient sidescripting javascript
Client sidescripting javascript
 
Functional Javascript
Functional JavascriptFunctional Javascript
Functional Javascript
 
JavaScript Functions
JavaScript FunctionsJavaScript Functions
JavaScript Functions
 
Functional Principles for OO Developers
Functional Principles for OO DevelopersFunctional Principles for OO Developers
Functional Principles for OO Developers
 
Function overloading and overriding
Function overloading and overridingFunction overloading and overriding
Function overloading and overriding
 
Class 3 - PHP Functions
Class 3 - PHP FunctionsClass 3 - PHP Functions
Class 3 - PHP Functions
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
Functions in c++
Functions in c++Functions in c++
Functions in c++
 
Lecture 3 getting_started_with__c_
Lecture 3 getting_started_with__c_Lecture 3 getting_started_with__c_
Lecture 3 getting_started_with__c_
 
JavaScript - Chapter 9 - TypeConversion and Regular Expressions
 JavaScript - Chapter 9 - TypeConversion and Regular Expressions  JavaScript - Chapter 9 - TypeConversion and Regular Expressions
JavaScript - Chapter 9 - TypeConversion and Regular Expressions
 
Function overloading
Function overloadingFunction overloading
Function overloading
 
Contracts in-clojure-pete
Contracts in-clojure-peteContracts in-clojure-pete
Contracts in-clojure-pete
 
JavaScript For CSharp Developer
JavaScript For CSharp DeveloperJavaScript For CSharp Developer
JavaScript For CSharp Developer
 
JavaScript - Chapter 6 - Basic Functions
 JavaScript - Chapter 6 - Basic Functions JavaScript - Chapter 6 - Basic Functions
JavaScript - Chapter 6 - Basic Functions
 
Introduction to es6
Introduction to es6Introduction to es6
Introduction to es6
 

Ähnlich wie PHP: GraphQL consistency through code generation

Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 

Ähnlich wie PHP: GraphQL consistency through code generation (20)

Hexagonal architecture in PHP
Hexagonal architecture in PHPHexagonal architecture in PHP
Hexagonal architecture in PHP
 
Drupaljam xl 2019 presentation multilingualism makes better programmers
Drupaljam xl 2019 presentation   multilingualism makes better programmersDrupaljam xl 2019 presentation   multilingualism makes better programmers
Drupaljam xl 2019 presentation multilingualism makes better programmers
 
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
 
Using the Windows 8 Runtime from C++
Using the Windows 8 Runtime from C++Using the Windows 8 Runtime from C++
Using the Windows 8 Runtime from C++
 
Web2Day 2017 - Concilier DomainDriveDesign et API REST
Web2Day 2017 - Concilier DomainDriveDesign et API RESTWeb2Day 2017 - Concilier DomainDriveDesign et API REST
Web2Day 2017 - Concilier DomainDriveDesign et API REST
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
 
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
 
What's New In Laravel 5
What's New In Laravel 5What's New In Laravel 5
What's New In Laravel 5
 
Применение паттерна Page Object для автоматизации веб сервисов - новый взгляд
Применение паттерна Page Object для автоматизации веб сервисов - новый взглядПрименение паттерна Page Object для автоматизации веб сервисов - новый взгляд
Применение паттерна Page Object для автоматизации веб сервисов - новый взгляд
 
OOP in PHP
OOP in PHPOOP in PHP
OOP in PHP
 
How to code to code less
How to code to code lessHow to code to code less
How to code to code less
 
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Flask and Angular: An approach to build robust platforms
Flask and Angular:  An approach to build robust platformsFlask and Angular:  An approach to build robust platforms
Flask and Angular: An approach to build robust platforms
 
PHP in one presentation
PHP in one presentationPHP in one presentation
PHP in one presentation
 
Применение паттерна Page Object для автоматизации веб сервисов
Применение паттерна Page Object для автоматизации веб сервисовПрименение паттерна Page Object для автоматизации веб сервисов
Применение паттерна Page Object для автоматизации веб сервисов
 
Creating native apps with WordPress
Creating native apps with WordPressCreating native apps with WordPress
Creating native apps with WordPress
 
ClojureScript - Making Front-End development Fun again - John Stevenson - Cod...
ClojureScript - Making Front-End development Fun again - John Stevenson - Cod...ClojureScript - Making Front-End development Fun again - John Stevenson - Cod...
ClojureScript - Making Front-End development Fun again - John Stevenson - Cod...
 
Test-Driven Documentation for your REST(ful) service
Test-Driven Documentation for your REST(ful) serviceTest-Driven Documentation for your REST(ful) service
Test-Driven Documentation for your REST(ful) service
 
Building End to-End Web Apps Using TypeScript
Building End to-End Web Apps Using TypeScriptBuilding End to-End Web Apps Using TypeScript
Building End to-End Web Apps Using TypeScript
 

Kürzlich hochgeladen

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Kürzlich hochgeladen (20)

What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 

PHP: GraphQL consistency through code generation

  • 1. GraphQL Consistency through code generation by Aleksandr Obukhov
  • 2. Aleksandr Obukhov Lead Backend Engineer at AMBOSS GmbH https://github.com/obukhov @dclg_en
  • 3. GraphQL Consistency through code generation by Aleksandr Obukhov
  • 5. Rise-your-hands game Who is well versed with the GraphQL specification? Who tried to implement GraphQL server with PHP? Who is supporting GraphQL server in a production environment?
  • 6. GraphQL Server implementations are available for multiple languages, including Haskell, JavaScript, Python, Ruby, Java, C#, Scala, Go, Elixir, Erlang, PHP, and Clojure - API specification and runtime - developed by Facebook - published in 2015
  • 7. RPC API Interaction is just a remote procedure call: - It has “function name” - It has arguments - It has return value (response) REST All data is a resource: - It can be created / read / updated / deleted - Resource is identified by URL - It can be connected to another resources through relation Concepts of API
  • 8. RPC REST - Versioning of resource schema - Limited set of operations with resources Limitations - Versioning of endpoints and response data - Pre-designed structure of input and output
  • 9. Why do we like GraphQL? Validation of input / output data Tools: automatic documentation based on schema Ready-to-be-used specification Schema defines: data types, abstractions, relations Operations defined by root Query/Mutation objects All this can be found in other specifications
  • 10. Why do we like GraphQL? Query is disambiguous: - no wildcard fields – you don’t need resource versions, - type assertions, interfaces and unions – easy to extend with new types Nice: - built-in deprecation mechanism Instead of implementing complete endpoints, developer defines the way to resolve relations – easier to reuse code. Schema definition language is simple and robust
  • 11. Transport layer How do we GraphQL? webonyx/graphql-php Request Query validation Mapping to resolvers Assembling result Response validation & formatting Response Your app API layer ?
  • 12. Global field resolver to resolve object properties class DefaultFieldResolver { // ... public function __invoke($source, $args, $context, ResolveInfo $info) { $fieldName = $info->fieldName; $property = null; if ($source instanceof TypeInterface) { $method = 'get' . ucfirst($fieldName); if (!method_exists($source, $method)) { throw new FieldNotImplemented('Field <' . $fieldName . '> is not implemented for type <' . $info->parentType . '>'); } $property = call_user_func_array([$source, $method], $args); } $fieldValue = $property instanceof Closure ? $property($source, $args, $context, $info) : $property; return $fieldValue; } }
  • 13. Adapters to represent API objectTypes class TaxonomyType extends AbstractTaxonomyType { /** @var TaxonomyDTO */ private $taxonomy; // ... /** @return int */ public function getTreeDepth() { return $this->taxonomy->getTreeDepth(); } /** @return TaxonType[] */ public function getTaxa() { return $this->dataLoaderRegistry->get('taxonByParentTaxon')->load($this->taxonomy->getRootTaxonId()); } }
  • 14. What are challenges there Return values is up to your resolver implementation, but still duplicates schema Resolvers receive validated data, but it is still presented as associative array All inconsistencies can be spotted in the runtime only Enum values needs to be duplicated Input and output types field names needs to be duplicated You define a set of resolver functions that implement application BL
  • 15. GraphQL schema: code generation Tom Landvoigt & Alex Obukhov
  • 16. What schema defines Input Object Type Query Type Mutation Type EnumInterface Object Type Unions
  • 17. What schema defines Input Object Type input FeedbackInput { message: String! type: FeedbackType! source: FeedbackSource! } Query Type type Query { user(eid: ID!): User } Mutation Type type Mutation { submitFeedback(feedback: FeedbackInput!): Boolean } Enum enum Stage { preclinic clinic doctor } Interface interface Entity { eid: ID! } Object Type type User implements Entity { eid: ID! stage: Stage! firstName: String lastName: String }
  • 18. Code generation: Object Type type User implements Entity { eid: ID! stage: Stage! firstName: String lastName: String } abstract class AbstractUserType implements EntityInterface { /** @return string */ abstract public function getEid(); /** @return string */ abstract public function getStage(); /** @return null|string */ abstract public function getFirstName(); /** @return null|string */ abstract public function getLastName(); } Abstract class can be extended by multiple different classes
  • 19. Code generation: Object Type class UserType extends AbstractUserType { private $user; /** @return string */ public function getEid() { return $this->user->getUuid(); } ... } class AdminType extends AbstractUserType { private $admin; /** @return string */ public function getEid() { return $this->admin>getExternalId(); } ... }
  • 20. Code generation: Interface interface Entity { eid: ID! } interface EntityInterface { /** * @return string | int */ public function getEid(); } Abstract class implements this interface automatically
  • 21. Code generation: Input Object Type input FeedbackInput { message: String! type: FeedbackType! source: FeedbackSource! } class FeedbackInputType { /** @var string */ private $message; /** @var string */ private $type; /** @var FeedbackSourceType */ private $source; public function __construct(array $inputValues) { $this->message = $inputValues['message']; $this->type = $inputValues['type']; $this->source = new FeedbackSourceType( $inputValues['source'] ); } Input is wrapped to value object recursively
  • 22. Code generation: Enum enum Stage { preclinic clinic doctor } class StageEnum extends Enum { const PRECLINIC = 'preclinic'; const CLINIC = 'clinic'; const DOCTOR = 'doctor'; /** @inheritdoc */ public function getValues() { return [ self::PRECLINIC, self::CLINIC, self::DOCTOR, ]; } } Constants can be used to guarantee the consistency of schema via static analysis
  • 23. Code generation: Union union NodeContent = File | Folder type Node { eid: ID! firstName: String content: NodeContent } class NodeType extends AbstractNodeType { /** @return FileType|FolderType */ public function getContents() { ... } } Union definitions are used to declare @return in the docblcok
  • 24. Benefits - Easier to kick-off new type – just extend and use IDE to create stubs - Ready-to-be-used value objects for Enum and Input - Automatic interfaces implementation - Docblock support for types defined by schema - Schema inconsistencies can be detected with static code analysis