diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/404.html b/404.html new file mode 100644 index 000000000..ff33803d3 --- /dev/null +++ b/404.html @@ -0,0 +1,592 @@ + + + + + + + + + + + + + + + + + + + + graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 000000000..1cf13b9f9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.51d95adb.min.js b/assets/javascripts/bundle.51d95adb.min.js new file mode 100644 index 000000000..b20ec6835 --- /dev/null +++ b/assets/javascripts/bundle.51d95adb.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Hi=Object.create;var xr=Object.defineProperty;var Pi=Object.getOwnPropertyDescriptor;var $i=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Ii=Object.getPrototypeOf,Er=Object.prototype.hasOwnProperty,an=Object.prototype.propertyIsEnumerable;var on=(e,t,r)=>t in e?xr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Er.call(t,r)&&on(e,r,t[r]);if(kt)for(var r of kt(t))an.call(t,r)&&on(e,r,t[r]);return e};var sn=(e,t)=>{var r={};for(var n in e)Er.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&kt)for(var n of kt(e))t.indexOf(n)<0&&an.call(e,n)&&(r[n]=e[n]);return r};var Ht=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Fi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $i(t))!Er.call(e,o)&&o!==r&&xr(e,o,{get:()=>t[o],enumerable:!(n=Pi(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Hi(Ii(e)):{},Fi(t||!e||!e.__esModule?xr(r,"default",{value:e,enumerable:!0}):r,e));var fn=Ht((wr,cn)=>{(function(e,t){typeof wr=="object"&&typeof cn!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(wr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(T){return!!(T&&T!==document&&T.nodeName!=="HTML"&&T.nodeName!=="BODY"&&"classList"in T&&"contains"in T.classList)}function f(T){var Ke=T.type,We=T.tagName;return!!(We==="INPUT"&&a[Ke]&&!T.readOnly||We==="TEXTAREA"&&!T.readOnly||T.isContentEditable)}function c(T){T.classList.contains("focus-visible")||(T.classList.add("focus-visible"),T.setAttribute("data-focus-visible-added",""))}function u(T){T.hasAttribute("data-focus-visible-added")&&(T.classList.remove("focus-visible"),T.removeAttribute("data-focus-visible-added"))}function p(T){T.metaKey||T.altKey||T.ctrlKey||(s(r.activeElement)&&c(r.activeElement),n=!0)}function m(T){n=!1}function d(T){s(T.target)&&(n||f(T.target))&&c(T.target)}function h(T){s(T.target)&&(T.target.classList.contains("focus-visible")||T.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(T.target))}function v(T){document.visibilityState==="hidden"&&(o&&(n=!0),B())}function B(){document.addEventListener("mousemove",z),document.addEventListener("mousedown",z),document.addEventListener("mouseup",z),document.addEventListener("pointermove",z),document.addEventListener("pointerdown",z),document.addEventListener("pointerup",z),document.addEventListener("touchmove",z),document.addEventListener("touchstart",z),document.addEventListener("touchend",z)}function re(){document.removeEventListener("mousemove",z),document.removeEventListener("mousedown",z),document.removeEventListener("mouseup",z),document.removeEventListener("pointermove",z),document.removeEventListener("pointerdown",z),document.removeEventListener("pointerup",z),document.removeEventListener("touchmove",z),document.removeEventListener("touchstart",z),document.removeEventListener("touchend",z)}function z(T){T.target.nodeName&&T.target.nodeName.toLowerCase()==="html"||(n=!1,re())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),B(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var un=Ht(Sr=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},a=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(re,z){d.append(z,re)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(T){throw new Error("URL unable to set base "+c+" due to "+T)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,B=!0,re=this;["append","delete","set"].forEach(function(T){var Ke=h[T];h[T]=function(){Ke.apply(h,arguments),v&&(B=!1,re.search=h.toString(),B=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var z=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==z&&(z=this.search,B&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},a=i.prototype,s=function(f){Object.defineProperty(a,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){s(f)}),Object.defineProperty(a,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(a,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr)});var Qr=Ht((Lt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Lt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Lt=="object"?Lt.ClipboardJS=r():t.ClipboardJS=r()})(Lt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return ki}});var a=i(279),s=i.n(a),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(O){return!1}}var d=function(O){var w=p()(O);return m("cut"),w},h=d;function v(j){var O=document.documentElement.getAttribute("dir")==="rtl",w=document.createElement("textarea");w.style.fontSize="12pt",w.style.border="0",w.style.padding="0",w.style.margin="0",w.style.position="absolute",w.style[O?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return w.style.top="".concat(k,"px"),w.setAttribute("readonly",""),w.value=j,w}var B=function(O,w){var k=v(O);w.container.appendChild(k);var F=p()(k);return m("copy"),k.remove(),F},re=function(O){var w=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},k="";return typeof O=="string"?k=B(O,w):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?k=B(O.value,w):(k=p()(O),m("copy")),k},z=re;function T(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?T=function(w){return typeof w}:T=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},T(j)}var Ke=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},w=O.action,k=w===void 0?"copy":w,F=O.container,q=O.target,Le=O.text;if(k!=="copy"&&k!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&T(q)==="object"&&q.nodeType===1){if(k==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(k==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Le)return z(Le,{container:F});if(q)return k==="cut"?h(q):z(q,{container:F})},We=Ke;function Ie(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(w){return typeof w}:Ie=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},Ie(j)}function Ti(j,O){if(!(j instanceof O))throw new TypeError("Cannot call a class as a function")}function nn(j,O){for(var w=0;w0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Ie(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var q=this;this.listener=c()(F,"click",function(Le){return q.onClick(Le)})}},{key:"onClick",value:function(F){var q=F.delegateTarget||F.currentTarget,Le=this.action(q)||"copy",Rt=We({action:Le,container:this.container,target:this.target(q),text:this.text(q)});this.emit(Rt?"success":"error",{action:Le,text:Rt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return yr("action",F)}},{key:"defaultTarget",value:function(F){var q=yr("target",F);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(F){return yr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return z(F,q)}},{key:"cut",value:function(F){return h(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof F=="string"?[F]:F,Le=!!document.queryCommandSupported;return q.forEach(function(Rt){Le=Le&&!!document.queryCommandSupported(Rt)}),Le}}]),w}(s()),ki=Ri},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,f){for(;s&&s.nodeType!==o;){if(typeof s.matches=="function"&&s.matches(f))return s;s=s.parentNode}}n.exports=a},438:function(n,o,i){var a=i(828);function s(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?s.apply(null,arguments):typeof m=="function"?s.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return s(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=a(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(n,o,i){var a=i(879),s=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(h))throw new TypeError("Third argument must be a Function");if(a.node(m))return c(m,d,h);if(a.nodeList(m))return u(m,d,h);if(a.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return s(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),a=f.toString()}return a}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,a,s){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var f=this;function c(){f.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=s.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var is=/["'&<>]/;Jo.exports=as;function as(e){var t=""+e,r=is.exec(t);if(!r)return t;var n,o="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],a;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(s){a={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||s(m,d)})})}function s(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof Xe?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){s("next",m)}function u(m){s("throw",m)}function p(m,d){m(d),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof xe=="function"?xe(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(a){return new Promise(function(s,f){a=e[i](a),o(s,f,a.done,a.value)})}}function o(i,a,s,f){Promise.resolve(f).then(function(c){i({value:c,done:s})},a)}}function A(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var $t=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=xe(a),f=s.next();!f.done;f=s.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var u=this.initialTeardown;if(A(u))try{u()}catch(v){i=v instanceof $t?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=xe(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{dn(h)}catch(v){i=i!=null?i:[],v instanceof $t?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new $t(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)dn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Or=Fe.EMPTY;function It(e){return e instanceof Fe||e&&"closed"in e&&A(e.remove)&&A(e.add)&&A(e.unsubscribe)}function dn(e){A(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,a=o.isStopped,s=o.observers;return i||a?Or:(this.currentObservers=null,s.push(r),new Fe(function(){n.currentObservers=null,De(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,a=n.isStopped;o?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new U;return r.source=this,r},t.create=function(r,n){return new wn(r,n)},t}(U);var wn=function(e){ne(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Or},t}(E);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ne(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,a=n._infiniteTimeWindow,s=n._timestampProvider,f=n._windowTime;o||(i.push(r),!a&&i.push(s.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,a=o._buffer,s=a.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var a=r.actions;n!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Ut);var On=function(e){ne(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Wt);var we=new On(Tn);var R=new U(function(e){return e.complete()});function Dt(e){return e&&A(e.schedule)}function kr(e){return e[e.length-1]}function Qe(e){return A(kr(e))?e.pop():void 0}function Se(e){return Dt(kr(e))?e.pop():void 0}function Vt(e,t){return typeof kr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function zt(e){return A(e==null?void 0:e.then)}function Nt(e){return A(e[ft])}function qt(e){return Symbol.asyncIterator&&A(e==null?void 0:e[Symbol.asyncIterator])}function Kt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ki(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Qt=Ki();function Yt(e){return A(e==null?void 0:e[Qt])}function Gt(e){return ln(this,arguments,function(){var r,n,o,i;return Pt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,Xe(r.read())];case 3:return n=a.sent(),o=n.value,i=n.done,i?[4,Xe(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,Xe(o)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return A(e==null?void 0:e.getReader)}function $(e){if(e instanceof U)return e;if(e!=null){if(Nt(e))return Qi(e);if(pt(e))return Yi(e);if(zt(e))return Gi(e);if(qt(e))return _n(e);if(Yt(e))return Bi(e);if(Bt(e))return Ji(e)}throw Kt(e)}function Qi(e){return new U(function(t){var r=e[ft]();if(A(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Yi(e){return new U(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?_(function(o,i){return e(o,i,n)}):me,Oe(1),r?He(t):zn(function(){return new Xt}))}}function Nn(){for(var e=[],t=0;t=2,!0))}function fe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new E}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,f=s===void 0?!0:s;return function(c){var u,p,m,d=0,h=!1,v=!1,B=function(){p==null||p.unsubscribe(),p=void 0},re=function(){B(),u=m=void 0,h=v=!1},z=function(){var T=u;re(),T==null||T.unsubscribe()};return g(function(T,Ke){d++,!v&&!h&&B();var We=m=m!=null?m:r();Ke.add(function(){d--,d===0&&!v&&!h&&(p=jr(z,f))}),We.subscribe(Ke),!u&&d>0&&(u=new et({next:function(Ie){return We.next(Ie)},error:function(Ie){v=!0,B(),p=jr(re,o,Ie),We.error(Ie)},complete:function(){h=!0,B(),p=jr(re,a),We.complete()}}),$(T).subscribe(u))})(c)}}function jr(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function V(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),N(e===_e()),Y())}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function Yn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,we),l(()=>Be(e)),N(Be(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,we),l(()=>rr(e)),N(rr(e)))}var Bn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!zr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),xa?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!zr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ya.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Jn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Zn=typeof WeakMap!="undefined"?new WeakMap:new Bn,eo=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=Ea.getInstance(),n=new Ra(t,r,this);Zn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){eo.prototype[e]=function(){var t;return(t=Zn.get(this))[e].apply(t,arguments)}});var ka=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:eo}(),to=ka;var ro=new E,Ha=I(()=>H(new to(e=>{for(let t of e)ro.next(t)}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){return Ha.pipe(S(t=>t.observe(e)),x(t=>ro.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(()=>de(e)))),N(de(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var no=new E,Pa=I(()=>H(new IntersectionObserver(e=>{for(let t of e)no.next(t)},{threshold:0}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function sr(e){return Pa.pipe(S(t=>t.observe(e)),x(t=>no.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function oo(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=de(e),o=bt(e);return r>=o.height-n.height-t}),Y())}var cr={drawer:V("[data-md-toggle=drawer]"),search:V("[data-md-toggle=search]")};function io(e){return cr[e].checked}function qe(e,t){cr[e].checked!==t&&cr[e].click()}function je(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),N(t.checked))}function $a(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ia(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(N(!1))}function ao(){let e=b(window,"keydown").pipe(_(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:io("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),_(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!$a(n,r)}return!0}),fe());return Ia().pipe(x(t=>t?R:e))}function Me(){return new URL(location.href)}function ot(e){location.href=e.href}function so(){return new E}function co(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)co(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)co(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function fo(){return location.hash.substring(1)}function uo(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Fa(){return b(window,"hashchange").pipe(l(fo),N(fo()),_(e=>e.length>0),J(1))}function po(){return Fa().pipe(l(e=>se(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function Nr(e){let t=matchMedia(e);return Zt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function lo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(N(e.matches))}function qr(e,t){return e.pipe(x(r=>r?t():R))}function ur(e,t={credentials:"same-origin"}){return ve(fetch(`${e}`,t)).pipe(ce(()=>R),x(r=>r.status!==200?Tt(()=>new Error(r.statusText)):H(r)))}function Ue(e,t){return ur(e,t).pipe(x(r=>r.json()),J(1))}function mo(e,t){let r=new DOMParser;return ur(e,t).pipe(x(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return I(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(x(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),C(()=>document.head.removeChild(t)),Oe(1))))}function ho(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function bo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(ho),N(ho()))}function vo(){return{width:innerWidth,height:innerHeight}}function go(){return b(window,"resize",{passive:!0}).pipe(l(vo),N(vo()))}function yo(){return Q([bo(),go()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(X("size")),o=Q([n,r]).pipe(l(()=>Be(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:a,size:s},{x:f,y:c}])=>({offset:{x:a.x-f,y:a.y-c+i},size:s})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(a=>{let s=document.createElement("script");s.src=i,s.onload=a,document.body.appendChild(s)})),Promise.resolve())}var r=class{constructor(n){this.url=n,this.onerror=null,this.onmessage=null,this.onmessageerror=null,this.m=a=>{a.source===this.w&&(a.stopImmediatePropagation(),this.dispatchEvent(new MessageEvent("message",{data:a.data})),this.onmessage&&this.onmessage(a))},this.e=(a,s,f,c,u)=>{if(s===this.url.toString()){let p=new ErrorEvent("error",{message:a,filename:s,lineno:f,colno:c,error:u});this.dispatchEvent(p),this.onerror&&this.onerror(p)}};let o=new EventTarget;this.addEventListener=o.addEventListener.bind(o),this.removeEventListener=o.removeEventListener.bind(o),this.dispatchEvent=o.dispatchEvent.bind(o);let i=document.createElement("iframe");i.width=i.height=i.frameBorder="0",document.body.appendChild(this.iframe=i),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Class Reference

+ +

GraphQL\GraphQL

+

This is the primary facade for fulfilling GraphQL operations. +See related documentation.

+

@phpstan-import-type FieldResolver from Executor

+

@see \GraphQL\Tests\GraphQLTest

+

GraphQL\GraphQL Methods

+
/**
+ * Executes graphql query.
+ *
+ * More sophisticated GraphQL servers, such as those which persist queries,
+ * may wish to separate the validation and execution phases to a static time
+ * tooling step, and a server runtime step.
+ *
+ * Available options:
+ *
+ * schema:
+ *    The GraphQL type system to use when validating and executing a query.
+ * source:
+ *    A GraphQL language formatted string representing the requested operation.
+ * rootValue:
+ *    The value provided as the first argument to resolver functions on the top
+ *    level type (e.g. the query object type).
+ * contextValue:
+ *    The context value is provided as an argument to resolver functions after
+ *    field arguments. It is used to pass shared information useful at any point
+ *    during executing this query, for example the currently logged in user and
+ *    connections to databases or other services.
+ *    If the passed object implements the `ScopedContext` interface,
+ *    its `clone()` method will be called before passing the context down to a field.
+ *    This allows passing information to child fields in the query tree without affecting sibling or parent fields.
+ * variableValues:
+ *    A mapping of variable name to runtime value to use for all variables
+ *    defined in the requestString.
+ * operationName:
+ *    The name of the operation to use if requestString contains multiple
+ *    possible operations. Can be omitted if requestString contains only
+ *    one operation.
+ * fieldResolver:
+ *    A resolver function to use when one is not provided by the schema.
+ *    If not provided, the default field resolver is used (which looks for a
+ *    value on the source value with the field's name).
+ * validationRules:
+ *    A set of rules for query validation step. Default value is all available rules.
+ *    Empty array would allow to skip query validation (may be convenient for persisted
+ *    queries which are validated before persisting and assumed valid during execution)
+ *
+ * @param string|DocumentNode $source
+ * @param mixed $rootValue
+ * @param mixed $contextValue
+ * @param array<string, mixed>|null $variableValues
+ * @param array<ValidationRule>|null $validationRules
+ *
+ * @api
+ *
+ * @throws \Exception
+ * @throws InvariantViolation
+ */
+static function executeQuery(
+    GraphQL\Type\Schema $schema,
+    $source,
+    $rootValue = null,
+    $contextValue = null,
+    ?array $variableValues = null,
+    ?string $operationName = null,
+    ?callable $fieldResolver = null,
+    ?array $validationRules = null
+): GraphQL\Executor\ExecutionResult
+
+
/**
+ * Same as executeQuery(), but requires PromiseAdapter and always returns a Promise.
+ * Useful for Async PHP platforms.
+ *
+ * @param string|DocumentNode $source
+ * @param mixed $rootValue
+ * @param mixed $context
+ * @param array<string, mixed>|null $variableValues
+ * @param array<ValidationRule>|null $validationRules Defaults to using all available rules
+ *
+ * @api
+ *
+ * @throws \Exception
+ */
+static function promiseToExecute(
+    GraphQL\Executor\Promise\PromiseAdapter $promiseAdapter,
+    GraphQL\Type\Schema $schema,
+    $source,
+    $rootValue = null,
+    $context = null,
+    ?array $variableValues = null,
+    ?string $operationName = null,
+    ?callable $fieldResolver = null,
+    ?array $validationRules = null
+): GraphQL\Executor\Promise\Promise
+
+
/**
+ * Returns directives defined in GraphQL spec.
+ *
+ * @throws InvariantViolation
+ *
+ * @return array<string, Directive>
+ *
+ * @api
+ */
+static function getStandardDirectives(): array
+
+
/**
+ * Returns types defined in GraphQL spec.
+ *
+ * @throws InvariantViolation
+ *
+ * @return array<string, ScalarType>
+ *
+ * @api
+ */
+static function getStandardTypes(): array
+
+
/**
+ * Replaces standard types with types from this list (matching by name).
+ *
+ * Standard types not listed here remain untouched.
+ *
+ * @param array<string, ScalarType> $types
+ *
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+static function overrideStandardTypes(array $types): void
+
+
/**
+ * Returns standard validation rules implementing GraphQL spec.
+ *
+ * @return array<class-string<ValidationRule>, ValidationRule>
+ *
+ * @api
+ */
+static function getStandardValidationRules(): array
+
+
/**
+ * Set default resolver implementation.
+ *
+ * @phpstan-param FieldResolver $fn
+ *
+ * @api
+ */
+static function setDefaultFieldResolver(callable $fn): void
+
+

GraphQL\Type\Definition\Type

+

Registry of standard GraphQL types and base class for all other types.

+

GraphQL\Type\Definition\Type Methods

+
/**
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+static function int(): GraphQL\Type\Definition\ScalarType
+
+
/**
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+static function float(): GraphQL\Type\Definition\ScalarType
+
+
/**
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+static function string(): GraphQL\Type\Definition\ScalarType
+
+
/**
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+static function boolean(): GraphQL\Type\Definition\ScalarType
+
+
/**
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+static function id(): GraphQL\Type\Definition\ScalarType
+
+
/**
+ * @template T of Type
+ *
+ * @param T|callable():T $type
+ *
+ * @return ListOfType<T>
+ *
+ * @api
+ */
+static function listOf($type): GraphQL\Type\Definition\ListOfType
+
+
/**
+ * @param (NullableType&Type)|callable():(NullableType&Type) $type
+ *
+ * @api
+ */
+static function nonNull($type): GraphQL\Type\Definition\NonNull
+
+
/**
+ * @param mixed $type
+ *
+ * @api
+ */
+static function isInputType($type): bool
+
+
/**
+ * @return (Type&NamedType)|null
+ *
+ * @api
+ */
+static function getNamedType(?GraphQL\Type\Definition\Type $type): ?GraphQL\Type\Definition\Type
+
+
/**
+ * @param mixed $type
+ *
+ * @api
+ */
+static function isOutputType($type): bool
+
+
/**
+ * @param mixed $type
+ *
+ * @api
+ */
+static function isLeafType($type): bool
+
+
/**
+ * @param mixed $type
+ *
+ * @api
+ */
+static function isCompositeType($type): bool
+
+
/**
+ * @param mixed $type
+ *
+ * @api
+ */
+static function isAbstractType($type): bool
+
+
/**
+ * @return Type&NullableType
+ *
+ * @api
+ */
+static function getNullableType(GraphQL\Type\Definition\Type $type): GraphQL\Type\Definition\Type
+
+

GraphQL\Type\Definition\ResolveInfo

+

Structure containing information useful for field resolution process.

+

Passed as 4th argument to every field resolver. See docs on field resolving (data fetching).

+

@phpstan-import-type QueryPlanOptions from QueryPlan

+

@phpstan-type Path list

+

GraphQL\Type\Definition\ResolveInfo Props

+
/**
+ * The definition of the field being resolved.
+ *
+ * @api
+ */
+public $fieldDefinition;
+
+/**
+ * The name of the field being resolved.
+ *
+ * @api
+ */
+public $fieldName;
+
+/**
+ * Expected return type of the field being resolved.
+ *
+ * @api
+ */
+public $returnType;
+
+/**
+ * AST of all nodes referencing this field in the query.
+ *
+ * @api
+ *
+ * @var \ArrayObject<int, FieldNode>
+ */
+public $fieldNodes;
+
+/**
+ * Parent type of the field being resolved.
+ *
+ * @api
+ */
+public $parentType;
+
+/**
+ * Path to this field from the very root value. When fields are aliased, the path includes aliases.
+ *
+ * @api
+ *
+ * @var list<string|int>
+ *
+ * @phpstan-var Path
+ */
+public $path;
+
+/**
+ * Path to this field from the very root value. This will never include aliases.
+ *
+ * @api
+ *
+ * @var list<string|int>
+ *
+ * @phpstan-var Path
+ */
+public $unaliasedPath;
+
+/**
+ * Instance of a schema used for execution.
+ *
+ * @api
+ */
+public $schema;
+
+/**
+ * AST of all fragments defined in query.
+ *
+ * @api
+ *
+ * @var array<string, FragmentDefinitionNode>
+ */
+public $fragments;
+
+/**
+ * Root value passed to query execution.
+ *
+ * @api
+ *
+ * @var mixed
+ */
+public $rootValue;
+
+/**
+ * AST of operation definition node (query, mutation).
+ *
+ * @api
+ */
+public $operation;
+
+/**
+ * Array of variables passed to query execution.
+ *
+ * @api
+ *
+ * @var array<string, mixed>
+ */
+public $variableValues;
+
+

GraphQL\Type\Definition\ResolveInfo Methods

+
/**
+ * Helper method that returns names of all fields selected in query for
+ * $this->fieldName up to $depth levels.
+ *
+ * Example:
+ * query MyQuery{
+ * {
+ *   root {
+ *     id,
+ *     nested {
+ *      nested1
+ *      nested2 {
+ *        nested3
+ *      }
+ *     }
+ *   }
+ * }
+ *
+ * Given this ResolveInfo instance is a part of "root" field resolution, and $depth === 1,
+ * method will return:
+ * [
+ *     'id' => true,
+ *     'nested' => [
+ *         nested1 => true,
+ *         nested2 => true
+ *     ]
+ * ]
+ *
+ * Warning: this method it is a naive implementation which does not take into account
+ * conditional typed fragments. So use it with care for fields of interface and union types.
+ *
+ * @param int $depth How many levels to include in output
+ *
+ * @return array<string, mixed>
+ *
+ * @api
+ */
+function getFieldSelection(int $depth = 0): array
+
+

GraphQL\Language\DirectiveLocation

+

Enumeration of available directive locations.

+

GraphQL\Language\DirectiveLocation Constants

+
const QUERY = 'QUERY';
+const MUTATION = 'MUTATION';
+const SUBSCRIPTION = 'SUBSCRIPTION';
+const FIELD = 'FIELD';
+const FRAGMENT_DEFINITION = 'FRAGMENT_DEFINITION';
+const FRAGMENT_SPREAD = 'FRAGMENT_SPREAD';
+const INLINE_FRAGMENT = 'INLINE_FRAGMENT';
+const VARIABLE_DEFINITION = 'VARIABLE_DEFINITION';
+const EXECUTABLE_LOCATIONS = [
+    'QUERY' => 'QUERY',
+    'MUTATION' => 'MUTATION',
+    'SUBSCRIPTION' => 'SUBSCRIPTION',
+    'FIELD' => 'FIELD',
+    'FRAGMENT_DEFINITION' => 'FRAGMENT_DEFINITION',
+    'FRAGMENT_SPREAD' => 'FRAGMENT_SPREAD',
+    'INLINE_FRAGMENT' => 'INLINE_FRAGMENT',
+    'VARIABLE_DEFINITION' => 'VARIABLE_DEFINITION',
+];
+const SCHEMA = 'SCHEMA';
+const SCALAR = 'SCALAR';
+const OBJECT = 'OBJECT';
+const FIELD_DEFINITION = 'FIELD_DEFINITION';
+const ARGUMENT_DEFINITION = 'ARGUMENT_DEFINITION';
+const IFACE = 'INTERFACE';
+const UNION = 'UNION';
+const ENUM = 'ENUM';
+const ENUM_VALUE = 'ENUM_VALUE';
+const INPUT_OBJECT = 'INPUT_OBJECT';
+const INPUT_FIELD_DEFINITION = 'INPUT_FIELD_DEFINITION';
+const TYPE_SYSTEM_LOCATIONS = [
+    'SCHEMA' => 'SCHEMA',
+    'SCALAR' => 'SCALAR',
+    'OBJECT' => 'OBJECT',
+    'FIELD_DEFINITION' => 'FIELD_DEFINITION',
+    'ARGUMENT_DEFINITION' => 'ARGUMENT_DEFINITION',
+    'INTERFACE' => 'INTERFACE',
+    'UNION' => 'UNION',
+    'ENUM' => 'ENUM',
+    'ENUM_VALUE' => 'ENUM_VALUE',
+    'INPUT_OBJECT' => 'INPUT_OBJECT',
+    'INPUT_FIELD_DEFINITION' => 'INPUT_FIELD_DEFINITION',
+];
+const LOCATIONS = [
+    'QUERY' => 'QUERY',
+    'MUTATION' => 'MUTATION',
+    'SUBSCRIPTION' => 'SUBSCRIPTION',
+    'FIELD' => 'FIELD',
+    'FRAGMENT_DEFINITION' => 'FRAGMENT_DEFINITION',
+    'FRAGMENT_SPREAD' => 'FRAGMENT_SPREAD',
+    'INLINE_FRAGMENT' => 'INLINE_FRAGMENT',
+    'VARIABLE_DEFINITION' => 'VARIABLE_DEFINITION',
+    'SCHEMA' => 'SCHEMA',
+    'SCALAR' => 'SCALAR',
+    'OBJECT' => 'OBJECT',
+    'FIELD_DEFINITION' => 'FIELD_DEFINITION',
+    'ARGUMENT_DEFINITION' => 'ARGUMENT_DEFINITION',
+    'INTERFACE' => 'INTERFACE',
+    'UNION' => 'UNION',
+    'ENUM' => 'ENUM',
+    'ENUM_VALUE' => 'ENUM_VALUE',
+    'INPUT_OBJECT' => 'INPUT_OBJECT',
+    'INPUT_FIELD_DEFINITION' => 'INPUT_FIELD_DEFINITION',
+];
+
+

GraphQL\Type\SchemaConfig

+

Configuration options for schema construction.

+

The options accepted by the create method are described +in the schema definition docs.

+

Usage example:

+
$config = SchemaConfig::create()
+    ->setQuery($myQueryType)
+    ->setTypeLoader($myTypeLoader);
+
+$schema = new Schema($config);
+
+

@see Type, NamedType

+

@phpstan-type MaybeLazyObjectType ObjectType|(callable(): (ObjectType|null))|null +@phpstan-type TypeLoader callable(string $typeName): ((Type&NamedType)|null) +@phpstan-type Types iterable|(callable(): iterable)|iterable<(callable(): Type&NamedType)>|(callable(): iterable<(callable(): Type&NamedType)>) +@phpstan-type SchemaConfigOptions array{ +query?: MaybeLazyObjectType, +mutation?: MaybeLazyObjectType, +subscription?: MaybeLazyObjectType, +types?: Types|null, +directives?: array|null, +typeLoader?: TypeLoader|null, +assumeValid?: bool|null, +astNode?: SchemaDefinitionNode|null, +extensionASTNodes?: array|null, +}

+

GraphQL\Type\SchemaConfig Methods

+
/**
+ * Converts an array of options to instance of SchemaConfig
+ * (or just returns empty config when array is not passed).
+ *
+ * @phpstan-param SchemaConfigOptions $options
+ *
+ * @throws InvariantViolation
+ *
+ * @api
+ */
+static function create(array $options = []): self
+
+
/**
+ * @return MaybeLazyObjectType
+ *
+ * @api
+ */
+function getQuery()
+
+
/**
+ * @param MaybeLazyObjectType $query
+ *
+ * @throws InvariantViolation
+ *
+ * @api
+ */
+function setQuery($query): self
+
+
/**
+ * @return MaybeLazyObjectType
+ *
+ * @api
+ */
+function getMutation()
+
+
/**
+ * @param MaybeLazyObjectType $mutation
+ *
+ * @throws InvariantViolation
+ *
+ * @api
+ */
+function setMutation($mutation): self
+
+
/**
+ * @return MaybeLazyObjectType
+ *
+ * @api
+ */
+function getSubscription()
+
+
/**
+ * @param MaybeLazyObjectType $subscription
+ *
+ * @throws InvariantViolation
+ *
+ * @api
+ */
+function setSubscription($subscription): self
+
+
/**
+ * @return array|callable
+ *
+ * @phpstan-return Types
+ *
+ * @api
+ */
+function getTypes()
+
+
/**
+ * @param array|callable $types
+ *
+ * @phpstan-param Types $types
+ *
+ * @api
+ */
+function setTypes($types): self
+
+
/**
+ * @return array<Directive>|null
+ *
+ * @api
+ */
+function getDirectives(): ?array
+
+
/**
+ * @param array<Directive>|null $directives
+ *
+ * @api
+ */
+function setDirectives(?array $directives): self
+
+
/**
+ * @return callable|null $typeLoader
+ *
+ * @phpstan-return TypeLoader|null $typeLoader
+ *
+ * @api
+ */
+function getTypeLoader(): ?callable
+
+
/**
+ * @phpstan-param TypeLoader|null $typeLoader
+ *
+ * @api
+ */
+function setTypeLoader(?callable $typeLoader): self
+
+

GraphQL\Type\Schema

+

Schema Definition (see schema definition docs).

+

A Schema is created by supplying the root types of each type of operation: +query, mutation (optional) and subscription (optional). A schema definition is +then supplied to the validator and executor. Usage Example:

+
$schema = new GraphQL\Type\Schema([
+  'query' => $MyAppQueryRootType,
+  'mutation' => $MyAppMutationRootType,
+]);
+
+

Or using Schema Config instance:

+
$config = GraphQL\Type\SchemaConfig::create()
+    ->setQuery($MyAppQueryRootType)
+    ->setMutation($MyAppMutationRootType);
+
+$schema = new GraphQL\Type\Schema($config);
+
+

@phpstan-import-type SchemaConfigOptions from SchemaConfig +@phpstan-import-type OperationType from OperationDefinitionNode

+

@see \GraphQL\Tests\Type\SchemaTest

+

GraphQL\Type\Schema Methods

+
/**
+ * @param SchemaConfig|array<string, mixed> $config
+ *
+ * @phpstan-param SchemaConfig|SchemaConfigOptions $config
+ *
+ * @throws InvariantViolation
+ *
+ * @api
+ */
+function __construct($config)
+
+
/**
+ * Returns all types in this schema.
+ *
+ * This operation requires a full schema scan. Do not use in production environment.
+ *
+ * @throws InvariantViolation
+ *
+ * @return array<string, Type&NamedType> Keys represent type names, values are instances of corresponding type definitions
+ *
+ * @api
+ */
+function getTypeMap(): array
+
+
/**
+ * Returns a list of directives supported by this schema.
+ *
+ * @throws InvariantViolation
+ *
+ * @return array<Directive>
+ *
+ * @api
+ */
+function getDirectives(): array
+
+
/**
+ * Returns root query type.
+ *
+ * @api
+ */
+function getQueryType(): ?GraphQL\Type\Definition\ObjectType
+
+
/**
+ * Returns root mutation type.
+ *
+ * @api
+ */
+function getMutationType(): ?GraphQL\Type\Definition\ObjectType
+
+
/**
+ * Returns schema subscription.
+ *
+ * @api
+ */
+function getSubscriptionType(): ?GraphQL\Type\Definition\ObjectType
+
+
/**
+ * Returns a type by name.
+ *
+ * @throws InvariantViolation
+ *
+ * @return (Type&NamedType)|null
+ *
+ * @api
+ */
+function getType(string $name): ?GraphQL\Type\Definition\Type
+
+
/**
+ * Returns all possible concrete types for given abstract type
+ * (implementations for interfaces and members of union type for unions).
+ *
+ * This operation requires full schema scan. Do not use in production environment.
+ *
+ * @param AbstractType&Type $abstractType
+ *
+ * @throws InvariantViolation
+ *
+ * @return array<ObjectType>
+ *
+ * @api
+ */
+function getPossibleTypes(GraphQL\Type\Definition\AbstractType $abstractType): array
+
+
/**
+ * Returns all types that implement a given interface type.
+ *
+ * This operation requires full schema scan. Do not use in production environment.
+ *
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+function getImplementations(GraphQL\Type\Definition\InterfaceType $abstractType): GraphQL\Utils\InterfaceImplementations
+
+
/**
+ * Returns true if the given type is a sub type of the given abstract type.
+ *
+ * @param AbstractType&Type $abstractType
+ * @param ImplementingType&Type $maybeSubType
+ *
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+function isSubType(
+    GraphQL\Type\Definition\AbstractType $abstractType,
+    GraphQL\Type\Definition\ImplementingType $maybeSubType
+): bool
+
+
/**
+ * Returns instance of directive by name.
+ *
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+function getDirective(string $name): ?GraphQL\Type\Definition\Directive
+
+
/**
+ * Throws if the schema is not valid.
+ *
+ * This operation requires a full schema scan. Do not use in production environment.
+ *
+ * @throws Error
+ * @throws InvariantViolation
+ *
+ * @api
+ */
+function assertValid(): void
+
+
/**
+ * Validate the schema and return any errors.
+ *
+ * This operation requires a full schema scan. Do not use in production environment.
+ *
+ * @throws InvariantViolation
+ *
+ * @return array<int, Error>
+ *
+ * @api
+ */
+function validate(): array
+
+

GraphQL\Language\Parser

+

Parses string containing GraphQL query language or schema definition language to Abstract Syntax Tree.

+

@phpstan-type ParserOptions array{ +noLocation?: bool, +allowLegacySDLEmptyFields?: bool, +allowLegacySDLImplementsInterfaces?: bool, +experimentalFragmentVariables?: bool +}

+

noLocation: +(By default, the parser creates AST nodes that know the location +in the source that they correspond to. This configuration flag +disables that behavior for performance or testing.)

+

allowLegacySDLEmptyFields: +If enabled, the parser will parse empty fields sets in the Schema +Definition Language. Otherwise, the parser will follow the current +specification.

+

This option is provided to ease adoption of the final SDL specification +and will be removed in a future major release.

+

allowLegacySDLImplementsInterfaces: +If enabled, the parser will parse implemented interfaces with no & +character between each interface. Otherwise, the parser will follow the +current specification.

+

This option is provided to ease adoption of the final SDL specification +and will be removed in a future major release.

+

experimentalFragmentVariables: +(If enabled, the parser will understand and parse variable definitions +contained in a fragment definition. They'll be represented in the +variableDefinitions field of the FragmentDefinitionNode.

+

The syntax is identical to normal, query-defined variables. For example:

+
fragment A($var: Boolean = false) on T  {
+  ...
+}
+
+

Note: this feature is experimental and may change or be removed in the +future.) +Those magic functions allow partial parsing:

+

@method static NameNode name(Source|string $source, bool[] $options = []) +@method static ExecutableDefinitionNode|TypeSystemDefinitionNode definition(Source|string $source, bool[] $options = []) +@method static ExecutableDefinitionNode executableDefinition(Source|string $source, bool[] $options = []) +@method static OperationDefinitionNode operationDefinition(Source|string $source, bool[] $options = []) +@method static string operationType(Source|string $source, bool[] $options = []) +@method static NodeList variableDefinitions(Source|string $source, bool[] $options = []) +@method static VariableDefinitionNode variableDefinition(Source|string $source, bool[] $options = []) +@method static VariableNode variable(Source|string $source, bool[] $options = []) +@method static SelectionSetNode selectionSet(Source|string $source, bool[] $options = []) +@method static mixed selection(Source|string $source, bool[] $options = []) +@method static FieldNode field(Source|string $source, bool[] $options = []) +@method static NodeList arguments(Source|string $source, bool[] $options = []) +@method static NodeList constArguments(Source|string $source, bool[] $options = []) +@method static ArgumentNode argument(Source|string $source, bool[] $options = []) +@method static ArgumentNode constArgument(Source|string $source, bool[] $options = []) +@method static FragmentSpreadNode|InlineFragmentNode fragment(Source|string $source, bool[] $options = []) +@method static FragmentDefinitionNode fragmentDefinition(Source|string $source, bool[] $options = []) +@method static NameNode fragmentName(Source|string $source, bool[] $options = []) +@method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode|VariableNode valueLiteral(Source|string $source, bool[] $options = []) +@method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode constValueLiteral(Source|string $source, bool[] $options = []) +@method static StringValueNode stringLiteral(Source|string $source, bool[] $options = []) +@method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|StringValueNode constValue(Source|string $source, bool[] $options = []) +@method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|ObjectValueNode|StringValueNode|VariableNode variableValue(Source|string $source, bool[] $options = []) +@method static ListValueNode array(Source|string $source, bool[] $options = []) +@method static ListValueNode constArray(Source|string $source, bool[] $options = []) +@method static ObjectValueNode object(Source|string $source, bool[] $options = []) +@method static ObjectValueNode constObject(Source|string $source, bool[] $options = []) +@method static ObjectFieldNode objectField(Source|string $source, bool[] $options = []) +@method static ObjectFieldNode constObjectField(Source|string $source, bool[] $options = []) +@method static NodeList directives(Source|string $source, bool[] $options = []) +@method static NodeList constDirectives(Source|string $source, bool[] $options = []) +@method static DirectiveNode directive(Source|string $source, bool[] $options = []) +@method static DirectiveNode constDirective(Source|string $source, bool[] $options = []) +@method static ListTypeNode|NamedTypeNode|NonNullTypeNode typeReference(Source|string $source, bool[] $options = []) +@method static NamedTypeNode namedType(Source|string $source, bool[] $options = []) +@method static TypeSystemDefinitionNode typeSystemDefinition(Source|string $source, bool[] $options = []) +@method static StringValueNode|null description(Source|string $source, bool[] $options = []) +@method static SchemaDefinitionNode schemaDefinition(Source|string $source, bool[] $options = []) +@method static OperationTypeDefinitionNode operationTypeDefinition(Source|string $source, bool[] $options = []) +@method static ScalarTypeDefinitionNode scalarTypeDefinition(Source|string $source, bool[] $options = []) +@method static ObjectTypeDefinitionNode objectTypeDefinition(Source|string $source, bool[] $options = []) +@method static NodeList implementsInterfaces(Source|string $source, bool[] $options = []) +@method static NodeList fieldsDefinition(Source|string $source, bool[] $options = []) +@method static FieldDefinitionNode fieldDefinition(Source|string $source, bool[] $options = []) +@method static NodeList argumentsDefinition(Source|string $source, bool[] $options = []) +@method static InputValueDefinitionNode inputValueDefinition(Source|string $source, bool[] $options = []) +@method static InterfaceTypeDefinitionNode interfaceTypeDefinition(Source|string $source, bool[] $options = []) +@method static UnionTypeDefinitionNode unionTypeDefinition(Source|string $source, bool[] $options = []) +@method static NodeList unionMemberTypes(Source|string $source, bool[] $options = []) +@method static EnumTypeDefinitionNode enumTypeDefinition(Source|string $source, bool[] $options = []) +@method static NodeList enumValuesDefinition(Source|string $source, bool[] $options = []) +@method static EnumValueDefinitionNode enumValueDefinition(Source|string $source, bool[] $options = []) +@method static InputObjectTypeDefinitionNode inputObjectTypeDefinition(Source|string $source, bool[] $options = []) +@method static NodeList inputFieldsDefinition(Source|string $source, bool[] $options = []) +@method static TypeExtensionNode typeExtension(Source|string $source, bool[] $options = []) +@method static SchemaExtensionNode schemaTypeExtension(Source|string $source, bool[] $options = []) +@method static ScalarTypeExtensionNode scalarTypeExtension(Source|string $source, bool[] $options = []) +@method static ObjectTypeExtensionNode objectTypeExtension(Source|string $source, bool[] $options = []) +@method static InterfaceTypeExtensionNode interfaceTypeExtension(Source|string $source, bool[] $options = []) +@method static UnionTypeExtensionNode unionTypeExtension(Source|string $source, bool[] $options = []) +@method static EnumTypeExtensionNode enumTypeExtension(Source|string $source, bool[] $options = []) +@method static InputObjectTypeExtensionNode inputObjectTypeExtension(Source|string $source, bool[] $options = []) +@method static DirectiveDefinitionNode directiveDefinition(Source|string $source, bool[] $options = []) +@method static NodeList directiveLocations(Source|string $source, bool[] $options = []) +@method static NameNode directiveLocation(Source|string $source, bool[] $options = [])

+

@see \GraphQL\Tests\Language\ParserTest

+

GraphQL\Language\Parser Methods

+
/**
+ * Given a GraphQL source, parses it into a `GraphQL\Language\AST\DocumentNode`.
+ *
+ * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
+ *
+ * @param Source|string $source
+ *
+ * @phpstan-param ParserOptions $options
+ *
+ * @api
+ *
+ * @throws \JsonException
+ * @throws SyntaxError
+ */
+static function parse($source, array $options = []): GraphQL\Language\AST\DocumentNode
+
+
/**
+ * Given a string containing a GraphQL value (ex. `[42]`), parse the AST for that value.
+ *
+ * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
+ *
+ * This is useful within tools that operate upon GraphQL Values directly and
+ * in isolation of complete GraphQL documents.
+ *
+ * Consider providing the results to the utility function: `GraphQL\Utils\AST::valueFromAST()`.
+ *
+ * @param Source|string $source
+ *
+ * @phpstan-param ParserOptions $options
+ *
+ * @throws \JsonException
+ * @throws SyntaxError
+ *
+ * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode|VariableNode
+ *
+ * @api
+ */
+static function parseValue($source, array $options = [])
+
+
/**
+ * Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for that type.
+ *
+ * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
+ *
+ * This is useful within tools that operate upon GraphQL Types directly and
+ * in isolation of complete GraphQL documents.
+ *
+ * Consider providing the results to the utility function: `GraphQL\Utils\AST::typeFromAST()`.
+ *
+ * @param Source|string $source
+ *
+ * @phpstan-param ParserOptions $options
+ *
+ * @throws \JsonException
+ * @throws SyntaxError
+ *
+ * @return ListTypeNode|NamedTypeNode|NonNullTypeNode
+ *
+ * @api
+ */
+static function parseType($source, array $options = [])
+
+

GraphQL\Language\Printer

+

Prints AST to string. Capable of printing GraphQL queries and Type definition language. +Useful for pretty-printing queries or printing back AST for logging, documentation, etc.

+

Usage example:

+
$query = 'query myQuery {someField}';
+$ast = GraphQL\Language\Parser::parse($query);
+$printed = GraphQL\Language\Printer::doPrint($ast);
+
+

@see \GraphQL\Tests\Language\PrinterTest

+

GraphQL\Language\Printer Methods

+
/**
+ * Converts the AST of a GraphQL node to a string.
+ *
+ * Handles both executable definitions and schema definitions.
+ *
+ * @api
+ */
+static function doPrint(GraphQL\Language\AST\Node $ast): string
+
+

GraphQL\Language\Visitor

+

Utility for efficient AST traversal and modification.

+

visit() will walk through an AST using a depth first traversal, calling +the visitor's enter function at each node in the traversal, and calling the +leave function after visiting that node and all of its child nodes.

+

By returning different values from the enter and leave functions, the +behavior of the visitor can be altered.

+
    +
  • no return (void) or return null: no action
  • +
  • Visitor::skipNode(): skips over the subtree at the current node of the AST
  • +
  • Visitor::stop(): stop the Visitor completely
  • +
  • Visitor::removeNode(): remove the current node
  • +
  • return any other value: replace this node with the returned value
  • +
+

When using visit() to edit an AST, the original AST will not be modified, and +a new version of the AST with the changes applied will be returned from the +visit function.

+

$editedAST = Visitor::visit($ast, [ +'enter' => function ($node, $key, $parent, $path, $ancestors) { +// ... +}, +'leave' => function ($node, $key, $parent, $path, $ancestors) { +// ... +} +]);

+

Alternatively to providing enter and leave functions, a visitor can +instead provide functions named the same as the kinds of AST nodes, +or enter/leave visitors at a named key, leading to four permutations of +visitor API:

+
    +
  1. Named visitors triggered when entering a node a specific kind.
  2. +
+

Visitor::visit($ast, [ + 'Kind' => function ($node) { + // enter the "Kind" node + } + ]);

+
    +
  1. Named visitors that trigger upon entering and leaving a node of + a specific kind.
  2. +
+

Visitor::visit($ast, [ + 'Kind' => [ + 'enter' => function ($node) { + // enter the "Kind" node + } + 'leave' => function ($node) { + // leave the "Kind" node + } + ] + ]);

+
    +
  1. Generic visitors that trigger upon entering and leaving any node.
  2. +
+

Visitor::visit($ast, [ + 'enter' => function ($node) { + // enter any node + }, + 'leave' => function ($node) { + // leave any node + } + ]);

+
    +
  1. Parallel visitors for entering and leaving nodes of a specific kind.
  2. +
+

Visitor::visit($ast, [ + 'enter' => [ + 'Kind' => function($node) { + // enter the "Kind" node + } + }, + 'leave' => [ + 'Kind' => function ($node) { + // leave the "Kind" node + } + ] + ]);

+

@phpstan-type NodeVisitor callable(Node): (VisitorOperation|null|false|void) +@phpstan-type VisitorArray array|array>

+

@see \GraphQL\Tests\Language\VisitorTest

+

GraphQL\Language\Visitor Methods

+
/**
+ * Visit the AST (see class description for details).
+ *
+ * @param NodeList<Node>|Node $root
+ * @param VisitorArray $visitor
+ * @param array<string, mixed>|null $keyMap
+ *
+ * @throws \Exception
+ *
+ * @return mixed
+ *
+ * @api
+ */
+static function visit(object $root, array $visitor, ?array $keyMap = null)
+
+
/**
+ * Returns marker for stopping.
+ *
+ * @api
+ */
+static function stop(): GraphQL\Language\VisitorStop
+
+
/**
+ * Returns marker for skipping the subtree at the current node.
+ *
+ * @api
+ */
+static function skipNode(): GraphQL\Language\VisitorSkipNode
+
+
/**
+ * Returns marker for removing the current node.
+ *
+ * @api
+ */
+static function removeNode(): GraphQL\Language\VisitorRemoveNode
+
+

GraphQL\Language\AST\NodeKind

+

Holds constants of possible AST nodes.

+

GraphQL\Language\AST\NodeKind Constants

+
const NAME = 'Name';
+const DOCUMENT = 'Document';
+const OPERATION_DEFINITION = 'OperationDefinition';
+const VARIABLE_DEFINITION = 'VariableDefinition';
+const VARIABLE = 'Variable';
+const SELECTION_SET = 'SelectionSet';
+const FIELD = 'Field';
+const ARGUMENT = 'Argument';
+const FRAGMENT_SPREAD = 'FragmentSpread';
+const INLINE_FRAGMENT = 'InlineFragment';
+const FRAGMENT_DEFINITION = 'FragmentDefinition';
+const INT = 'IntValue';
+const FLOAT = 'FloatValue';
+const STRING = 'StringValue';
+const BOOLEAN = 'BooleanValue';
+const ENUM = 'EnumValue';
+const NULL = 'NullValue';
+const LST = 'ListValue';
+const OBJECT = 'ObjectValue';
+const OBJECT_FIELD = 'ObjectField';
+const DIRECTIVE = 'Directive';
+const NAMED_TYPE = 'NamedType';
+const LIST_TYPE = 'ListType';
+const NON_NULL_TYPE = 'NonNullType';
+const SCHEMA_DEFINITION = 'SchemaDefinition';
+const OPERATION_TYPE_DEFINITION = 'OperationTypeDefinition';
+const SCALAR_TYPE_DEFINITION = 'ScalarTypeDefinition';
+const OBJECT_TYPE_DEFINITION = 'ObjectTypeDefinition';
+const FIELD_DEFINITION = 'FieldDefinition';
+const INPUT_VALUE_DEFINITION = 'InputValueDefinition';
+const INTERFACE_TYPE_DEFINITION = 'InterfaceTypeDefinition';
+const UNION_TYPE_DEFINITION = 'UnionTypeDefinition';
+const ENUM_TYPE_DEFINITION = 'EnumTypeDefinition';
+const ENUM_VALUE_DEFINITION = 'EnumValueDefinition';
+const INPUT_OBJECT_TYPE_DEFINITION = 'InputObjectTypeDefinition';
+const SCALAR_TYPE_EXTENSION = 'ScalarTypeExtension';
+const OBJECT_TYPE_EXTENSION = 'ObjectTypeExtension';
+const INTERFACE_TYPE_EXTENSION = 'InterfaceTypeExtension';
+const UNION_TYPE_EXTENSION = 'UnionTypeExtension';
+const ENUM_TYPE_EXTENSION = 'EnumTypeExtension';
+const INPUT_OBJECT_TYPE_EXTENSION = 'InputObjectTypeExtension';
+const DIRECTIVE_DEFINITION = 'DirectiveDefinition';
+const SCHEMA_EXTENSION = 'SchemaExtension';
+const CLASS_MAP = [
+    'Name' => 'GraphQL\\Language\\AST\\NameNode',
+    'Document' => 'GraphQL\\Language\\AST\\DocumentNode',
+    'OperationDefinition' => 'GraphQL\\Language\\AST\\OperationDefinitionNode',
+    'VariableDefinition' => 'GraphQL\\Language\\AST\\VariableDefinitionNode',
+    'Variable' => 'GraphQL\\Language\\AST\\VariableNode',
+    'SelectionSet' => 'GraphQL\\Language\\AST\\SelectionSetNode',
+    'Field' => 'GraphQL\\Language\\AST\\FieldNode',
+    'Argument' => 'GraphQL\\Language\\AST\\ArgumentNode',
+    'FragmentSpread' => 'GraphQL\\Language\\AST\\FragmentSpreadNode',
+    'InlineFragment' => 'GraphQL\\Language\\AST\\InlineFragmentNode',
+    'FragmentDefinition' => 'GraphQL\\Language\\AST\\FragmentDefinitionNode',
+    'IntValue' => 'GraphQL\\Language\\AST\\IntValueNode',
+    'FloatValue' => 'GraphQL\\Language\\AST\\FloatValueNode',
+    'StringValue' => 'GraphQL\\Language\\AST\\StringValueNode',
+    'BooleanValue' => 'GraphQL\\Language\\AST\\BooleanValueNode',
+    'EnumValue' => 'GraphQL\\Language\\AST\\EnumValueNode',
+    'NullValue' => 'GraphQL\\Language\\AST\\NullValueNode',
+    'ListValue' => 'GraphQL\\Language\\AST\\ListValueNode',
+    'ObjectValue' => 'GraphQL\\Language\\AST\\ObjectValueNode',
+    'ObjectField' => 'GraphQL\\Language\\AST\\ObjectFieldNode',
+    'Directive' => 'GraphQL\\Language\\AST\\DirectiveNode',
+    'NamedType' => 'GraphQL\\Language\\AST\\NamedTypeNode',
+    'ListType' => 'GraphQL\\Language\\AST\\ListTypeNode',
+    'NonNullType' => 'GraphQL\\Language\\AST\\NonNullTypeNode',
+    'SchemaDefinition' => 'GraphQL\\Language\\AST\\SchemaDefinitionNode',
+    'OperationTypeDefinition' => 'GraphQL\\Language\\AST\\OperationTypeDefinitionNode',
+    'ScalarTypeDefinition' => 'GraphQL\\Language\\AST\\ScalarTypeDefinitionNode',
+    'ObjectTypeDefinition' => 'GraphQL\\Language\\AST\\ObjectTypeDefinitionNode',
+    'FieldDefinition' => 'GraphQL\\Language\\AST\\FieldDefinitionNode',
+    'InputValueDefinition' => 'GraphQL\\Language\\AST\\InputValueDefinitionNode',
+    'InterfaceTypeDefinition' => 'GraphQL\\Language\\AST\\InterfaceTypeDefinitionNode',
+    'UnionTypeDefinition' => 'GraphQL\\Language\\AST\\UnionTypeDefinitionNode',
+    'EnumTypeDefinition' => 'GraphQL\\Language\\AST\\EnumTypeDefinitionNode',
+    'EnumValueDefinition' => 'GraphQL\\Language\\AST\\EnumValueDefinitionNode',
+    'InputObjectTypeDefinition' => 'GraphQL\\Language\\AST\\InputObjectTypeDefinitionNode',
+    'ScalarTypeExtension' => 'GraphQL\\Language\\AST\\ScalarTypeExtensionNode',
+    'ObjectTypeExtension' => 'GraphQL\\Language\\AST\\ObjectTypeExtensionNode',
+    'InterfaceTypeExtension' => 'GraphQL\\Language\\AST\\InterfaceTypeExtensionNode',
+    'UnionTypeExtension' => 'GraphQL\\Language\\AST\\UnionTypeExtensionNode',
+    'EnumTypeExtension' => 'GraphQL\\Language\\AST\\EnumTypeExtensionNode',
+    'InputObjectTypeExtension' => 'GraphQL\\Language\\AST\\InputObjectTypeExtensionNode',
+    'DirectiveDefinition' => 'GraphQL\\Language\\AST\\DirectiveDefinitionNode',
+];
+
+

GraphQL\Executor\Executor

+

Implements the "Evaluating requests" section of the GraphQL specification.

+

@phpstan-type FieldResolver callable(mixed, array, mixed, ResolveInfo): mixed +@phpstan-type ImplementationFactory callable(PromiseAdapter, Schema, DocumentNode, mixed, mixed, array, ?string, callable): ExecutorImplementation

+

@see \GraphQL\Tests\Executor\ExecutorTest

+

GraphQL\Executor\Executor Methods

+
/**
+ * Executes DocumentNode against given $schema.
+ *
+ * Always returns ExecutionResult and never throws.
+ * All errors which occur during operation execution are collected in `$result->errors`.
+ *
+ * @param mixed $rootValue
+ * @param mixed $contextValue
+ * @param array<string, mixed>|null $variableValues
+ *
+ * @phpstan-param FieldResolver|null $fieldResolver
+ *
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+static function execute(
+    GraphQL\Type\Schema $schema,
+    GraphQL\Language\AST\DocumentNode $documentNode,
+    $rootValue = null,
+    $contextValue = null,
+    ?array $variableValues = null,
+    ?string $operationName = null,
+    ?callable $fieldResolver = null
+): GraphQL\Executor\ExecutionResult
+
+
/**
+ * Same as execute(), but requires promise adapter and returns a promise which is always
+ * fulfilled with an instance of ExecutionResult and never rejected.
+ *
+ * Useful for async PHP platforms.
+ *
+ * @param mixed $rootValue
+ * @param mixed $contextValue
+ * @param array<string, mixed>|null $variableValues
+ *
+ * @phpstan-param FieldResolver|null $fieldResolver
+ *
+ * @api
+ */
+static function promiseToExecute(
+    GraphQL\Executor\Promise\PromiseAdapter $promiseAdapter,
+    GraphQL\Type\Schema $schema,
+    GraphQL\Language\AST\DocumentNode $documentNode,
+    $rootValue = null,
+    $contextValue = null,
+    ?array $variableValues = null,
+    ?string $operationName = null,
+    ?callable $fieldResolver = null
+): GraphQL\Executor\Promise\Promise
+
+

GraphQL\Executor\ScopedContext

+

When the object passed as $contextValue to GraphQL execution implements this, +its clone() method will be called before passing the context down to a field. +This allows passing information to child fields in the query tree without affecting sibling or parent fields.

+

GraphQL\Executor\ExecutionResult

+

Returned after query execution. +Represents both - result of successful execution and of a failed one +(with errors collected in errors prop).

+

Could be converted to spec-compliant +serializable array using toArray().

+

@phpstan-type SerializableError array{ +message: string, +locations?: array, +path?: array, +extensions?: array +} +@phpstan-type SerializableErrors array +@phpstan-type SerializableResult array{ +data?: array, +errors?: SerializableErrors, +extensions?: array +} +@phpstan-type ErrorFormatter callable(\Throwable): SerializableError +@phpstan-type ErrorsHandler callable(array $errors, ErrorFormatter $formatter): SerializableErrors

+

@see \GraphQL\Tests\Executor\ExecutionResultTest

+

GraphQL\Executor\ExecutionResult Props

+
/**
+ * Data collected from resolvers during query execution.
+ *
+ * @api
+ *
+ * @var array<string, mixed>|null
+ */
+public $data;
+
+/**
+ * Errors registered during query execution.
+ *
+ * If an error was caused by exception thrown in resolver, $error->getPrevious() would
+ * contain original exception.
+ *
+ * @api
+ *
+ * @var array<Error>
+ */
+public $errors;
+
+/**
+ * User-defined serializable array of extensions included in serialized result.
+ *
+ * @api
+ *
+ * @var array<string, mixed>|null
+ */
+public $extensions;
+
+

GraphQL\Executor\ExecutionResult Methods

+
/**
+ * Define custom error formatting (must conform to http://facebook.github.io/graphql/#sec-Errors).
+ *
+ * Expected signature is: function (GraphQL\Error\Error $error): array
+ *
+ * Default formatter is "GraphQL\Error\FormattedError::createFromException"
+ *
+ * Expected returned value must be an array:
+ * array(
+ *    'message' => 'errorMessage',
+ *    // ... other keys
+ * );
+ *
+ * @phpstan-param ErrorFormatter|null $errorFormatter
+ *
+ * @api
+ */
+function setErrorFormatter(?callable $errorFormatter): self
+
+
/**
+ * Define custom logic for error handling (filtering, logging, etc).
+ *
+ * Expected handler signature is:
+ * fn (array $errors, callable $formatter): array
+ *
+ * Default handler is:
+ * fn (array $errors, callable $formatter): array => array_map($formatter, $errors)
+ *
+ * @phpstan-param ErrorsHandler|null $errorsHandler
+ *
+ * @api
+ */
+function setErrorsHandler(?callable $errorsHandler): self
+
+
/**
+ * Converts GraphQL query result to spec-compliant serializable array using provided
+ * errors handler and formatter.
+ *
+ * If debug argument is passed, output of error formatter is enriched which debugging information
+ * ("debugMessage", "trace" keys depending on flags).
+ *
+ * $debug argument must sum of flags from @see \GraphQL\Error\DebugFlag
+ *
+ * @phpstan-return SerializableResult
+ *
+ * @api
+ */
+function toArray(int $debug = 'GraphQL\\Error\\DebugFlag::NONE'): array
+
+

GraphQL\Executor\Promise\PromiseAdapter

+

Provides a means for integration of async PHP platforms (related docs).

+

GraphQL\Executor\Promise\PromiseAdapter Methods

+
/**
+ * Is the value a promise or a deferred of the underlying platform?
+ *
+ * @param mixed $value
+ *
+ * @api
+ */
+function isThenable($value): bool
+
+
/**
+ * Converts thenable of the underlying platform into GraphQL\Executor\Promise\Promise instance.
+ *
+ * @param mixed $thenable
+ *
+ * @api
+ */
+function convertThenable($thenable): GraphQL\Executor\Promise\Promise
+
+
/**
+ * Accepts our Promise wrapper, extracts adopted promise out of it and executes actual `then` logic described
+ * in Promises/A+ specs. Then returns new wrapped instance of GraphQL\Executor\Promise\Promise.
+ *
+ * @api
+ */
+function then(
+    GraphQL\Executor\Promise\Promise $promise,
+    ?callable $onFulfilled = null,
+    ?callable $onRejected = null
+): GraphQL\Executor\Promise\Promise
+
+
/**
+ * Creates a Promise from the given resolver callable.
+ *
+ * @param callable(callable $resolve, callable $reject): void $resolver
+ *
+ * @api
+ */
+function create(callable $resolver): GraphQL\Executor\Promise\Promise
+
+
/**
+ * Creates a fulfilled Promise for a value if the value is not a promise.
+ *
+ * @param mixed $value
+ *
+ * @api
+ */
+function createFulfilled($value = null): GraphQL\Executor\Promise\Promise
+
+
/**
+ * Creates a rejected promise for a reason if the reason is not a promise.
+ *
+ * If the provided reason is a promise, then it is returned as-is.
+ *
+ * @api
+ */
+function createRejected(Throwable $reason): GraphQL\Executor\Promise\Promise
+
+
/**
+ * Given an iterable of promises (or values), returns a promise that is fulfilled when all the
+ * items in the iterable are fulfilled.
+ *
+ * @param iterable<Promise|mixed> $promisesOrValues
+ *
+ * @api
+ */
+function all(iterable $promisesOrValues): GraphQL\Executor\Promise\Promise
+
+

GraphQL\Validator\DocumentValidator

+

Implements the "Validation" section of the spec.

+

Validation runs synchronously, returning an array of encountered errors, or +an empty array if no errors were encountered and the document is valid.

+

A list of specific validation rules may be provided. If not provided, the +default list of rules defined by the GraphQL specification will be used.

+

Each validation rule is an instance of GraphQL\Validator\Rules\ValidationRule +which returns a visitor (see the GraphQL\Language\Visitor API).

+

Visitor methods are expected to return an instance of GraphQL\Error\Error, +or array of such instances when invalid.

+

Optionally a custom TypeInfo instance may be provided. If not provided, one +will be created from the provided schema.

+

GraphQL\Validator\DocumentValidator Methods

+
/**
+ * Validate a GraphQL query against a schema.
+ *
+ * @param array<ValidationRule>|null $rules Defaults to using all available rules
+ *
+ * @throws \Exception
+ *
+ * @return array<int, Error>
+ *
+ * @api
+ */
+static function validate(
+    GraphQL\Type\Schema $schema,
+    GraphQL\Language\AST\DocumentNode $ast,
+    ?array $rules = null,
+    ?GraphQL\Utils\TypeInfo $typeInfo = null
+): array
+
+
/**
+ * Returns all global validation rules.
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return array<string, ValidationRule>
+ *
+ * @api
+ */
+static function allRules(): array
+
+
/**
+ * Returns global validation rule by name.
+ *
+ * Standard rules are named by class name, so example usage for such rules:
+ *
+ * @example DocumentValidator::getRule(GraphQL\Validator\Rules\QueryComplexity::class);
+ *
+ * @api
+ *
+ * @throws \InvalidArgumentException
+ */
+static function getRule(string $name): ?GraphQL\Validator\Rules\ValidationRule
+
+
/**
+ * Add rule to list of global validation rules.
+ *
+ * @api
+ */
+static function addRule(GraphQL\Validator\Rules\ValidationRule $rule): void
+
+
/**
+ * Remove rule from list of global validation rules.
+ *
+ * @api
+ */
+static function removeRule(GraphQL\Validator\Rules\ValidationRule $rule): void
+
+

GraphQL\Error\Error

+

Describes an Error found during the parse, validate, or +execute phases of performing a GraphQL operation. In addition to a message +and stack trace, it also includes information about the locations in a +GraphQL document and/or execution result that correspond to the Error.

+

When the error was caused by an exception thrown in resolver, original exception +is available via getPrevious().

+

Also read related docs on error handling

+

Class extends standard PHP \Exception, so all standard methods of base \Exception class +are available in addition to those listed below.

+

@see \GraphQL\Tests\Error\ErrorTest

+

GraphQL\Error\Error Methods

+
/**
+ * An array of locations within the source GraphQL document which correspond to this error.
+ *
+ * Each entry has information about `line` and `column` within source GraphQL document:
+ * $location->line;
+ * $location->column;
+ *
+ * Errors during validation often contain multiple locations, for example to
+ * point out to field mentioned in multiple fragments. Errors during execution include a
+ * single location, the field which produced the error.
+ *
+ * @return array<int, SourceLocation>
+ *
+ * @api
+ */
+function getLocations(): array
+
+
/**
+ * Returns an array describing the path from the root value to the field which produced this error.
+ * Only included for execution errors. When fields are aliased, the path includes aliases.
+ *
+ * @return list<int|string>|null
+ *
+ * @api
+ */
+function getPath(): ?array
+
+
/**
+ * Returns an array describing the path from the root value to the field which produced this error.
+ * Only included for execution errors. This will never include aliases.
+ *
+ * @return list<int|string>|null
+ *
+ * @api
+ */
+function getUnaliasedPath(): ?array
+
+

GraphQL\Error\Warning

+

Encapsulates warnings produced by the library.

+

Warnings can be suppressed (individually or all) if required. +Also, it is possible to override warning handler (which is trigger_error() by default).

+

@phpstan-type WarningHandler callable(string $errorMessage, int $warningId, ?int $messageLevel): void

+

GraphQL\Error\Warning Constants

+
const NONE = 0;
+const WARNING_ASSIGN = 2;
+const WARNING_CONFIG = 4;
+const WARNING_FULL_SCHEMA_SCAN = 8;
+const WARNING_CONFIG_DEPRECATION = 16;
+const WARNING_NOT_A_TYPE = 32;
+const ALL = 63;
+
+

GraphQL\Error\Warning Methods

+
/**
+ * Sets warning handler which can intercept all system warnings.
+ * When not set, trigger_error() is used to notify about warnings.
+ *
+ * @phpstan-param WarningHandler|null $warningHandler
+ *
+ * @api
+ */
+static function setWarningHandler(?callable $warningHandler = null): void
+
+
/**
+ * Suppress warning by id (has no effect when custom warning handler is set).
+ *
+ * @param bool|int $suppress
+ *
+ * @example Warning::suppress(Warning::WARNING_NOT_A_TYPE) suppress a specific warning
+ * @example Warning::suppress(true) suppresses all warnings
+ * @example Warning::suppress(false) enables all warnings
+ *
+ * @api
+ */
+static function suppress($suppress = true): void
+
+
/**
+ * Re-enable previously suppressed warning by id (has no effect when custom warning handler is set).
+ *
+ * @param bool|int $enable
+ *
+ * @example Warning::suppress(Warning::WARNING_NOT_A_TYPE) re-enables a specific warning
+ * @example Warning::suppress(true) re-enables all warnings
+ * @example Warning::suppress(false) suppresses all warnings
+ *
+ * @api
+ */
+static function enable($enable = true): void
+
+

GraphQL\Error\ClientAware

+

Implementing ClientAware allows graphql-php to decide if this error is safe to be shown to clients.

+

Only errors that both implement this interface and return true from isClientSafe() +will retain their original error message during formatting.

+

All other errors will have their message replaced with "Internal server error".

+

GraphQL\Error\ClientAware Methods

+
/**
+ * Is it safe to show the error message to clients?
+ *
+ * @api
+ */
+function isClientSafe(): bool
+
+

GraphQL\Error\DebugFlag

+

Collection of flags for error debugging.

+

GraphQL\Error\DebugFlag Constants

+
const NONE = 0;
+const INCLUDE_DEBUG_MESSAGE = 1;
+const INCLUDE_TRACE = 2;
+const RETHROW_INTERNAL_EXCEPTIONS = 4;
+const RETHROW_UNSAFE_EXCEPTIONS = 8;
+
+

GraphQL\Error\FormattedError

+

This class is used for default error formatting. +It converts PHP exceptions to spec-compliant errors +and provides tools for error debugging.

+

@see ExecutionResult

+

@phpstan-import-type SerializableError from ExecutionResult +@phpstan-import-type ErrorFormatter from ExecutionResult

+

@see \GraphQL\Tests\Error\FormattedErrorTest

+

GraphQL\Error\FormattedError Methods

+
/**
+ * Set default error message for internal errors formatted using createFormattedError().
+ * This value can be overridden by passing 3rd argument to `createFormattedError()`.
+ *
+ * @api
+ */
+static function setInternalErrorMessage(string $msg): void
+
+
/**
+ * Convert any exception to a GraphQL spec compliant array.
+ *
+ * This method only exposes the exception message when the given exception
+ * implements the ClientAware interface, or when debug flags are passed.
+ *
+ * For a list of available debug flags @see \GraphQL\Error\DebugFlag constants.
+ *
+ * @return SerializableError
+ *
+ * @api
+ */
+static function createFromException(
+    Throwable $exception,
+    int $debugFlag = 'GraphQL\\Error\\DebugFlag::NONE',
+    ?string $internalErrorMessage = null
+): array
+
+
/**
+ * Returns error trace as serializable array.
+ *
+ * @return array<int, array{
+ *     file?: string,
+ *     line?: int,
+ *     function?: string,
+ *     call?: string,
+ * }>
+ *
+ * @api
+ */
+static function toSafeTrace(Throwable $error): array
+
+

GraphQL\Server\StandardServer

+

GraphQL server compatible with both: express-graphql +and Apollo Server. +Usage Example:.

+
$server = new StandardServer([
+  'schema' => $mySchema
+]);
+$server->handleRequest();
+
+

Or using ServerConfig instance:

+
$config = GraphQL\Server\ServerConfig::create()
+    ->setSchema($mySchema)
+    ->setContext($myContext);
+
+$server = new GraphQL\Server\StandardServer($config);
+$server->handleRequest();
+
+

See dedicated section in docs for details.

+

@see \GraphQL\Tests\Server\StandardServerTest

+

GraphQL\Server\StandardServer Methods

+
/**
+ * @param ServerConfig|array<string, mixed> $config
+ *
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+function __construct($config)
+
+
/**
+ * Parses HTTP request, executes and emits response (using standard PHP `header` function and `echo`).
+ *
+ * When $parsedBody is not set, it uses PHP globals to parse a request.
+ * It is possible to implement request parsing elsewhere (e.g. using framework Request instance)
+ * and then pass it to the server.
+ *
+ * See `executeRequest()` if you prefer to emit the response yourself
+ * (e.g. using the Response object of some framework).
+ *
+ * @param OperationParams|array<OperationParams> $parsedBody
+ *
+ * @api
+ *
+ * @throws \Exception
+ * @throws InvariantViolation
+ * @throws RequestError
+ */
+function handleRequest($parsedBody = null): void
+
+
/**
+ * Executes a GraphQL operation and returns an execution result
+ * (or promise when promise adapter is different from SyncPromiseAdapter).
+ *
+ * When $parsedBody is not set, it uses PHP globals to parse a request.
+ * It is possible to implement request parsing elsewhere (e.g. using framework Request instance)
+ * and then pass it to the server.
+ *
+ * PSR-7 compatible method executePsrRequest() does exactly this.
+ *
+ * @param OperationParams|array<OperationParams> $parsedBody
+ *
+ * @throws \Exception
+ * @throws InvariantViolation
+ * @throws RequestError
+ *
+ * @return ExecutionResult|array<int, ExecutionResult>|Promise
+ *
+ * @api
+ */
+function executeRequest($parsedBody = null)
+
+
/**
+ * Executes PSR-7 request and fulfills PSR-7 response.
+ *
+ * See `executePsrRequest()` if you prefer to create response yourself
+ * (e.g. using specific JsonResponse instance of some framework).
+ *
+ * @throws \Exception
+ * @throws \InvalidArgumentException
+ * @throws \JsonException
+ * @throws \RuntimeException
+ * @throws InvariantViolation
+ * @throws RequestError
+ *
+ * @return ResponseInterface|Promise
+ *
+ * @api
+ */
+function processPsrRequest(
+    Psr\Http\Message\RequestInterface $request,
+    Psr\Http\Message\ResponseInterface $response,
+    Psr\Http\Message\StreamInterface $writableBodyStream
+)
+
+
/**
+ * Executes GraphQL operation and returns execution result
+ * (or promise when promise adapter is different from SyncPromiseAdapter).
+ *
+ * @throws \Exception
+ * @throws \JsonException
+ * @throws InvariantViolation
+ * @throws RequestError
+ *
+ * @return ExecutionResult|array<int, ExecutionResult>|Promise
+ *
+ * @api
+ */
+function executePsrRequest(Psr\Http\Message\RequestInterface $request)
+
+

GraphQL\Server\ServerConfig

+

Server configuration class. +Could be passed directly to server constructor. List of options accepted by create method is +described in docs.

+

Usage example:

+
$config = GraphQL\Server\ServerConfig::create()
+    ->setSchema($mySchema)
+    ->setContext($myContext);
+
+$server = new GraphQL\Server\StandardServer($config);
+
+

@see ExecutionResult

+

@phpstan-type PersistedQueryLoader callable(string $queryId, OperationParams $operation): (string|DocumentNode) +@phpstan-type RootValueResolver callable(OperationParams $operation, DocumentNode $doc, string $operationType): mixed +@phpstan-type ValidationRulesOption array|null|callable(OperationParams $operation, DocumentNode $doc, string $operationType): array

+

@phpstan-import-type ErrorsHandler from ExecutionResult +@phpstan-import-type ErrorFormatter from ExecutionResult

+

@see \GraphQL\Tests\Server\ServerConfigTest

+

GraphQL\Server\ServerConfig Methods

+
/**
+ * Converts an array of options to instance of ServerConfig
+ * (or just returns empty config when array is not passed).
+ *
+ * @param array<string, mixed> $config
+ *
+ * @api
+ *
+ * @throws InvariantViolation
+ */
+static function create(array $config = []): self
+
+
/**
+ * @param mixed|callable $context
+ *
+ * @api
+ */
+function setContext($context): self
+
+
/**
+ * @param mixed|callable $rootValue
+ *
+ * @phpstan-param mixed|RootValueResolver $rootValue
+ *
+ * @api
+ */
+function setRootValue($rootValue): self
+
+
/**
+ * @phpstan-param ErrorFormatter $errorFormatter
+ *
+ * @api
+ */
+function setErrorFormatter(callable $errorFormatter): self
+
+
/**
+ * @phpstan-param ErrorsHandler $handler
+ *
+ * @api
+ */
+function setErrorsHandler(callable $handler): self
+
+
/**
+ * Set validation rules for this server.
+ *
+ * @param array<ValidationRule>|callable|null $validationRules
+ *
+ * @phpstan-param ValidationRulesOption $validationRules
+ *
+ * @api
+ */
+function setValidationRules($validationRules): self
+
+
/**
+ * @phpstan-param PersistedQueryLoader|null $persistedQueryLoader
+ *
+ * @api
+ */
+function setPersistedQueryLoader(?callable $persistedQueryLoader): self
+
+
/**
+ * Set response debug flags.
+ *
+ * @see \GraphQL\Error\DebugFlag class for a list of all available flags
+ *
+ * @api
+ */
+function setDebugFlag(int $debugFlag = 'GraphQL\\Error\\DebugFlag::INCLUDE_DEBUG_MESSAGE'): self
+
+
/**
+ * Allow batching queries (disabled by default).
+ *
+ * @api
+ */
+function setQueryBatching(bool $enableBatching): self
+
+

GraphQL\Server\Helper

+

Contains functionality that could be re-used by various server implementations.

+

@see \GraphQL\Tests\Server\HelperTest

+

GraphQL\Server\Helper Methods

+
/**
+ * Parses HTTP request using PHP globals and returns GraphQL OperationParams
+ * contained in this request. For batched requests it returns an array of OperationParams.
+ *
+ * This function does not check validity of these params
+ * (validation is performed separately in validateOperationParams() method).
+ *
+ * If $readRawBodyFn argument is not provided - will attempt to read raw request body
+ * from `php://input` stream.
+ *
+ * Internally it normalizes input to $method, $bodyParams and $queryParams and
+ * calls `parseRequestParams()` to produce actual return value.
+ *
+ * For PSR-7 request parsing use `parsePsrRequest()` instead.
+ *
+ * @throws RequestError
+ *
+ * @return OperationParams|array<int, OperationParams>
+ *
+ * @api
+ */
+function parseHttpRequest(?callable $readRawBodyFn = null)
+
+
/**
+ * Parses normalized request params and returns instance of OperationParams
+ * or array of OperationParams in case of batch operation.
+ *
+ * Returned value is a suitable input for `executeOperation` or `executeBatch` (if array)
+ *
+ * @param array<mixed> $bodyParams
+ * @param array<mixed> $queryParams
+ *
+ * @throws RequestError
+ *
+ * @return OperationParams|array<int, OperationParams>
+ *
+ * @api
+ */
+function parseRequestParams(string $method, array $bodyParams, array $queryParams)
+
+
/**
+ * Checks validity of OperationParams extracted from HTTP request and returns an array of errors
+ * if params are invalid (or empty array when params are valid).
+ *
+ * @return array<int, RequestError>
+ *
+ * @api
+ */
+function validateOperationParams(GraphQL\Server\OperationParams $params): array
+
+
/**
+ * Executes GraphQL operation with given server configuration and returns execution result
+ * (or promise when promise adapter is different from SyncPromiseAdapter).
+ *
+ * @throws \Exception
+ * @throws InvariantViolation
+ *
+ * @return ExecutionResult|Promise
+ *
+ * @api
+ */
+function executeOperation(GraphQL\Server\ServerConfig $config, GraphQL\Server\OperationParams $op)
+
+
/**
+ * Executes batched GraphQL operations with shared promise queue
+ * (thus, effectively batching deferreds|promises of all queries at once).
+ *
+ * @param array<OperationParams> $operations
+ *
+ * @throws \Exception
+ * @throws InvariantViolation
+ *
+ * @return array<int, ExecutionResult>|Promise
+ *
+ * @api
+ */
+function executeBatch(GraphQL\Server\ServerConfig $config, array $operations)
+
+
/**
+ * Send response using standard PHP `header()` and `echo`.
+ *
+ * @param Promise|ExecutionResult|array<ExecutionResult> $result
+ *
+ * @api
+ *
+ * @throws \JsonException
+ */
+function sendResponse($result): void
+
+
/**
+ * Converts PSR-7 request to OperationParams or an array thereof.
+ *
+ * @throws RequestError
+ *
+ * @return OperationParams|array<OperationParams>
+ *
+ * @api
+ */
+function parsePsrRequest(Psr\Http\Message\RequestInterface $request)
+
+
/**
+ * Converts query execution result to PSR-7 response.
+ *
+ * @param Promise|ExecutionResult|array<ExecutionResult> $result
+ *
+ * @throws \InvalidArgumentException
+ * @throws \JsonException
+ * @throws \RuntimeException
+ *
+ * @return Promise|ResponseInterface
+ *
+ * @api
+ */
+function toPsrResponse(
+    $result,
+    Psr\Http\Message\ResponseInterface $response,
+    Psr\Http\Message\StreamInterface $writableBodyStream
+)
+
+

GraphQL\Server\OperationParams

+

Structure representing parsed HTTP parameters for GraphQL operation.

+

The properties in this class are not strictly typed, as this class +is only meant to serve as an intermediary representation which is +not yet validated.

+

GraphQL\Server\OperationParams Props

+
/**
+ * Id of the query (when using persisted queries).
+ *
+ * Valid aliases (case-insensitive):
+ * - id
+ * - queryId
+ * - documentId
+ *
+ * @api
+ *
+ * @var mixed should be string|null
+ */
+public $queryId;
+
+/**
+ * A document containing GraphQL operations and fragments to execute.
+ *
+ * @api
+ *
+ * @var mixed should be string|null
+ */
+public $query;
+
+/**
+ * The name of the operation in the document to execute.
+ *
+ * @api
+ *
+ * @var mixed should be string|null
+ */
+public $operation;
+
+/**
+ * Values for any variables defined by the operation.
+ *
+ * @api
+ *
+ * @var mixed should be array<string, mixed>
+ */
+public $variables;
+
+/**
+ * Reserved for implementors to extend the protocol however they see fit.
+ *
+ * @api
+ *
+ * @var mixed should be array<string, mixed>
+ */
+public $extensions;
+
+/**
+ * Executed in read-only context (e.g. via HTTP GET request)?
+ *
+ * @api
+ */
+public $readOnly;
+
+/**
+ * The raw params used to construct this instance.
+ *
+ * @api
+ *
+ * @var array<string, mixed>
+ */
+public $originalInput;
+
+

GraphQL\Server\OperationParams Methods

+
/**
+ * Creates an instance from given array.
+ *
+ * @param array<string, mixed> $params
+ *
+ * @api
+ */
+static function create(array $params, bool $readonly = false): GraphQL\Server\OperationParams
+
+

GraphQL\Utils\BuildSchema

+

Build instance of @see \GraphQL\Type\Schema out of schema language definition (string or parsed AST).

+

See schema definition language docs for details.

+

@phpstan-import-type TypeConfigDecorator from ASTDefinitionBuilder

+

@phpstan-type BuildSchemaOptions array{ +assumeValid?: bool, +assumeValidSDL?: bool +}

+
    +
  • assumeValid: + When building a schema from a GraphQL service's introspection result, it + might be safe to assume the schema is valid. Set to true to assume the + produced schema is valid.
  • +
+

Default: false

+
    +
  • assumeValidSDL: + Set to true to assume the SDL is valid.
  • +
+

Default: false

+

@see \GraphQL\Tests\Utils\BuildSchemaTest

+

GraphQL\Utils\BuildSchema Methods

+
/**
+ * A helper function to build a GraphQLSchema directly from a source
+ * document.
+ *
+ * @param DocumentNode|Source|string $source
+ *
+ * @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
+ *
+ * @param array<string, bool> $options
+ *
+ * @phpstan-param BuildSchemaOptions $options
+ *
+ * @api
+ *
+ * @throws \Exception
+ * @throws \ReflectionException
+ * @throws Error
+ * @throws InvariantViolation
+ * @throws SyntaxError
+ */
+static function build($source, ?callable $typeConfigDecorator = null, array $options = []): GraphQL\Type\Schema
+
+
/**
+ * This takes the AST of a schema from @see \GraphQL\Language\Parser::parse().
+ *
+ * If no schema definition is provided, then it will look for types named Query and Mutation.
+ *
+ * Given that AST it constructs a @see \GraphQL\Type\Schema. The resulting schema
+ * has no resolve methods, so execution will use default resolvers.
+ *
+ * @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
+ *
+ * @param array<string, bool> $options
+ *
+ * @phpstan-param BuildSchemaOptions $options
+ *
+ * @api
+ *
+ * @throws \Exception
+ * @throws \ReflectionException
+ * @throws Error
+ * @throws InvariantViolation
+ */
+static function buildAST(
+    GraphQL\Language\AST\DocumentNode $ast,
+    ?callable $typeConfigDecorator = null,
+    array $options = []
+): GraphQL\Type\Schema
+
+

GraphQL\Utils\AST

+

Various utilities dealing with AST.

+

GraphQL\Utils\AST Methods

+
/**
+ * Convert representation of AST as an associative array to instance of GraphQL\Language\AST\Node.
+ *
+ * For example:
+ *
+ * ```php
+ * AST::fromArray([
+ *     'kind' => 'ListValue',
+ *     'values' => [
+ *         ['kind' => 'StringValue', 'value' => 'my str'],
+ *         ['kind' => 'StringValue', 'value' => 'my other str']
+ *     ],
+ *     'loc' => ['start' => 21, 'end' => 25]
+ * ]);
+ * ```
+ *
+ * Will produce instance of `ListValueNode` where `values` prop is a lazily-evaluated `NodeList`
+ * returning instances of `StringValueNode` on access.
+ *
+ * This is a reverse operation for AST::toArray($node)
+ *
+ * @param array<string, mixed> $node
+ *
+ * @api
+ *
+ * @throws \JsonException
+ * @throws InvariantViolation
+ */
+static function fromArray(array $node): GraphQL\Language\AST\Node
+
+
/**
+ * Convert AST node to serializable array.
+ *
+ * @return array<string, mixed>
+ *
+ * @api
+ */
+static function toArray(GraphQL\Language\AST\Node $node): array
+
+
/**
+ * Produces a GraphQL Value AST given a PHP value.
+ *
+ * Optionally, a GraphQL type may be provided, which will be used to
+ * disambiguate between value primitives.
+ *
+ * | PHP Value     | GraphQL Value        |
+ * | ------------- | -------------------- |
+ * | Object        | Input Object         |
+ * | Assoc Array   | Input Object         |
+ * | Array         | List                 |
+ * | Boolean       | Boolean              |
+ * | String        | String / Enum Value  |
+ * | Int           | Int                  |
+ * | Float         | Int / Float          |
+ * | Mixed         | Enum Value           |
+ * | null          | NullValue            |
+ *
+ * @param mixed $value
+ * @param InputType&Type $type
+ *
+ * @throws \JsonException
+ * @throws InvariantViolation
+ * @throws SerializationError
+ *
+ * @return (ValueNode&Node)|null
+ *
+ * @api
+ */
+static function astFromValue($value, GraphQL\Type\Definition\InputType $type): ?GraphQL\Language\AST\ValueNode
+
+
/**
+ * Produces a PHP value given a GraphQL Value AST.
+ *
+ * A GraphQL type must be provided, which will be used to interpret different
+ * GraphQL Value literals.
+ *
+ * Returns `null` when the value could not be validly coerced according to
+ * the provided type.
+ *
+ * | GraphQL Value        | PHP Value     |
+ * | -------------------- | ------------- |
+ * | Input Object         | Assoc Array   |
+ * | List                 | Array         |
+ * | Boolean              | Boolean       |
+ * | String               | String        |
+ * | Int / Float          | Int / Float   |
+ * | Enum Value           | Mixed         |
+ * | Null Value           | null          |
+ *
+ * @param (ValueNode&Node)|null $valueNode
+ * @param array<string, mixed>|null $variables
+ *
+ * @throws \Exception
+ *
+ * @return mixed
+ *
+ * @api
+ */
+static function valueFromAST(
+    ?GraphQL\Language\AST\ValueNode $valueNode,
+    GraphQL\Type\Definition\Type $type,
+    ?array $variables = null
+)
+
+
/**
+ * Produces a PHP value given a GraphQL Value AST.
+ *
+ * Unlike `valueFromAST()`, no type is provided. The resulting PHP value
+ * will reflect the provided GraphQL value AST.
+ *
+ * | GraphQL Value        | PHP Value     |
+ * | -------------------- | ------------- |
+ * | Input Object         | Assoc Array   |
+ * | List                 | Array         |
+ * | Boolean              | Boolean       |
+ * | String               | String        |
+ * | Int / Float          | Int / Float   |
+ * | Enum                 | Mixed         |
+ * | Null                 | null          |
+ *
+ * @param array<string, mixed>|null $variables
+ *
+ * @throws \Exception
+ *
+ * @return mixed
+ *
+ * @api
+ */
+static function valueFromASTUntyped(GraphQL\Language\AST\Node $valueNode, ?array $variables = null)
+
+
/**
+ * Returns type definition for given AST Type node.
+ *
+ * @param callable(string): ?Type $typeLoader
+ * @param NamedTypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode
+ *
+ * @throws \Exception
+ *
+ * @api
+ */
+static function typeFromAST(callable $typeLoader, GraphQL\Language\AST\Node $inputTypeNode): ?GraphQL\Type\Definition\Type
+
+
/**
+ * Returns the operation within a document by name.
+ *
+ * If a name is not provided, an operation is only returned if the document has exactly one.
+ *
+ * @api
+ */
+static function getOperationAST(GraphQL\Language\AST\DocumentNode $document, ?string $operationName = null): ?GraphQL\Language\AST\OperationDefinitionNode
+
+
/**
+ * Provided a collection of ASTs, presumably each from different files,
+ * concatenate the ASTs together into batched AST, useful for validating many
+ * GraphQL source files which together represent one conceptual application.
+ *
+ * @param array<DocumentNode> $documents
+ *
+ * @api
+ */
+static function concatAST(array $documents): GraphQL\Language\AST\DocumentNode
+
+

GraphQL\Utils\SchemaPrinter

+

Prints the contents of a Schema in schema definition language.

+

All sorting options sort alphabetically. If not given or false, the original schema definition order will be used.

+

@phpstan-type Options array{ +sortArguments?: bool, +sortEnumValues?: bool, +sortFields?: bool, +sortInputFields?: bool, +sortTypes?: bool, +}

+

@see \GraphQL\Tests\Utils\SchemaPrinterTest

+

GraphQL\Utils\SchemaPrinter Methods

+
/**
+ * @param array<string, bool> $options
+ *
+ * @phpstan-param Options $options
+ *
+ * @api
+ *
+ * @throws \JsonException
+ * @throws Error
+ * @throws InvariantViolation
+ * @throws SerializationError
+ */
+static function doPrint(GraphQL\Type\Schema $schema, array $options = []): string
+
+
/**
+ * @param array<string, bool> $options
+ *
+ * @phpstan-param Options $options
+ *
+ * @api
+ *
+ * @throws \JsonException
+ * @throws Error
+ * @throws InvariantViolation
+ * @throws SerializationError
+ */
+static function printIntrospectionSchema(GraphQL\Type\Schema $schema, array $options = []): string
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/complementary-tools/index.html b/complementary-tools/index.html new file mode 100644 index 000000000..a353b6713 --- /dev/null +++ b/complementary-tools/index.html @@ -0,0 +1,727 @@ + + + + + + + + + + + + + + + + + + + + + + + + Complementary Tools - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Complementary Tools

+ +

Server Integrations

+ +

Server Utilities

+ +

GraphQL Clients

+
    +
  • GraphiQL – Graphical interactive in-browser GraphQL IDE
  • +
  • GraphQL Playground – GraphQL IDE for better development workflows (GraphQL Subscriptions, interactive docs & collaboration)
  • +
  • Altair GraphQL Client - Beautiful feature-rich GraphQL Client for all platforms
  • +
  • Sailor - Typesafe GraphQL client for PHP
  • +
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/concepts/index.html b/concepts/index.html new file mode 100644 index 000000000..cd40fac1e --- /dev/null +++ b/concepts/index.html @@ -0,0 +1,841 @@ + + + + + + + + + + + + + + + + + + + + + + + + Concepts - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Concepts

+ +

Overview

+

GraphQL is data-centric. On the very top level it is built around three major concepts: +Schema, Query and Mutation.

+

You are expected to express your application as a Schema (aka Type System) and expose it +as a single HTTP endpoint (e.g. using our standard server). +Application clients (e.g. web or mobile clients) send Queries +to this endpoint to request structured data and Mutations to perform changes (usually with HTTP POST method).

+

Queries

+

Queries are expressed in simple language that resembles JSON:

+
{
+  hero {
+    name
+    friends {
+      name
+    }
+  }
+}
+
+

It was designed to mirror the structure of the expected response:

+
{
+  "hero": {
+    "name": "R2-D2",
+    "friends": [
+      { "name": "Luke Skywalker" },
+      { "name": "Han Solo" },
+      { "name": "Leia Organa" }
+    ]
+  }
+}
+
+

The graphql-php runtime parses Queries, makes sure that they are valid for a given Type System +and executes using data fetching tools provided by you +as part of the integration. Queries are supposed to be idempotent.

+

Mutations

+

Mutations are root fields that are allowed to have side effects, such as creating, updating or deleting data. +In contrast to Query fields, the fields within the root Mutation type are executed serially. +Otherwise, their definition and execution is identical to all other fields.

+
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
+  createReview(episode: $ep, review: $review) {
+    stars
+    commentary
+  }
+}
+
+

Variables $ep and $review are sent alongside with the mutation. A full HTTP request might look like this:

+
// POST /graphql-endpoint
+// Content-Type: application/javascript
+//
+{
+  "query": "mutation CreateReviewForEpisode...",
+  "variables": {
+    "ep": "JEDI",
+    "review": {
+      "stars": 5,
+      "commentary": "This is a great movie!"
+    }
+  }
+}
+
+

As you see variables may include complex objects and they will be correctly validated by +the graphql-php runtime.

+

Another nice feature of GraphQL mutations is that they also hold the query for data to be +returned after mutation. In our example the mutation will return:

+
{
+  "createReview": {
+    "stars": 5,
+    "commentary": "This is a great movie!"
+  }
+}
+
+

Type System

+

Conceptually a GraphQL type is a collection of fields. Each field in turn +has its own type which allows building complex hierarchies.

+

Quick example on pseudo-language:

+
type BlogPost {
+    title: String!
+    author: User
+    body: String
+}
+
+type User {
+    id: Id!
+    firstName: String
+    lastName: String
+}
+
+

The type system is at the heart of GraphQL integration. That's where graphql-php comes into play.

+

It provides the following tools and primitives to describe your App as a hierarchy of types:

+
    +
  • Primitives for defining objects and interfaces
  • +
  • Primitives for defining enumerations and unions
  • +
  • Primitives for defining custom scalar types
  • +
  • Built-in scalar types: ID, String, Int, Float, Boolean
  • +
  • Built-in type modifiers: ListOf and NonNull
  • +
+

Same example expressed in graphql-php:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$userType = new ObjectType([
+    'name' => 'User',
+    'fields' => [
+        'id' => Type::nonNull(Type::id()),
+        'firstName' => Type::string(),
+        'lastName' => Type::string()
+    ]
+]);
+
+$blogPostType = new ObjectType([
+    'name' => 'BlogPost',
+    'fields' => [
+        'title' => Type::nonNull(Type::string()),
+        'author' => $userType
+    ]
+]);
+
+

Further Reading

+

To get deeper understanding of GraphQL concepts - read the docs on official GraphQL website

+

To get started with graphql-php - continue to next section "Getting Started"

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/data-fetching/index.html b/data-fetching/index.html new file mode 100644 index 000000000..e10788b4f --- /dev/null +++ b/data-fetching/index.html @@ -0,0 +1,985 @@ + + + + + + + + + + + + + + + + + + + + + + + + Fetching Data - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Fetching Data

+ +

Overview

+

GraphQL is data-storage agnostic. You can use any underlying data storage engine, including but not limited to +SQL or NoSQL databases, plain files or in-memory data structures.

+

In order to convert the GraphQL query to a PHP array, graphql-php traverses query fields (using depth-first algorithm) +and runs the special resolve function on each field. This resolve function is provided by you as a part of the +field definition or query execution call.

+

The result returned by the resolve function is directly included in the response (for scalars and enums) +or passed down to nested fields (for objects).

+

Let's walk through an example. Consider the following GraphQL query:

+
{
+  lastStory {
+    title
+    author {
+      name
+    }
+  }
+}
+
+

We need a Schema that can fulfill it. On the very top level, the Schema contains the Query type:

+
use GraphQL\Type\Definition\ObjectType;
+
+$queryType = new ObjectType([
+    'name' => 'Query',
+        'fields' => [
+        'lastStory' => [
+            'type' => $blogStoryType,
+            'resolve' => fn (): array => [
+                'id' => 1,
+                'title' => 'Example blog post',
+                'authorId' => 1
+            ],
+        ]
+    ]
+]);
+
+

As we see, the field lastStory has a resolve function that is responsible for fetching data.

+

In our example, we simply return a static value, but in the real-world application you would query +your data source and return the result from there.

+

Since lastStory is of composite type BlogStory, this result is passed down to fields of this type:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+const USERS = [
+    1 => [
+        'id' => 1,
+        'name' => 'Smith'
+    ],
+    2 => [
+        'id' => 2,
+        'name' => 'Anderson'
+    ]
+];
+
+$blogStoryType = new ObjectType([
+    'name' => 'BlogStory',
+    'fields' => [
+        'author' => [
+            'type' => $userType,
+            'resolve' => fn (array $blogStory): array => USERS[$blogStory['authorId']],
+        ],
+        'title' => [
+            'type' => Type::string()
+        ]
+    ]
+]);
+
+

Here $blogStory is the array returned by the lastStory field above.

+

Again: in real-world applications you would fetch user data from your data source by authorId and return it. +Also, note that you don't have to return arrays. You can return any value, graphql-php will pass it untouched +to nested resolvers.

+

But then the question appears - the field title has no resolve option, how is it resolved? +When you define no custom resolver, the default field resolver applies.

+

Default Field Resolver

+

graphql-php provides the following default field resolver:

+
use GraphQL\Type\Definition\ResolveInfo;
+
+function defaultFieldResolver($objectValue, array $args, $context, ResolveInfo $info)
+{
+    $fieldName = $info->fieldName;
+    $property = null;
+
+    if (is_array($objectValue) || $objectValue instanceof ArrayAccess) {
+        if (isset($objectValue[$fieldName])) {
+            $property = $objectValue[$fieldName];
+        }
+    } elseif (is_object($objectValue)) {
+        if (isset($objectValue->{$fieldName})) {
+            $property = $objectValue->{$fieldName};
+        }
+    }
+
+    return $property instanceof Closure
+        ? $property($objectValue, $args, $contextValue, $info)
+        : $property;
+}
+
+

It returns value by key (for arrays) or property (for objects). +If the value is not set, it returns null.

+

To override the default resolver, pass it as an argument to executeQuery.

+

Default Field Resolver per Type

+

Sometimes it might be convenient to set default field resolver per type. You can do so by providing +resolveField option in type config. For example:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\ResolveInfo;
+
+$userType = new ObjectType([
+    'name' => 'User',
+    'fields' => [
+        'name' => Type::string(),
+        'email' => Type::string()
+    ],
+    'resolveField' => function (User $user, array $args, $context, ResolveInfo $info) {
+        switch ($info->fieldName) {
+            case 'name': return $user->getName();
+            case 'email': return $user->getEmail();
+            default: return null;
+        }
+    },
+]);
+
+

Keep in mind that field resolver has precedence over default field resolver per type which in turn +has precedence over default field resolver.

+

Optimize Resolvers

+

The 4th argument of resolver functions is an instance of ResolveInfo. +It contains information that is useful for the field resolution process.

+

Depending on which data source is used, knowing which fields the client queried can be used to optimize +the performance of a resolver. For example, an SQL query may only need to select the queried fields.

+

The following example limits which columns are selected from the database:

+
use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\ResolveInfo;
+
+$queryType = new ObjectType([
+    'name' => 'Query',
+    'fields' => [
+        'lastStory' => [
+            'type' => $storyType,
+            'resolve' => function ($root, array $args, $context, ResolveInfo $resolveInfo): Story {
+                // Fictitious API, use whatever database access your application/framework provides
+                $builder = Story::builder();
+                foreach ($resolveInfo->getFieldSelection() as $field => $_) {
+                  $builder->addSelect($field);
+                }
+
+                return $builder->last();
+            }
+        ]
+    ]
+]);
+
+

Solving N+1 Problem

+

Since: 0.9.0

+

One of the most annoying problems with data fetching is a so-called +N+1 problem.
+Consider following GraphQL query:

+
{
+  topStories(limit: 10) {
+    title
+    author {
+      name
+      email
+    }
+  }
+}
+
+

Naive field resolution process would require up to 10 calls to the underlying data store to fetch authors for all 10 stories.

+

graphql-php provides tools to mitigate this problem: it allows you to defer actual field resolution to a later stage +when one batched query could be executed instead of 10 distinct queries.

+

Here is an example of BlogStory resolver for field author that uses deferring:

+
use GraphQL\Deferred;
+
+'resolve' => function (array $blogStory): Deferred {
+    MyUserBuffer::add($blogStory['authorId']);
+
+    return new Deferred(function () use ($blogStory): User {
+        MyUserBuffer::loadBuffered();
+
+        return MyUserBuffer::get($blogStory['authorId']);
+    });
+}
+
+

In this example, we fill up the buffer with 10 author ids first. Then graphql-php continues +resolving other non-deferred fields until there are none of them left.

+

After that, it calls closures wrapped by GraphQL\Deferred which in turn load all buffered +ids once (using SQL IN(?), Redis MGET or similar tools) and returns the final field value.

+

Originally this approach was advocated by Facebook in their Dataloader +project. This solution enables very interesting optimizations at no cost. Consider the following query:

+
{
+  topStories(limit: 10) {
+    author {
+      email
+    }
+  }
+  category {
+    stories(limit: 10) {
+      author {
+        email
+      }
+    }
+  }
+}
+
+

Even though author field is located on different levels of the query - it can be buffered in the same buffer. +In this example, only one query will be executed for all story authors comparing to 20 queries +in a naive implementation.

+

Async PHP

+

If your project runs in an environment that supports async operations +(like HHVM, ReactPHP, AMPHP, appserver.io, PHP threads, etc) +you can leverage the power of your platform to resolve some fields asynchronously.

+

The only requirement: your platform must support the concept of Promises compatible with +Promises A+ specification.

+

To start using this feature, switch facade method for query execution from +executeQuery to promiseToExecute:

+
use GraphQL\GraphQL;
+use GraphQL\Executor\ExecutionResult;
+
+$promise = GraphQL::promiseToExecute(
+    $promiseAdapter,
+    $schema,
+    $queryString,
+    $rootValue = null,
+    $contextValue = null,
+    $variableValues = null,
+    $operationName = null,
+    $fieldResolver = null,
+    $validationRules = null
+);
+$promise->then(fn (ExecutionResult $result): array => $result->toArray());
+
+

Where $promiseAdapter is an instance of:

+ +

Then your resolve functions should return promises of your platform instead of GraphQL\Deferreds.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/error-handling/index.html b/error-handling/index.html new file mode 100644 index 000000000..ec18c6bc8 --- /dev/null +++ b/error-handling/index.html @@ -0,0 +1,906 @@ + + + + + + + + + + + + + + + + + + + + + + + + Handling Errors - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Handling Errors

+ +

Errors in GraphQL

+

There are 3 types of errors in GraphQL:

+
    +
  • Syntax: query has invalid syntax and could not be parsed;
  • +
  • Validation: query is incompatible with type system (e.g. unknown field is requested);
  • +
  • Execution: occurs when some field resolver throws (or returns unexpected value).
  • +
+

When Syntax or Validation errors are detected, an exception is thrown +and the query is not executed.

+

Exceptions thrown during query execution are caught and collected in the result. +They are available in $errors prop of GraphQL\Executor\ExecutionResult.

+

GraphQL is forgiving to Execution errors which occur in resolvers of nullable fields. +If such field throws or returns unexpected value the value of the field in response will be simply +replaced with null and error entry will be registered.

+

If an exception is thrown in the non-null field - error bubbles up to the first nullable field. +This nullable field is replaced with null and error entry is added to the result. +If all fields up to the root are non-null - data entry will be removed from the result
+and only errors key will be presented.

+

When the result is converted to a serializable array using its toArray() method, all errors are +converted to arrays as well using default error formatting (see below).

+

Alternatively, you can apply custom error filtering and formatting +for your specific requirements.

+

Default Error formatting

+

By default, each error entry is converted to an associative array with following structure:

+
[
+    'message' => 'Error message',
+    'extensions' => [
+        'key' => 'value',
+    ],
+    'locations' => [
+        ['line' => 1, 'column' => 2],
+    ],
+    'path' => [
+        'listField',
+        0,
+        'fieldWithException',
+    ],
+];
+
+

Entry at key locations points to a character in query string which caused the error. +In some cases (like deep fragment fields) locations will include several entries to track down +the path to field with the error in query.

+

Entry at key path exists only for errors caused by exceptions thrown in resolvers. +It contains a path from the very root field to actual field value producing an error +(including indexes for list types and field names for composite types).

+

Internal errors

+

As of version 0.10.0, all exceptions thrown in resolvers are reported with generic message "Internal server error". +This is done to avoid information leak in production environments (e.g. database connection errors, file access errors, etc).

+

Only exceptions implementing interface GraphQL\Error\ClientAware and claiming themselves as safe will +be reported with a full error message.

+

For example:

+
use GraphQL\Error\ClientAware;
+
+class MySafeException extends \Exception implements ClientAware
+{
+    public function isClientSafe(): bool
+    {
+        return true;
+    }
+}
+
+

When such exception is thrown it will be reported with a full error message:

+
[
+    'message' => 'My reported error',
+    'locations' => [
+        ['line' => 10, 'column' => 2],
+    ],
+    'path' => [
+        'path',
+        'to',
+        'fieldWithException',
+    ]
+];
+
+

To change default "Internal server error" message to something else, use:

+
GraphQL\Error\FormattedError::setInternalErrorMessage("Unexpected error");
+
+

Debugging tools

+

During development or debugging, use DebugFlag::INCLUDE_DEBUG_MESSAGE to +add hidden error messages each formatted error entry under the key extensions.debugMessage.

+
use GraphQL\GraphQL;
+use GraphQL\Error\DebugFlag;
+
+$result = GraphQL::executeQuery(/*args*/)
+    ->toArray(DebugFlag::INCLUDE_DEBUG_MESSAGE);
+
+

If you also want to add the exception trace, pass DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::INCLUDE_TRACE instead. +This will make each error entry look like this:

+
[
+    'message' => 'Internal server error',
+    'locations' => [
+        ['line' => 10, 'column' => 2],
+    ],
+    'path' => [
+        'listField',
+        0,
+        'fieldWithException',
+    ],
+    'extensions' => [
+        'debugMessage' => 'Actual exception message',
+        'trace' => [
+            /* Formatted original exception trace */
+        ],
+    ],
+]
+
+

If you prefer the first resolver exception to be re-thrown, use the following flags:

+
use GraphQL\GraphQL;
+use GraphQL\Error\DebugFlag;
+
+$executionResult = GraphQL::executeQuery(/*args*/);
+
+// Will throw if there was an exception in resolver during execution
+$executionResult ->toArray(DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::RETHROW_INTERNAL_EXCEPTIONS);
+
+

If you only want to re-throw Exceptions that are not marked as safe through the ClientAware interface, use DebugFlag::RETHROW_UNSAFE_EXCEPTIONS.

+

Custom Error Handling and Formatting

+

It is possible to define custom formatter and handler for result errors.

+

Formatter is responsible for converting instances of GraphQL\Error\Error +to an array. Handler is useful for error filtering and logging.

+

For example, these are default formatter and handler:

+
use GraphQL\GraphQL;
+use GraphQL\Error\Error;
+use GraphQL\Error\FormattedError;
+
+$result = GraphQL::executeQuery(/* $args */)
+    ->setErrorFormatter(fn (Error $error): array => FormattedError::createFromException($error))
+    ->setErrorsHandler(fn (array $errors, callable $formatter): array => array_map($formatter, $errors))
+    ->toArray();
+
+

Note that when you pass debug flags to toArray() your custom formatter will still be +decorated with same debugging information mentioned above.

+

Schema Errors

+

So far we only covered errors which occur during query execution process. Schema definition can +also throw GraphQL\Error\InvariantViolation if there is an error in one of type definitions.

+

Usually such errors mean that there is some logical error in your schema. +In this case it makes sense to return a status code 500 (Internal Server Error) for GraphQL endpoint:

+
use GraphQL\GraphQL;
+use GraphQL\Type\Schema;
+use GraphQL\Error\FormattedError;
+
+try {
+    $schema = new Schema([
+        // ...
+    ]);
+
+    $body = GraphQL::executeQuery($schema, $query);
+    $status = 200;
+} catch(\Exception $e) {
+    $body = [
+        'errors' => [FormattedError::createFromException($e)]
+    ];
+    $status = 500;
+}
+
+header('Content-Type: application/json', true, $status);
+echo json_encode($body, JSON_THROW_ON_ERROR);
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/executing-queries/index.html b/executing-queries/index.html new file mode 100644 index 000000000..e8f906bb7 --- /dev/null +++ b/executing-queries/index.html @@ -0,0 +1,1048 @@ + + + + + + + + + + + + + + + + + + + + + + + + Executing Queries - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Executing Queries

+ +

Using Facade Method

+

Query execution is a complex process involving multiple steps, including query parsing, +validating and finally executing against your schema.

+

graphql-php provides a convenient facade for this process in class +GraphQL\GraphQL:

+
use GraphQL\GraphQL;
+
+$result = GraphQL::executeQuery(
+    $schema,
+    $queryString,
+    $rootValue = null,
+    $context = null,
+    $variableValues = null,
+    $operationName = null,
+    $fieldResolver = null,
+    $validationRules = null
+);
+
+

It returns an instance of GraphQL\Executor\ExecutionResult +which can be easily converted to array:

+
$serializableResult = $result->toArray();
+
+

Returned array contains data and errors keys, as described by the +GraphQL spec. +This array is suitable for further serialization (e.g. using json_encode). +See also the section on error handling and formatting.

+

Method arguments

+

Description of executeQuery method arguments:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArgumentTypeNotes
schemaGraphQL\Type\SchemaRequired. Instance of your application Schema
queryStringstring or GraphQL\Language\AST\DocumentNodeRequired. Actual GraphQL query string to be parsed, validated and executed. If you parse query elsewhere before executing - pass corresponding AST document here to avoid new parsing.
rootValuemixedAny value that represents a root of your data graph. It is passed as the 1st argument to field resolvers of Query type. Can be omitted or set to null if actual root values are fetched by Query type itself.
contextmixedAny value that holds information shared between all field resolvers. Most often they use it to pass currently logged in user, locale details, etc.

It will be available as the 3rd argument in all field resolvers. (see section on Field Definitions for reference) graphql-php never modifies this value and passes it as is to all underlying resolvers.
variableValuesarrayMap of variable values passed along with query string. See section on query variables on official GraphQL website. Note that while variableValues must be an associative array, the values inside it can be nested using \stdClass if desired.
operationNamestringAllows the caller to specify which operation in queryString will be run, in cases where queryString contains multiple top-level operations.
fieldResolvercallableA resolver function to use when one is not provided by the schema. If not provided, the default field resolver is used.
validationRulesarrayA set of rules for query validation step. The default value is all available rules. Empty array would allow skipping query validation (may be convenient for persisted queries which are validated before persisting and assumed valid during execution)
+

Using Server

+

If you are building HTTP GraphQL API, you may prefer our Standard Server +(compatible with express-graphql). +It supports more features out of the box, including parsing HTTP requests, producing a spec-compliant response; batched queries; persisted queries.

+

Usage example (with plain PHP):

+
use GraphQL\Server\StandardServer;
+
+$server = new StandardServer([/* server options, see below */]);
+$server->handleRequest(); // parses PHP globals and emits response
+
+

Server also supports PSR-7 request/response interfaces:

+
use GraphQL\Server\StandardServer;
+use GraphQL\Executor\ExecutionResult;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/** @var RequestInterface $psrRequest */
+/** @var ResponseInterface $psrResponse */
+/** @var StreamInterface $psrBodyStream */
+$server = new StandardServer([/* server options, see below */]);
+$psrResponse = $server->processPsrRequest($psrRequest, $psrResponse, $psrBodyStream);
+
+
+// Alternatively create PSR-7 response yourself:
+
+/** @var ExecutionResult|ExecutionResult[] $result */
+$result = $server->executePsrRequest($psrRequest);
+$jsonResult = json_encode($result, JSON_THROW_ON_ERROR);
+$psrResponse = new SomePsr7ResponseImplementation($jsonResult );
+
+

PSR-7 is useful when you want to integrate the server into existing framework:

+ +

Server configuration options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArgumentTypeNotes
schemaSchemaRequired. Instance of your application Schema
rootValuemixedAny value that represents a root of your data graph. It is passed as the 1st argument to field resolvers of Query type. Can be omitted or set to null if actual root values are fetched by Query type itself.
contextmixedAny value that holds information shared between all field resolvers. Most often they use it to pass currently logged in user, locale details, etc.

It will be available as the 3rd argument in all field resolvers. (see section on Field Definitions for reference) graphql-php never modifies this value and passes it as is to all underlying resolvers.
fieldResolvercallableA resolver function to use when one is not provided by the schema. If not provided, the default field resolver is used.
validationRulesarray or callableA set of rules for query validation step. The default value is all available rules. The empty array would allow skipping query validation (may be convenient for persisted queries which are validated before persisting and assumed valid during execution).

Pass callable to return different validation rules for different queries (e.g. empty array for persisted query and a full list of rules for regular queries). When passed, it is expected to have the following signature:

function (OperationParams $params, DocumentNode $node, $operationType): array
queryBatchingboolFlag indicating whether this server supports query batching (apollo-style).

Defaults to false
debugFlagintDebug flags. See docs on error debugging (flag values are the same).
persistedQueryLoadercallableA function which is called to fetch actual query when server encounters a queryId.

The server does not implement persistence part (which you will have to build on your own), but it allows you to execute queries which were persisted previously.

Expected function signature:
function ($queryId, OperationParams $params)

Function is expected to return query string or parsed DocumentNode

Read more about persisted queries.
errorFormattercallableCustom error formatter. See error handling docs.
errorsHandlercallableCustom errors handler. See error handling docs.
promiseAdapterPromiseAdapterRequired for Async PHP only.
+

Using config class

+

If you prefer fluid interface for config with autocomplete in IDE and static time validation, +use GraphQL\Server\ServerConfig instead of an array:

+
use GraphQL\Server\ServerConfig;
+use GraphQL\Server\StandardServer;
+
+$config = ServerConfig::create()
+    ->setSchema($schema)
+    ->setErrorFormatter($myFormatter)
+    ->setDebugFlag($debug)
+;
+
+$server = new StandardServer($config);
+
+

Query batching

+

Standard Server supports query batching (apollo-style).

+

One of the major benefits of Server over a sequence of executeQuery() calls is that +Deferred resolvers won't be isolated in queries. +So for example following batch will require single DB request (if user field is deferred):

+
[
+  {
+    "query": "{user(id: 1) { id }}"
+  },
+  {
+    "query": "{user(id: 2) { id }}"
+  },
+  {
+    "query": "{user(id: 3) { id }}"
+  }
+]
+
+

To enable query batching, pass queryBatching option in server config:

+
use GraphQL\Server\StandardServer;
+
+$server = new StandardServer([
+    'queryBatching' => true
+]);
+
+

Custom Validation Rules

+

Before execution, a query is validated using a set of standard rules defined by the GraphQL spec. +It is possible to override standard set of rules globally or per execution.

+

Add rules globally:

+
use GraphQL\Validator\Rules;
+use GraphQL\Validator\DocumentValidator;
+
+// Add to standard set of rules globally:
+DocumentValidator::addRule(new Rules\DisableIntrospection());
+
+

Custom rules per execution:

+
use GraphQL\GraphQL;
+use GraphQL\Validator\Rules;
+
+$myValidationRules = array_merge(
+    GraphQL::getStandardValidationRules(),
+    [
+        new Rules\QueryComplexity(100),
+        new Rules\DisableIntrospection()
+    ]
+);
+
+$result = GraphQL::executeQuery(
+    $schema,
+    $queryString,
+    $rootValue = null,
+    $context = null,
+    $variableValues = null,
+    $operationName = null,
+    $fieldResolver = null,
+    $myValidationRules // <-- this will override global validation rules for this request
+);
+
+

Or with a standard server:

+
use GraphQL\Server\StandardServer;
+
+$server = new StandardServer([
+    'validationRules' => $myValidationRules
+]);
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/getting-started/index.html b/getting-started/index.html new file mode 100644 index 000000000..13987a3e6 --- /dev/null +++ b/getting-started/index.html @@ -0,0 +1,830 @@ + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Getting Started

+ +

Prerequisites

+

This documentation assumes your familiarity with GraphQL concepts. If it is not the case - +first learn about GraphQL on the official website.

+

Installation

+

Using composer, run:

+
composer require webonyx/graphql-php
+
+

Upgrading

+

We try to keep library releases backwards compatible when possible. +For breaking changes we provide upgrade instructions.

+

Install Tools (optional)

+

While it is possible to communicate with GraphQL API using regular HTTP tools it is way +more convenient for humans to use GraphiQL - an in-browser +IDE for exploring GraphQL APIs.

+

It provides syntax-highlighting, auto-completion and auto-generated documentation for +GraphQL API.

+

The easiest way to use it is to install one of the existing Google Chrome extensions:

+ +

Alternatively, you can follow instructions on the GraphiQL +page and install it locally.

+

Hello World

+

Let's create a type system that will be capable to process the following simple query:

+
query {
+  echo(message: "Hello World")
+}
+
+

We need an object type with the field echo:

+
use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\Type;
+
+$queryType = new ObjectType([
+    'name' => 'Query',
+    'fields' => [
+        'echo' => [
+            'type' => Type::string(),
+            'args' => [
+                'message' => Type::nonNull(Type::string()),
+            ],
+            'resolve' => fn ($rootValue, array $args): string => $rootValue['prefix'] . $args['message'],
+        ],
+    ],
+]);
+
+

(Note: type definition can be expressed in different styles)

+

The interesting piece here is the resolve option of the field definition. It is responsible for returning +a value of our field. Values of scalar fields will be directly included in the response while values of +composite fields (objects, interfaces, unions) will be passed down to nested field resolvers +(not in this example though).

+

Now when our type is ready, let's create a GraphQL endpoint file for it graphql.php:

+
use GraphQL\GraphQL;
+use GraphQL\Type\Schema;
+
+$schema = new Schema([
+    'query' => $queryType
+]);
+
+$rawInput = file_get_contents('php://input');
+$input = json_decode($rawInput, true);
+$query = $input['query'];
+$variableValues = isset($input['variables']) ? $input['variables'] : null;
+
+try {
+    $rootValue = ['prefix' => 'You said: '];
+    $result = GraphQL::executeQuery($schema, $query, $rootValue, null, $variableValues);
+    $output = $result->toArray();
+} catch (\Exception $e) {
+    $output = [
+        'errors' => [
+            [
+                'message' => $e->getMessage()
+            ]
+        ]
+    ];
+}
+header('Content-Type: application/json');
+echo json_encode($output, JSON_THROW_ON_ERROR);
+
+

Our example is finished. Try it by running:

+
php -S localhost:8080 graphql.php
+curl http://localhost:8080 -d '{"query": "query { echo(message: \"Hello World\") }" }'
+
+

Check out the full source code of this example +which also includes simple mutation.

+

Check out the blog example for something +which is closer to real-world apps or read about the details of schema definition.

+

Next Steps

+

Obviously hello world only scratches the surface of what is possible.

+

To learn by example, check out the blog example +which is quite close to real-world GraphQL hierarchies.

+

For a deeper understanding of GraphQL in general, check out concepts.

+

To delve right into the implementation, see schema definition.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..bdd030620 --- /dev/null +++ b/index.html @@ -0,0 +1,765 @@ + + + + + + + + + + + + + + + + + + + + + + graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

graphql-php

+

GitHub stars +CI +Coverage Status +Latest Stable Version +License

+

About GraphQL

+

GraphQL is a modern way to build HTTP APIs consumed by the web and mobile clients. +It is intended to be an alternative to REST and SOAP APIs (even for existing applications).

+

GraphQL itself is a specification designed by Facebook +engineers. Various implementations of this specification were written +in different languages and environments.

+

Great overview of GraphQL features and benefits is presented on the official website. +All of them equally apply to this PHP implementation.

+

About graphql-php

+

graphql-php is a feature-complete implementation of GraphQL specification in PHP. +It was originally inspired by reference JavaScript implementation +published by Facebook.

+

This library is a thin wrapper around your existing data layer and business logic. +It doesn't dictate how these layers are implemented or which storage engines +are used. Instead, it provides tools for creating rich API for your existing app.

+

Library features include:

+ +

Also, several complementary tools are available which provide integrations with +existing PHP frameworks, add support for Relay, etc.

+

Current Status

+

The first version of this library (v0.1) was released on August 10th 2015.

+

The current version supports all features described by GraphQL specification +as well as some experimental features like +schema definition language and +schema printer.

+

Ready for real-world usage.

+

GitHub

+

Project source code is hosted on GitHub.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/schema-definition-language/index.html b/schema-definition-language/index.html new file mode 100644 index 000000000..ea2b7cf37 --- /dev/null +++ b/schema-definition-language/index.html @@ -0,0 +1,754 @@ + + + + + + + + + + + + + + + + + + + + + + + + Schema Definition Language - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Schema Definition Language

+

Since 0.9.0

+

The schema definition language is a convenient way to define your schema, +especially with IDE autocompletion and syntax validation.

+

You can define this separate from your PHP code, e.g. in a schema.graphql file:

+
schema {
+  query: Query
+  mutation: Mutation
+}
+
+type Query {
+  greetings(input: HelloInput!): String!
+}
+
+input HelloInput {
+  firstName: String!
+  lastName: String
+}
+
+

In order to create schema instance out of this file, use +GraphQL\Utils\BuildSchema:

+
use GraphQL\Utils\BuildSchema;
+
+$contents = file_get_contents('schema.graphql');
+$schema = BuildSchema::build($contents);
+
+

By default, such schema is created without any resolvers.

+

We have to rely on default field resolver and root value in +order to execute a query against this schema.

+

Defining resolvers

+

Since 0.10.0

+

In order to enable Interfaces, Unions and custom field resolvers you can pass the second argument: +type config decorator to schema builder.

+

It accepts default type config produced by the builder and is expected to add missing options like +resolveType for interface types or +resolveField for object types.

+
use GraphQL\Utils\BuildSchema;
+use GraphQL\Language\AST\TypeDefinitionNode;
+
+$typeConfigDecorator = function (array $typeConfig, TypeDefinitionNode $typeDefinitionNode): array {
+    $name = $typeConfig['name'];
+    // ... add missing options to $typeConfig based on type $name
+    return $typeConfig;
+};
+
+$contents = file_get_contents('schema.graphql');
+$schema = BuildSchema::build($contents, $typeConfigDecorator);
+
+

Performance considerations

+

Since 0.10.0

+

Method build() produces a lazy schema +automatically, so it works efficiently even with very large schemas.

+

But parsing type definition file on each request is suboptimal, so it is recommended to cache +intermediate parsed representation of the schema for the production environment:

+
use GraphQL\Language\Parser;
+use GraphQL\Utils\BuildSchema;
+use GraphQL\Utils\AST;
+
+$cacheFilename = 'cached_schema.php';
+
+if (!file_exists($cacheFilename)) {
+    $document = Parser::parse(file_get_contents('./schema.graphql'));
+    file_put_contents($cacheFilename, "<?php\nreturn " . var_export(AST::toArray($document), true) . ";\n");
+} else {
+    $document = AST::fromArray(require $cacheFilename); // fromArray() is a lazy operation as well
+}
+
+$typeConfigDecorator = function () {};
+$schema = BuildSchema::build($document, $typeConfigDecorator);
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/schema-definition/index.html b/schema-definition/index.html new file mode 100644 index 000000000..ab7563195 --- /dev/null +++ b/schema-definition/index.html @@ -0,0 +1,1019 @@ + + + + + + + + + + + + + + + + + + + + + + + + Schema Definition - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Schema Definition

+

The schema is a container of your type hierarchy, which accepts root types in a constructor and provides +methods for receiving information about your types to internal GraphQL tools.

+

In graphql-php, the schema is an instance of GraphQL\Type\Schema:

+
use GraphQL\Type\Schema;
+
+$schema = new Schema([
+    'query' => $queryType,
+    'mutation' => $mutationType,
+]);
+
+

See possible constructor options below.

+

Query and Mutation types

+

The schema consists of two root types:

+
    +
  • Query type is a surface of your read API
  • +
  • Mutation type (optional) exposes write API by declaring all possible mutations in your app.
  • +
+

Query and Mutation types are regular object types containing root-level fields +of your API:

+
use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\Type;
+
+$queryType = new ObjectType([
+    'name' => 'Query',
+    'fields' => [
+        'hello' => [
+            'type' => Type::string(),
+            'resolve' => fn () => 'Hello World!',
+        ],
+        'hero' => [
+            'type' => $characterInterface,
+            'args' => [
+                'episode' => [
+                    'type' => $episodeEnum,
+                ],
+            ],
+            'resolve' => fn ($rootValue, array $args): Hero => StarWarsData::getHero($args['episode'] ?? null),
+        ]
+    ]
+]);
+
+$mutationType = new ObjectType([
+    'name' => 'Mutation',
+    'fields' => [
+        'createReview' => [
+            'type' => $createReviewOutput,
+            'args' => [
+                'episode' => Type::nonNull($episodeEnum),
+                'review' => Type::nonNull($reviewInputObject),
+            ],
+            // TODO
+            'resolve' => fn ($rootValue, array $args): Review => StarWarsData::createReview($args['episode'], $args['review']),
+        ]
+    ]
+]);
+
+

Keep in mind that other than the special meaning of declaring a surface area of your API, +those types are the same as any other object type, and their fields work +exactly the same way.

+

Mutation type is also just a regular object type. The difference is in semantics. +Field names of Mutation type are usually verbs and they almost always have arguments - quite often +with complex input values (see Mutations and Input Types for details).

+

Configuration Options

+

The schema constructor expects an instance of GraphQL\Type\SchemaConfig +or an array with the following options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
queryObjectType or callable(): ?ObjectType or nullRequired. Object type (usually named Query) containing root-level fields of your read API
mutationObjectType or callable(): ?ObjectType or nullObject type (usually named Mutation) containing root-level fields of your write API
subscriptionObjectType or callable(): ?ObjectType or nullReserved for future subscriptions implementation. Currently presented for compatibility with introspection query of graphql-js, used by various clients (like Relay or GraphiQL)
directivesarray<Directive>A full list of directives supported by your schema. By default, contains built-in @skip and @include directives.

If you pass your own directives and still want to use built-in directives - add them explicitly. For example:

array_merge(GraphQL::getStandardDirectives(), [$myCustomDirective]);
typesarray<ObjectType>List of object types which cannot be detected by graphql-php during static schema analysis.

Most often this happens when the object type is never referenced in fields directly but is still a part of a schema because it implements an interface which resolves to this object type in its resolveType callable.

Note that you are not required to pass all of your types here - it is simply a workaround for a concrete use-case.
typeLoadercallable(string $name): TypeExpected to return a type instance given the name. Must always return the same instance if called multiple times, see lazy loading. See section below on lazy type loading.
+

Using config class

+

If you prefer a fluid interface for the config with auto-completion in IDE and static time validation, +use GraphQL\Type\SchemaConfig instead of an array:

+
use GraphQL\Type\SchemaConfig;
+use GraphQL\Type\Schema;
+
+$config = SchemaConfig::create()
+    ->setQuery($myQueryType)
+    ->setTypeLoader($myTypeLoader);
+
+$schema = new Schema($config);
+
+

Lazy loading of types

+

If your schema makes use of a large number of complex or dynamically-generated types, they can become a performance concern. +There are a few best practices that can lessen their impact:

+
    +
  1. +

    Use a type registry. + This will put you in a position to implement your own caching and lookup strategies, and GraphQL won't need to preload a map of all known types to do its work.

    +
  2. +
  3. +

    Define each custom type as a callable that returns a type, rather than an object instance. + Then, the work of instantiating them will only happen as they are needed by each query.

    +
  4. +
  5. +

    Define all of your object fields as callbacks. + If you're already doing #2 then this isn't needed, but it's a quick and easy precaution.

    +
  6. +
+

It is recommended to centralize this kind of functionality in a type registry. +A typical example might look like the following:

+
// StoryType.php
+use GraphQL\Type\Definition\ObjectType;
+
+final class StoryType extends ObjectType
+{
+    public function __construct()
+    {
+        parent::__construct([
+            'fields' => static fn (): array => [
+                'author' => [
+                    'type' => Types::author(),
+                    'resolve' => static fn (Story $story): ?Author => DataSource::findUser($story->authorId),
+                ],
+            ],
+        ]);
+    }
+}
+
+// AuthorType.php
+use GraphQL\Type\Definition\ObjectType;
+
+final class AuthorType extends ObjectType
+{
+    public function __construct()
+    {
+        parent::__construct([
+            'description' => 'Writer of books',
+            'fields' => static fn (): array => [
+                'firstName' => Types::string(),
+            ],
+        ]);
+    }
+}
+
+// Types.php
+use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\NamedType;
+
+final class Types
+{
+    /** @var array<string, Type&NamedType> */
+    private static array $types = [];
+
+    /** @return Type&NamedType */
+    public static function load(string $typeName): Type
+    {
+        if (isset(self::$types[$typeName])) {
+            return self::$types[$typeName];
+        }
+
+        // For every type, this class must define a method with the same name
+        // but the first letter is in lower case.
+        $methodName = match ($typeName) {
+            'ID' => 'id',
+            default => lcfirst($typeName),
+        };
+        if (! method_exists(self::class, $methodName)) {
+            throw new \Exception("Unknown GraphQL type: {$typeName}.");
+        }
+
+        $type = self::{$methodName}(); // @phpstan-ignore-line variable static method call
+        if (is_callable($type)) {
+            $type = $type();
+        }
+
+        return self::$types[$typeName] = $type;
+    }
+
+    /** @return Type&NamedType */
+    private static function byClassName(string $className): Type
+    {
+        $classNameParts = explode('\\', $className);
+        $baseClassName = end($classNameParts);
+        // All type classes must use the suffix Type.
+        // This prevents name collisions between types and PHP keywords.
+        $typeName = preg_replace('~Type$~', '', $baseClassName);
+
+        // Type loading is very similar to PHP class loading, but keep in mind
+        // that the **typeLoader** must always return the same instance of a type.
+        // We can enforce that in our type registry by caching known types.
+        return self::$types[$typeName] ??= new $className;
+    }
+
+    /** @return \Closure(): (Type&NamedType) */
+    private static function lazyByClassName(string $className): \Closure
+    {
+        return static fn () => self::byClassName($className);
+    }
+
+    public static function boolean(): ScalarType { return Type::boolean(); }
+    public static function float(): ScalarType { return Type::float(); }
+    public static function id(): ScalarType { return Type::id(); }
+    public static function int(): ScalarType { return Type::int(); }
+    public static function string(): ScalarType { return Type::string(); }
+    public static function author(): callable { return self::lazyByClassName(AuthorType::class); }
+    public static function story(): callable { return self::lazyByClassName(StoryType::class); }
+    ...
+}
+
+// api/index.php
+use GraphQL\Type\Definition\ObjectType;
+
+$schema = new Schema([
+    'query' => new ObjectType([
+        'name' => 'Query',
+        'fields' => static fn() => [
+            'story' => [
+                'args'=>[
+                  'id' => Types::int(),
+                ],
+                'type' => Types::story(),
+                'description' => 'Returns my A',
+                'resolve' => static fn ($rootValue, array $args): ?Story => DataSource::findStory($args['id']),
+            ],
+        ],
+    ]),
+    'typeLoader' => Types::load(...),
+]);
+
+

A working demonstration of this kind of architecture can be found in the 01-blog sample.

+

Schema Validation

+

By default, the schema is created with only shallow validation of type and field definitions
+(because validation requires a full schema scan and is very costly on bigger schemas).

+

There is a special method assertValid() on the schema instance which throws +GraphQL\Error\InvariantViolation exception when it encounters any error, like:

+
    +
  • Invalid types used for fields/arguments
  • +
  • Missing interface implementations
  • +
  • Invalid interface implementations
  • +
  • Other schema errors...
  • +
+

Schema validation is supposed to be used in CLI commands or during a build step of your app. +Don't call it in web requests in production.

+

Usage example:

+
try {
+    $schema = new GraphQL\Type\Schema([
+        'query' => $myQueryType
+    ]);
+    $schema->assertValid();
+} catch (GraphQL\Error\InvariantViolation $e) {
+    echo $e->getMessage();
+}
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 000000000..41644502a --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"graphql-php","text":""},{"location":"#about-graphql","title":"About GraphQL","text":"

GraphQL is a modern way to build HTTP APIs consumed by the web and mobile clients. It is intended to be an alternative to REST and SOAP APIs (even for existing applications).

GraphQL itself is a specification designed by Facebook engineers. Various implementations of this specification were written in different languages and environments.

Great overview of GraphQL features and benefits is presented on the official website. All of them equally apply to this PHP implementation.

"},{"location":"#about-graphql-php","title":"About graphql-php","text":"

graphql-php is a feature-complete implementation of GraphQL specification in PHP. It was originally inspired by reference JavaScript implementation published by Facebook.

This library is a thin wrapper around your existing data layer and business logic. It doesn't dictate how these layers are implemented or which storage engines are used. Instead, it provides tools for creating rich API for your existing app.

Library features include:

  • Primitives to express your app as a Type System
  • Validation and introspection of this Type System (for compatibility with tools like GraphiQL)
  • Parsing, validating and executing GraphQL queries against this Type System
  • Rich error reporting, including query validation and execution errors
  • Optional tools for parsing schema definition language
  • Tools for batching requests to backend storage
  • Async PHP platforms support via promises
  • Standard HTTP server

Also, several complementary tools are available which provide integrations with existing PHP frameworks, add support for Relay, etc.

"},{"location":"#current-status","title":"Current Status","text":"

The first version of this library (v0.1) was released on August 10th 2015.

The current version supports all features described by GraphQL specification as well as some experimental features like schema definition language and schema printer.

Ready for real-world usage.

"},{"location":"#github","title":"GitHub","text":"

Project source code is hosted on GitHub.

"},{"location":"class-reference/","title":"Class Reference","text":""},{"location":"class-reference/#graphqlgraphql","title":"GraphQL\\GraphQL","text":"

This is the primary facade for fulfilling GraphQL operations. See related documentation.

@phpstan-import-type FieldResolver from Executor

@see \\GraphQL\\Tests\\GraphQLTest

"},{"location":"class-reference/#graphqlgraphql-methods","title":"GraphQL\\GraphQL Methods","text":"
/**\n * Executes graphql query.\n *\n * More sophisticated GraphQL servers, such as those which persist queries,\n * may wish to separate the validation and execution phases to a static time\n * tooling step, and a server runtime step.\n *\n * Available options:\n *\n * schema:\n *    The GraphQL type system to use when validating and executing a query.\n * source:\n *    A GraphQL language formatted string representing the requested operation.\n * rootValue:\n *    The value provided as the first argument to resolver functions on the top\n *    level type (e.g. the query object type).\n * contextValue:\n *    The context value is provided as an argument to resolver functions after\n *    field arguments. It is used to pass shared information useful at any point\n *    during executing this query, for example the currently logged in user and\n *    connections to databases or other services.\n *    If the passed object implements the `ScopedContext` interface,\n *    its `clone()` method will be called before passing the context down to a field.\n *    This allows passing information to child fields in the query tree without affecting sibling or parent fields.\n * variableValues:\n *    A mapping of variable name to runtime value to use for all variables\n *    defined in the requestString.\n * operationName:\n *    The name of the operation to use if requestString contains multiple\n *    possible operations. Can be omitted if requestString contains only\n *    one operation.\n * fieldResolver:\n *    A resolver function to use when one is not provided by the schema.\n *    If not provided, the default field resolver is used (which looks for a\n *    value on the source value with the field's name).\n * validationRules:\n *    A set of rules for query validation step. Default value is all available rules.\n *    Empty array would allow to skip query validation (may be convenient for persisted\n *    queries which are validated before persisting and assumed valid during execution)\n *\n * @param string|DocumentNode $source\n * @param mixed $rootValue\n * @param mixed $contextValue\n * @param array<string, mixed>|null $variableValues\n * @param array<ValidationRule>|null $validationRules\n *\n * @api\n *\n * @throws \\Exception\n * @throws InvariantViolation\n */\nstatic function executeQuery(\n    GraphQL\\Type\\Schema $schema,\n    $source,\n    $rootValue = null,\n    $contextValue = null,\n    ?array $variableValues = null,\n    ?string $operationName = null,\n    ?callable $fieldResolver = null,\n    ?array $validationRules = null\n): GraphQL\\Executor\\ExecutionResult\n
/**\n * Same as executeQuery(), but requires PromiseAdapter and always returns a Promise.\n * Useful for Async PHP platforms.\n *\n * @param string|DocumentNode $source\n * @param mixed $rootValue\n * @param mixed $context\n * @param array<string, mixed>|null $variableValues\n * @param array<ValidationRule>|null $validationRules Defaults to using all available rules\n *\n * @api\n *\n * @throws \\Exception\n */\nstatic function promiseToExecute(\n    GraphQL\\Executor\\Promise\\PromiseAdapter $promiseAdapter,\n    GraphQL\\Type\\Schema $schema,\n    $source,\n    $rootValue = null,\n    $context = null,\n    ?array $variableValues = null,\n    ?string $operationName = null,\n    ?callable $fieldResolver = null,\n    ?array $validationRules = null\n): GraphQL\\Executor\\Promise\\Promise\n
/**\n * Returns directives defined in GraphQL spec.\n *\n * @throws InvariantViolation\n *\n * @return array<string, Directive>\n *\n * @api\n */\nstatic function getStandardDirectives(): array\n
/**\n * Returns types defined in GraphQL spec.\n *\n * @throws InvariantViolation\n *\n * @return array<string, ScalarType>\n *\n * @api\n */\nstatic function getStandardTypes(): array\n
/**\n * Replaces standard types with types from this list (matching by name).\n *\n * Standard types not listed here remain untouched.\n *\n * @param array<string, ScalarType> $types\n *\n * @api\n *\n * @throws InvariantViolation\n */\nstatic function overrideStandardTypes(array $types): void\n
/**\n * Returns standard validation rules implementing GraphQL spec.\n *\n * @return array<class-string<ValidationRule>, ValidationRule>\n *\n * @api\n */\nstatic function getStandardValidationRules(): array\n
/**\n * Set default resolver implementation.\n *\n * @phpstan-param FieldResolver $fn\n *\n * @api\n */\nstatic function setDefaultFieldResolver(callable $fn): void\n
"},{"location":"class-reference/#graphqltypedefinitiontype","title":"GraphQL\\Type\\Definition\\Type","text":"

Registry of standard GraphQL types and base class for all other types.

"},{"location":"class-reference/#graphqltypedefinitiontype-methods","title":"GraphQL\\Type\\Definition\\Type Methods","text":"
/**\n * @api\n *\n * @throws InvariantViolation\n */\nstatic function int(): GraphQL\\Type\\Definition\\ScalarType\n
/**\n * @api\n *\n * @throws InvariantViolation\n */\nstatic function float(): GraphQL\\Type\\Definition\\ScalarType\n
/**\n * @api\n *\n * @throws InvariantViolation\n */\nstatic function string(): GraphQL\\Type\\Definition\\ScalarType\n
/**\n * @api\n *\n * @throws InvariantViolation\n */\nstatic function boolean(): GraphQL\\Type\\Definition\\ScalarType\n
/**\n * @api\n *\n * @throws InvariantViolation\n */\nstatic function id(): GraphQL\\Type\\Definition\\ScalarType\n
/**\n * @template T of Type\n *\n * @param T|callable():T $type\n *\n * @return ListOfType<T>\n *\n * @api\n */\nstatic function listOf($type): GraphQL\\Type\\Definition\\ListOfType\n
/**\n * @param (NullableType&Type)|callable():(NullableType&Type) $type\n *\n * @api\n */\nstatic function nonNull($type): GraphQL\\Type\\Definition\\NonNull\n
/**\n * @param mixed $type\n *\n * @api\n */\nstatic function isInputType($type): bool\n
/**\n * @return (Type&NamedType)|null\n *\n * @api\n */\nstatic function getNamedType(?GraphQL\\Type\\Definition\\Type $type): ?GraphQL\\Type\\Definition\\Type\n
/**\n * @param mixed $type\n *\n * @api\n */\nstatic function isOutputType($type): bool\n
/**\n * @param mixed $type\n *\n * @api\n */\nstatic function isLeafType($type): bool\n
/**\n * @param mixed $type\n *\n * @api\n */\nstatic function isCompositeType($type): bool\n
/**\n * @param mixed $type\n *\n * @api\n */\nstatic function isAbstractType($type): bool\n
/**\n * @return Type&NullableType\n *\n * @api\n */\nstatic function getNullableType(GraphQL\\Type\\Definition\\Type $type): GraphQL\\Type\\Definition\\Type\n
"},{"location":"class-reference/#graphqltypedefinitionresolveinfo","title":"GraphQL\\Type\\Definition\\ResolveInfo","text":"

Structure containing information useful for field resolution process.

Passed as 4th argument to every field resolver. See docs on field resolving (data fetching).

@phpstan-import-type QueryPlanOptions from QueryPlan

@phpstan-type Path list"},{"location":"class-reference/#graphqltypedefinitionresolveinfo-props","title":"GraphQL\\Type\\Definition\\ResolveInfo Props","text":"

/**\n * The definition of the field being resolved.\n *\n * @api\n */\npublic $fieldDefinition;\n\n/**\n * The name of the field being resolved.\n *\n * @api\n */\npublic $fieldName;\n\n/**\n * Expected return type of the field being resolved.\n *\n * @api\n */\npublic $returnType;\n\n/**\n * AST of all nodes referencing this field in the query.\n *\n * @api\n *\n * @var \\ArrayObject<int, FieldNode>\n */\npublic $fieldNodes;\n\n/**\n * Parent type of the field being resolved.\n *\n * @api\n */\npublic $parentType;\n\n/**\n * Path to this field from the very root value. When fields are aliased, the path includes aliases.\n *\n * @api\n *\n * @var list<string|int>\n *\n * @phpstan-var Path\n */\npublic $path;\n\n/**\n * Path to this field from the very root value. This will never include aliases.\n *\n * @api\n *\n * @var list<string|int>\n *\n * @phpstan-var Path\n */\npublic $unaliasedPath;\n\n/**\n * Instance of a schema used for execution.\n *\n * @api\n */\npublic $schema;\n\n/**\n * AST of all fragments defined in query.\n *\n * @api\n *\n * @var array<string, FragmentDefinitionNode>\n */\npublic $fragments;\n\n/**\n * Root value passed to query execution.\n *\n * @api\n *\n * @var mixed\n */\npublic $rootValue;\n\n/**\n * AST of operation definition node (query, mutation).\n *\n * @api\n */\npublic $operation;\n\n/**\n * Array of variables passed to query execution.\n *\n * @api\n *\n * @var array<string, mixed>\n */\npublic $variableValues;\n
"},{"location":"class-reference/#graphqltypedefinitionresolveinfo-methods","title":"GraphQL\\Type\\Definition\\ResolveInfo Methods","text":"
/**\n * Helper method that returns names of all fields selected in query for\n * $this->fieldName up to $depth levels.\n *\n * Example:\n * query MyQuery{\n * {\n *   root {\n *     id,\n *     nested {\n *      nested1\n *      nested2 {\n *        nested3\n *      }\n *     }\n *   }\n * }\n *\n * Given this ResolveInfo instance is a part of \"root\" field resolution, and $depth === 1,\n * method will return:\n * [\n *     'id' => true,\n *     'nested' => [\n *         nested1 => true,\n *         nested2 => true\n *     ]\n * ]\n *\n * Warning: this method it is a naive implementation which does not take into account\n * conditional typed fragments. So use it with care for fields of interface and union types.\n *\n * @param int $depth How many levels to include in output\n *\n * @return array<string, mixed>\n *\n * @api\n */\nfunction getFieldSelection(int $depth = 0): array\n
"},{"location":"class-reference/#graphqllanguagedirectivelocation","title":"GraphQL\\Language\\DirectiveLocation","text":"

Enumeration of available directive locations.

"},{"location":"class-reference/#graphqllanguagedirectivelocation-constants","title":"GraphQL\\Language\\DirectiveLocation Constants","text":"
const QUERY = 'QUERY';\nconst MUTATION = 'MUTATION';\nconst SUBSCRIPTION = 'SUBSCRIPTION';\nconst FIELD = 'FIELD';\nconst FRAGMENT_DEFINITION = 'FRAGMENT_DEFINITION';\nconst FRAGMENT_SPREAD = 'FRAGMENT_SPREAD';\nconst INLINE_FRAGMENT = 'INLINE_FRAGMENT';\nconst VARIABLE_DEFINITION = 'VARIABLE_DEFINITION';\nconst EXECUTABLE_LOCATIONS = [\n    'QUERY' => 'QUERY',\n    'MUTATION' => 'MUTATION',\n    'SUBSCRIPTION' => 'SUBSCRIPTION',\n    'FIELD' => 'FIELD',\n    'FRAGMENT_DEFINITION' => 'FRAGMENT_DEFINITION',\n    'FRAGMENT_SPREAD' => 'FRAGMENT_SPREAD',\n    'INLINE_FRAGMENT' => 'INLINE_FRAGMENT',\n    'VARIABLE_DEFINITION' => 'VARIABLE_DEFINITION',\n];\nconst SCHEMA = 'SCHEMA';\nconst SCALAR = 'SCALAR';\nconst OBJECT = 'OBJECT';\nconst FIELD_DEFINITION = 'FIELD_DEFINITION';\nconst ARGUMENT_DEFINITION = 'ARGUMENT_DEFINITION';\nconst IFACE = 'INTERFACE';\nconst UNION = 'UNION';\nconst ENUM = 'ENUM';\nconst ENUM_VALUE = 'ENUM_VALUE';\nconst INPUT_OBJECT = 'INPUT_OBJECT';\nconst INPUT_FIELD_DEFINITION = 'INPUT_FIELD_DEFINITION';\nconst TYPE_SYSTEM_LOCATIONS = [\n    'SCHEMA' => 'SCHEMA',\n    'SCALAR' => 'SCALAR',\n    'OBJECT' => 'OBJECT',\n    'FIELD_DEFINITION' => 'FIELD_DEFINITION',\n    'ARGUMENT_DEFINITION' => 'ARGUMENT_DEFINITION',\n    'INTERFACE' => 'INTERFACE',\n    'UNION' => 'UNION',\n    'ENUM' => 'ENUM',\n    'ENUM_VALUE' => 'ENUM_VALUE',\n    'INPUT_OBJECT' => 'INPUT_OBJECT',\n    'INPUT_FIELD_DEFINITION' => 'INPUT_FIELD_DEFINITION',\n];\nconst LOCATIONS = [\n    'QUERY' => 'QUERY',\n    'MUTATION' => 'MUTATION',\n    'SUBSCRIPTION' => 'SUBSCRIPTION',\n    'FIELD' => 'FIELD',\n    'FRAGMENT_DEFINITION' => 'FRAGMENT_DEFINITION',\n    'FRAGMENT_SPREAD' => 'FRAGMENT_SPREAD',\n    'INLINE_FRAGMENT' => 'INLINE_FRAGMENT',\n    'VARIABLE_DEFINITION' => 'VARIABLE_DEFINITION',\n    'SCHEMA' => 'SCHEMA',\n    'SCALAR' => 'SCALAR',\n    'OBJECT' => 'OBJECT',\n    'FIELD_DEFINITION' => 'FIELD_DEFINITION',\n    'ARGUMENT_DEFINITION' => 'ARGUMENT_DEFINITION',\n    'INTERFACE' => 'INTERFACE',\n    'UNION' => 'UNION',\n    'ENUM' => 'ENUM',\n    'ENUM_VALUE' => 'ENUM_VALUE',\n    'INPUT_OBJECT' => 'INPUT_OBJECT',\n    'INPUT_FIELD_DEFINITION' => 'INPUT_FIELD_DEFINITION',\n];\n
"},{"location":"class-reference/#graphqltypeschemaconfig","title":"GraphQL\\Type\\SchemaConfig","text":"

Configuration options for schema construction.

The options accepted by the create method are described in the schema definition docs.

Usage example:

$config = SchemaConfig::create()\n    ->setQuery($myQueryType)\n    ->setTypeLoader($myTypeLoader);\n\n$schema = new Schema($config);\n

@see Type, NamedType

@phpstan-type MaybeLazyObjectType ObjectType|(callable(): (ObjectType|null))|null @phpstan-type TypeLoader callable(string $typeName): ((Type&NamedType)|null) @phpstan-type Types iterable|(callable(): iterable)|iterable<(callable(): Type&NamedType)>|(callable(): iterable<(callable(): Type&NamedType)>) @phpstan-type SchemaConfigOptions array{ query?: MaybeLazyObjectType, mutation?: MaybeLazyObjectType, subscription?: MaybeLazyObjectType, types?: Types|null, directives?: array|null, typeLoader?: TypeLoader|null, assumeValid?: bool|null, astNode?: SchemaDefinitionNode|null, extensionASTNodes?: array|null, }"},{"location":"class-reference/#graphqltypeschemaconfig-methods","title":"GraphQL\\Type\\SchemaConfig Methods","text":"

/**\n * Converts an array of options to instance of SchemaConfig\n * (or just returns empty config when array is not passed).\n *\n * @phpstan-param SchemaConfigOptions $options\n *\n * @throws InvariantViolation\n *\n * @api\n */\nstatic function create(array $options = []): self\n
/**\n * @return MaybeLazyObjectType\n *\n * @api\n */\nfunction getQuery()\n
/**\n * @param MaybeLazyObjectType $query\n *\n * @throws InvariantViolation\n *\n * @api\n */\nfunction setQuery($query): self\n
/**\n * @return MaybeLazyObjectType\n *\n * @api\n */\nfunction getMutation()\n
/**\n * @param MaybeLazyObjectType $mutation\n *\n * @throws InvariantViolation\n *\n * @api\n */\nfunction setMutation($mutation): self\n
/**\n * @return MaybeLazyObjectType\n *\n * @api\n */\nfunction getSubscription()\n
/**\n * @param MaybeLazyObjectType $subscription\n *\n * @throws InvariantViolation\n *\n * @api\n */\nfunction setSubscription($subscription): self\n
/**\n * @return array|callable\n *\n * @phpstan-return Types\n *\n * @api\n */\nfunction getTypes()\n
/**\n * @param array|callable $types\n *\n * @phpstan-param Types $types\n *\n * @api\n */\nfunction setTypes($types): self\n
/**\n * @return array<Directive>|null\n *\n * @api\n */\nfunction getDirectives(): ?array\n
/**\n * @param array<Directive>|null $directives\n *\n * @api\n */\nfunction setDirectives(?array $directives): self\n
/**\n * @return callable|null $typeLoader\n *\n * @phpstan-return TypeLoader|null $typeLoader\n *\n * @api\n */\nfunction getTypeLoader(): ?callable\n
/**\n * @phpstan-param TypeLoader|null $typeLoader\n *\n * @api\n */\nfunction setTypeLoader(?callable $typeLoader): self\n
"},{"location":"class-reference/#graphqltypeschema","title":"GraphQL\\Type\\Schema","text":"

Schema Definition (see schema definition docs).

A Schema is created by supplying the root types of each type of operation: query, mutation (optional) and subscription (optional). A schema definition is then supplied to the validator and executor. Usage Example:

$schema = new GraphQL\\Type\\Schema([\n  'query' => $MyAppQueryRootType,\n  'mutation' => $MyAppMutationRootType,\n]);\n

Or using Schema Config instance:

$config = GraphQL\\Type\\SchemaConfig::create()\n    ->setQuery($MyAppQueryRootType)\n    ->setMutation($MyAppMutationRootType);\n\n$schema = new GraphQL\\Type\\Schema($config);\n

@phpstan-import-type SchemaConfigOptions from SchemaConfig @phpstan-import-type OperationType from OperationDefinitionNode

@see \\GraphQL\\Tests\\Type\\SchemaTest

"},{"location":"class-reference/#graphqltypeschema-methods","title":"GraphQL\\Type\\Schema Methods","text":"
/**\n * @param SchemaConfig|array<string, mixed> $config\n *\n * @phpstan-param SchemaConfig|SchemaConfigOptions $config\n *\n * @throws InvariantViolation\n *\n * @api\n */\nfunction __construct($config)\n
/**\n * Returns all types in this schema.\n *\n * This operation requires a full schema scan. Do not use in production environment.\n *\n * @throws InvariantViolation\n *\n * @return array<string, Type&NamedType> Keys represent type names, values are instances of corresponding type definitions\n *\n * @api\n */\nfunction getTypeMap(): array\n
/**\n * Returns a list of directives supported by this schema.\n *\n * @throws InvariantViolation\n *\n * @return array<Directive>\n *\n * @api\n */\nfunction getDirectives(): array\n
/**\n * Returns root query type.\n *\n * @api\n */\nfunction getQueryType(): ?GraphQL\\Type\\Definition\\ObjectType\n
/**\n * Returns root mutation type.\n *\n * @api\n */\nfunction getMutationType(): ?GraphQL\\Type\\Definition\\ObjectType\n
/**\n * Returns schema subscription.\n *\n * @api\n */\nfunction getSubscriptionType(): ?GraphQL\\Type\\Definition\\ObjectType\n
/**\n * Returns a type by name.\n *\n * @throws InvariantViolation\n *\n * @return (Type&NamedType)|null\n *\n * @api\n */\nfunction getType(string $name): ?GraphQL\\Type\\Definition\\Type\n
/**\n * Returns all possible concrete types for given abstract type\n * (implementations for interfaces and members of union type for unions).\n *\n * This operation requires full schema scan. Do not use in production environment.\n *\n * @param AbstractType&Type $abstractType\n *\n * @throws InvariantViolation\n *\n * @return array<ObjectType>\n *\n * @api\n */\nfunction getPossibleTypes(GraphQL\\Type\\Definition\\AbstractType $abstractType): array\n
/**\n * Returns all types that implement a given interface type.\n *\n * This operation requires full schema scan. Do not use in production environment.\n *\n * @api\n *\n * @throws InvariantViolation\n */\nfunction getImplementations(GraphQL\\Type\\Definition\\InterfaceType $abstractType): GraphQL\\Utils\\InterfaceImplementations\n
/**\n * Returns true if the given type is a sub type of the given abstract type.\n *\n * @param AbstractType&Type $abstractType\n * @param ImplementingType&Type $maybeSubType\n *\n * @api\n *\n * @throws InvariantViolation\n */\nfunction isSubType(\n    GraphQL\\Type\\Definition\\AbstractType $abstractType,\n    GraphQL\\Type\\Definition\\ImplementingType $maybeSubType\n): bool\n
/**\n * Returns instance of directive by name.\n *\n * @api\n *\n * @throws InvariantViolation\n */\nfunction getDirective(string $name): ?GraphQL\\Type\\Definition\\Directive\n
/**\n * Throws if the schema is not valid.\n *\n * This operation requires a full schema scan. Do not use in production environment.\n *\n * @throws Error\n * @throws InvariantViolation\n *\n * @api\n */\nfunction assertValid(): void\n
/**\n * Validate the schema and return any errors.\n *\n * This operation requires a full schema scan. Do not use in production environment.\n *\n * @throws InvariantViolation\n *\n * @return array<int, Error>\n *\n * @api\n */\nfunction validate(): array\n
"},{"location":"class-reference/#graphqllanguageparser","title":"GraphQL\\Language\\Parser","text":"

Parses string containing GraphQL query language or schema definition language to Abstract Syntax Tree.

@phpstan-type ParserOptions array{ noLocation?: bool, allowLegacySDLEmptyFields?: bool, allowLegacySDLImplementsInterfaces?: bool, experimentalFragmentVariables?: bool }

noLocation: (By default, the parser creates AST nodes that know the location in the source that they correspond to. This configuration flag disables that behavior for performance or testing.)

allowLegacySDLEmptyFields: If enabled, the parser will parse empty fields sets in the Schema Definition Language. Otherwise, the parser will follow the current specification.

This option is provided to ease adoption of the final SDL specification and will be removed in a future major release.

allowLegacySDLImplementsInterfaces: If enabled, the parser will parse implemented interfaces with no & character between each interface. Otherwise, the parser will follow the current specification.

This option is provided to ease adoption of the final SDL specification and will be removed in a future major release.

experimentalFragmentVariables: (If enabled, the parser will understand and parse variable definitions contained in a fragment definition. They'll be represented in the variableDefinitions field of the FragmentDefinitionNode.

The syntax is identical to normal, query-defined variables. For example:

fragment A($var: Boolean = false) on T  {\n  ...\n}\n

Note: this feature is experimental and may change or be removed in the future.) Those magic functions allow partial parsing:

@method static NameNode name(Source|string $source, bool[] $options = []) @method static ExecutableDefinitionNode|TypeSystemDefinitionNode definition(Source|string $source, bool[] $options = []) @method static ExecutableDefinitionNode executableDefinition(Source|string $source, bool[] $options = []) @method static OperationDefinitionNode operationDefinition(Source|string $source, bool[] $options = []) @method static string operationType(Source|string $source, bool[] $options = []) @method static NodeList variableDefinitions(Source|string $source, bool[] $options = []) @method static VariableDefinitionNode variableDefinition(Source|string $source, bool[] $options = []) @method static VariableNode variable(Source|string $source, bool[] $options = []) @method static SelectionSetNode selectionSet(Source|string $source, bool[] $options = []) @method static mixed selection(Source|string $source, bool[] $options = []) @method static FieldNode field(Source|string $source, bool[] $options = []) @method static NodeList arguments(Source|string $source, bool[] $options = []) @method static NodeList constArguments(Source|string $source, bool[] $options = []) @method static ArgumentNode argument(Source|string $source, bool[] $options = []) @method static ArgumentNode constArgument(Source|string $source, bool[] $options = []) @method static FragmentSpreadNode|InlineFragmentNode fragment(Source|string $source, bool[] $options = []) @method static FragmentDefinitionNode fragmentDefinition(Source|string $source, bool[] $options = []) @method static NameNode fragmentName(Source|string $source, bool[] $options = []) @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode|VariableNode valueLiteral(Source|string $source, bool[] $options = []) @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode constValueLiteral(Source|string $source, bool[] $options = []) @method static StringValueNode stringLiteral(Source|string $source, bool[] $options = []) @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|StringValueNode constValue(Source|string $source, bool[] $options = []) @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|ObjectValueNode|StringValueNode|VariableNode variableValue(Source|string $source, bool[] $options = []) @method static ListValueNode array(Source|string $source, bool[] $options = []) @method static ListValueNode constArray(Source|string $source, bool[] $options = []) @method static ObjectValueNode object(Source|string $source, bool[] $options = []) @method static ObjectValueNode constObject(Source|string $source, bool[] $options = []) @method static ObjectFieldNode objectField(Source|string $source, bool[] $options = []) @method static ObjectFieldNode constObjectField(Source|string $source, bool[] $options = []) @method static NodeList directives(Source|string $source, bool[] $options = []) @method static NodeList constDirectives(Source|string $source, bool[] $options = []) @method static DirectiveNode directive(Source|string $source, bool[] $options = []) @method static DirectiveNode constDirective(Source|string $source, bool[] $options = []) @method static ListTypeNode|NamedTypeNode|NonNullTypeNode typeReference(Source|string $source, bool[] $options = []) @method static NamedTypeNode namedType(Source|string $source, bool[] $options = []) @method static TypeSystemDefinitionNode typeSystemDefinition(Source|string $source, bool[] $options = []) @method static StringValueNode|null description(Source|string $source, bool[] $options = []) @method static SchemaDefinitionNode schemaDefinition(Source|string $source, bool[] $options = []) @method static OperationTypeDefinitionNode operationTypeDefinition(Source|string $source, bool[] $options = []) @method static ScalarTypeDefinitionNode scalarTypeDefinition(Source|string $source, bool[] $options = []) @method static ObjectTypeDefinitionNode objectTypeDefinition(Source|string $source, bool[] $options = []) @method static NodeList implementsInterfaces(Source|string $source, bool[] $options = []) @method static NodeList fieldsDefinition(Source|string $source, bool[] $options = []) @method static FieldDefinitionNode fieldDefinition(Source|string $source, bool[] $options = []) @method static NodeList argumentsDefinition(Source|string $source, bool[] $options = []) @method static InputValueDefinitionNode inputValueDefinition(Source|string $source, bool[] $options = []) @method static InterfaceTypeDefinitionNode interfaceTypeDefinition(Source|string $source, bool[] $options = []) @method static UnionTypeDefinitionNode unionTypeDefinition(Source|string $source, bool[] $options = []) @method static NodeList unionMemberTypes(Source|string $source, bool[] $options = []) @method static EnumTypeDefinitionNode enumTypeDefinition(Source|string $source, bool[] $options = []) @method static NodeList enumValuesDefinition(Source|string $source, bool[] $options = []) @method static EnumValueDefinitionNode enumValueDefinition(Source|string $source, bool[] $options = []) @method static InputObjectTypeDefinitionNode inputObjectTypeDefinition(Source|string $source, bool[] $options = []) @method static NodeList inputFieldsDefinition(Source|string $source, bool[] $options = []) @method static TypeExtensionNode typeExtension(Source|string $source, bool[] $options = []) @method static SchemaExtensionNode schemaTypeExtension(Source|string $source, bool[] $options = []) @method static ScalarTypeExtensionNode scalarTypeExtension(Source|string $source, bool[] $options = []) @method static ObjectTypeExtensionNode objectTypeExtension(Source|string $source, bool[] $options = []) @method static InterfaceTypeExtensionNode interfaceTypeExtension(Source|string $source, bool[] $options = []) @method static UnionTypeExtensionNode unionTypeExtension(Source|string $source, bool[] $options = []) @method static EnumTypeExtensionNode enumTypeExtension(Source|string $source, bool[] $options = []) @method static InputObjectTypeExtensionNode inputObjectTypeExtension(Source|string $source, bool[] $options = []) @method static DirectiveDefinitionNode directiveDefinition(Source|string $source, bool[] $options = []) @method static NodeList directiveLocations(Source|string $source, bool[] $options = []) @method static NameNode directiveLocation(Source|string $source, bool[] $options = [])

@see \\GraphQL\\Tests\\Language\\ParserTest

"},{"location":"class-reference/#graphqllanguageparser-methods","title":"GraphQL\\Language\\Parser Methods","text":"
/**\n * Given a GraphQL source, parses it into a `GraphQL\\Language\\AST\\DocumentNode`.\n *\n * Throws `GraphQL\\Error\\SyntaxError` if a syntax error is encountered.\n *\n * @param Source|string $source\n *\n * @phpstan-param ParserOptions $options\n *\n * @api\n *\n * @throws \\JsonException\n * @throws SyntaxError\n */\nstatic function parse($source, array $options = []): GraphQL\\Language\\AST\\DocumentNode\n
/**\n * Given a string containing a GraphQL value (ex. `[42]`), parse the AST for that value.\n *\n * Throws `GraphQL\\Error\\SyntaxError` if a syntax error is encountered.\n *\n * This is useful within tools that operate upon GraphQL Values directly and\n * in isolation of complete GraphQL documents.\n *\n * Consider providing the results to the utility function: `GraphQL\\Utils\\AST::valueFromAST()`.\n *\n * @param Source|string $source\n *\n * @phpstan-param ParserOptions $options\n *\n * @throws \\JsonException\n * @throws SyntaxError\n *\n * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode|VariableNode\n *\n * @api\n */\nstatic function parseValue($source, array $options = [])\n
/**\n * Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for that type.\n *\n * Throws `GraphQL\\Error\\SyntaxError` if a syntax error is encountered.\n *\n * This is useful within tools that operate upon GraphQL Types directly and\n * in isolation of complete GraphQL documents.\n *\n * Consider providing the results to the utility function: `GraphQL\\Utils\\AST::typeFromAST()`.\n *\n * @param Source|string $source\n *\n * @phpstan-param ParserOptions $options\n *\n * @throws \\JsonException\n * @throws SyntaxError\n *\n * @return ListTypeNode|NamedTypeNode|NonNullTypeNode\n *\n * @api\n */\nstatic function parseType($source, array $options = [])\n
"},{"location":"class-reference/#graphqllanguageprinter","title":"GraphQL\\Language\\Printer","text":"

Prints AST to string. Capable of printing GraphQL queries and Type definition language. Useful for pretty-printing queries or printing back AST for logging, documentation, etc.

Usage example:

$query = 'query myQuery {someField}';\n$ast = GraphQL\\Language\\Parser::parse($query);\n$printed = GraphQL\\Language\\Printer::doPrint($ast);\n

@see \\GraphQL\\Tests\\Language\\PrinterTest

"},{"location":"class-reference/#graphqllanguageprinter-methods","title":"GraphQL\\Language\\Printer Methods","text":"
/**\n * Converts the AST of a GraphQL node to a string.\n *\n * Handles both executable definitions and schema definitions.\n *\n * @api\n */\nstatic function doPrint(GraphQL\\Language\\AST\\Node $ast): string\n
"},{"location":"class-reference/#graphqllanguagevisitor","title":"GraphQL\\Language\\Visitor","text":"

Utility for efficient AST traversal and modification.

visit() will walk through an AST using a depth first traversal, calling the visitor's enter function at each node in the traversal, and calling the leave function after visiting that node and all of its child nodes.

By returning different values from the enter and leave functions, the behavior of the visitor can be altered.

  • no return (void) or return null: no action
  • Visitor::skipNode(): skips over the subtree at the current node of the AST
  • Visitor::stop(): stop the Visitor completely
  • Visitor::removeNode(): remove the current node
  • return any other value: replace this node with the returned value

When using visit() to edit an AST, the original AST will not be modified, and a new version of the AST with the changes applied will be returned from the visit function.

$editedAST = Visitor::visit($ast, [ 'enter' => function ($node, $key, $parent, $path, $ancestors) { // ... }, 'leave' => function ($node, $key, $parent, $path, $ancestors) { // ... } ]);

Alternatively to providing enter and leave functions, a visitor can instead provide functions named the same as the kinds of AST nodes, or enter/leave visitors at a named key, leading to four permutations of visitor API:

  1. Named visitors triggered when entering a node a specific kind.

Visitor::visit($ast, [ 'Kind' => function ($node) { // enter the \"Kind\" node } ]);

  1. Named visitors that trigger upon entering and leaving a node of a specific kind.

Visitor::visit($ast, [ 'Kind' => [ 'enter' => function ($node) { // enter the \"Kind\" node } 'leave' => function ($node) { // leave the \"Kind\" node } ] ]);

  1. Generic visitors that trigger upon entering and leaving any node.

Visitor::visit($ast, [ 'enter' => function ($node) { // enter any node }, 'leave' => function ($node) { // leave any node } ]);

  1. Parallel visitors for entering and leaving nodes of a specific kind.

Visitor::visit($ast, [ 'enter' => [ 'Kind' => function($node) { // enter the \"Kind\" node } }, 'leave' => [ 'Kind' => function ($node) { // leave the \"Kind\" node } ] ]);

@phpstan-type NodeVisitor callable(Node): (VisitorOperation|null|false|void) @phpstan-type VisitorArray array|array>

@see \\GraphQL\\Tests\\Language\\VisitorTest

"},{"location":"class-reference/#graphqllanguagevisitor-methods","title":"GraphQL\\Language\\Visitor Methods","text":"
/**\n * Visit the AST (see class description for details).\n *\n * @param NodeList<Node>|Node $root\n * @param VisitorArray $visitor\n * @param array<string, mixed>|null $keyMap\n *\n * @throws \\Exception\n *\n * @return mixed\n *\n * @api\n */\nstatic function visit(object $root, array $visitor, ?array $keyMap = null)\n
/**\n * Returns marker for stopping.\n *\n * @api\n */\nstatic function stop(): GraphQL\\Language\\VisitorStop\n
/**\n * Returns marker for skipping the subtree at the current node.\n *\n * @api\n */\nstatic function skipNode(): GraphQL\\Language\\VisitorSkipNode\n
/**\n * Returns marker for removing the current node.\n *\n * @api\n */\nstatic function removeNode(): GraphQL\\Language\\VisitorRemoveNode\n
"},{"location":"class-reference/#graphqllanguageastnodekind","title":"GraphQL\\Language\\AST\\NodeKind","text":"

Holds constants of possible AST nodes.

"},{"location":"class-reference/#graphqllanguageastnodekind-constants","title":"GraphQL\\Language\\AST\\NodeKind Constants","text":"
const NAME = 'Name';\nconst DOCUMENT = 'Document';\nconst OPERATION_DEFINITION = 'OperationDefinition';\nconst VARIABLE_DEFINITION = 'VariableDefinition';\nconst VARIABLE = 'Variable';\nconst SELECTION_SET = 'SelectionSet';\nconst FIELD = 'Field';\nconst ARGUMENT = 'Argument';\nconst FRAGMENT_SPREAD = 'FragmentSpread';\nconst INLINE_FRAGMENT = 'InlineFragment';\nconst FRAGMENT_DEFINITION = 'FragmentDefinition';\nconst INT = 'IntValue';\nconst FLOAT = 'FloatValue';\nconst STRING = 'StringValue';\nconst BOOLEAN = 'BooleanValue';\nconst ENUM = 'EnumValue';\nconst NULL = 'NullValue';\nconst LST = 'ListValue';\nconst OBJECT = 'ObjectValue';\nconst OBJECT_FIELD = 'ObjectField';\nconst DIRECTIVE = 'Directive';\nconst NAMED_TYPE = 'NamedType';\nconst LIST_TYPE = 'ListType';\nconst NON_NULL_TYPE = 'NonNullType';\nconst SCHEMA_DEFINITION = 'SchemaDefinition';\nconst OPERATION_TYPE_DEFINITION = 'OperationTypeDefinition';\nconst SCALAR_TYPE_DEFINITION = 'ScalarTypeDefinition';\nconst OBJECT_TYPE_DEFINITION = 'ObjectTypeDefinition';\nconst FIELD_DEFINITION = 'FieldDefinition';\nconst INPUT_VALUE_DEFINITION = 'InputValueDefinition';\nconst INTERFACE_TYPE_DEFINITION = 'InterfaceTypeDefinition';\nconst UNION_TYPE_DEFINITION = 'UnionTypeDefinition';\nconst ENUM_TYPE_DEFINITION = 'EnumTypeDefinition';\nconst ENUM_VALUE_DEFINITION = 'EnumValueDefinition';\nconst INPUT_OBJECT_TYPE_DEFINITION = 'InputObjectTypeDefinition';\nconst SCALAR_TYPE_EXTENSION = 'ScalarTypeExtension';\nconst OBJECT_TYPE_EXTENSION = 'ObjectTypeExtension';\nconst INTERFACE_TYPE_EXTENSION = 'InterfaceTypeExtension';\nconst UNION_TYPE_EXTENSION = 'UnionTypeExtension';\nconst ENUM_TYPE_EXTENSION = 'EnumTypeExtension';\nconst INPUT_OBJECT_TYPE_EXTENSION = 'InputObjectTypeExtension';\nconst DIRECTIVE_DEFINITION = 'DirectiveDefinition';\nconst SCHEMA_EXTENSION = 'SchemaExtension';\nconst CLASS_MAP = [\n    'Name' => 'GraphQL\\\\Language\\\\AST\\\\NameNode',\n    'Document' => 'GraphQL\\\\Language\\\\AST\\\\DocumentNode',\n    'OperationDefinition' => 'GraphQL\\\\Language\\\\AST\\\\OperationDefinitionNode',\n    'VariableDefinition' => 'GraphQL\\\\Language\\\\AST\\\\VariableDefinitionNode',\n    'Variable' => 'GraphQL\\\\Language\\\\AST\\\\VariableNode',\n    'SelectionSet' => 'GraphQL\\\\Language\\\\AST\\\\SelectionSetNode',\n    'Field' => 'GraphQL\\\\Language\\\\AST\\\\FieldNode',\n    'Argument' => 'GraphQL\\\\Language\\\\AST\\\\ArgumentNode',\n    'FragmentSpread' => 'GraphQL\\\\Language\\\\AST\\\\FragmentSpreadNode',\n    'InlineFragment' => 'GraphQL\\\\Language\\\\AST\\\\InlineFragmentNode',\n    'FragmentDefinition' => 'GraphQL\\\\Language\\\\AST\\\\FragmentDefinitionNode',\n    'IntValue' => 'GraphQL\\\\Language\\\\AST\\\\IntValueNode',\n    'FloatValue' => 'GraphQL\\\\Language\\\\AST\\\\FloatValueNode',\n    'StringValue' => 'GraphQL\\\\Language\\\\AST\\\\StringValueNode',\n    'BooleanValue' => 'GraphQL\\\\Language\\\\AST\\\\BooleanValueNode',\n    'EnumValue' => 'GraphQL\\\\Language\\\\AST\\\\EnumValueNode',\n    'NullValue' => 'GraphQL\\\\Language\\\\AST\\\\NullValueNode',\n    'ListValue' => 'GraphQL\\\\Language\\\\AST\\\\ListValueNode',\n    'ObjectValue' => 'GraphQL\\\\Language\\\\AST\\\\ObjectValueNode',\n    'ObjectField' => 'GraphQL\\\\Language\\\\AST\\\\ObjectFieldNode',\n    'Directive' => 'GraphQL\\\\Language\\\\AST\\\\DirectiveNode',\n    'NamedType' => 'GraphQL\\\\Language\\\\AST\\\\NamedTypeNode',\n    'ListType' => 'GraphQL\\\\Language\\\\AST\\\\ListTypeNode',\n    'NonNullType' => 'GraphQL\\\\Language\\\\AST\\\\NonNullTypeNode',\n    'SchemaDefinition' => 'GraphQL\\\\Language\\\\AST\\\\SchemaDefinitionNode',\n    'OperationTypeDefinition' => 'GraphQL\\\\Language\\\\AST\\\\OperationTypeDefinitionNode',\n    'ScalarTypeDefinition' => 'GraphQL\\\\Language\\\\AST\\\\ScalarTypeDefinitionNode',\n    'ObjectTypeDefinition' => 'GraphQL\\\\Language\\\\AST\\\\ObjectTypeDefinitionNode',\n    'FieldDefinition' => 'GraphQL\\\\Language\\\\AST\\\\FieldDefinitionNode',\n    'InputValueDefinition' => 'GraphQL\\\\Language\\\\AST\\\\InputValueDefinitionNode',\n    'InterfaceTypeDefinition' => 'GraphQL\\\\Language\\\\AST\\\\InterfaceTypeDefinitionNode',\n    'UnionTypeDefinition' => 'GraphQL\\\\Language\\\\AST\\\\UnionTypeDefinitionNode',\n    'EnumTypeDefinition' => 'GraphQL\\\\Language\\\\AST\\\\EnumTypeDefinitionNode',\n    'EnumValueDefinition' => 'GraphQL\\\\Language\\\\AST\\\\EnumValueDefinitionNode',\n    'InputObjectTypeDefinition' => 'GraphQL\\\\Language\\\\AST\\\\InputObjectTypeDefinitionNode',\n    'ScalarTypeExtension' => 'GraphQL\\\\Language\\\\AST\\\\ScalarTypeExtensionNode',\n    'ObjectTypeExtension' => 'GraphQL\\\\Language\\\\AST\\\\ObjectTypeExtensionNode',\n    'InterfaceTypeExtension' => 'GraphQL\\\\Language\\\\AST\\\\InterfaceTypeExtensionNode',\n    'UnionTypeExtension' => 'GraphQL\\\\Language\\\\AST\\\\UnionTypeExtensionNode',\n    'EnumTypeExtension' => 'GraphQL\\\\Language\\\\AST\\\\EnumTypeExtensionNode',\n    'InputObjectTypeExtension' => 'GraphQL\\\\Language\\\\AST\\\\InputObjectTypeExtensionNode',\n    'DirectiveDefinition' => 'GraphQL\\\\Language\\\\AST\\\\DirectiveDefinitionNode',\n];\n
"},{"location":"class-reference/#graphqlexecutorexecutor","title":"GraphQL\\Executor\\Executor","text":"

Implements the \"Evaluating requests\" section of the GraphQL specification.

@phpstan-type FieldResolver callable(mixed, array, mixed, ResolveInfo): mixed @phpstan-type ImplementationFactory callable(PromiseAdapter, Schema, DocumentNode, mixed, mixed, array, ?string, callable): ExecutorImplementation

@see \\GraphQL\\Tests\\Executor\\ExecutorTest

"},{"location":"class-reference/#graphqlexecutorexecutor-methods","title":"GraphQL\\Executor\\Executor Methods","text":"
/**\n * Executes DocumentNode against given $schema.\n *\n * Always returns ExecutionResult and never throws.\n * All errors which occur during operation execution are collected in `$result->errors`.\n *\n * @param mixed $rootValue\n * @param mixed $contextValue\n * @param array<string, mixed>|null $variableValues\n *\n * @phpstan-param FieldResolver|null $fieldResolver\n *\n * @api\n *\n * @throws InvariantViolation\n */\nstatic function execute(\n    GraphQL\\Type\\Schema $schema,\n    GraphQL\\Language\\AST\\DocumentNode $documentNode,\n    $rootValue = null,\n    $contextValue = null,\n    ?array $variableValues = null,\n    ?string $operationName = null,\n    ?callable $fieldResolver = null\n): GraphQL\\Executor\\ExecutionResult\n
/**\n * Same as execute(), but requires promise adapter and returns a promise which is always\n * fulfilled with an instance of ExecutionResult and never rejected.\n *\n * Useful for async PHP platforms.\n *\n * @param mixed $rootValue\n * @param mixed $contextValue\n * @param array<string, mixed>|null $variableValues\n *\n * @phpstan-param FieldResolver|null $fieldResolver\n *\n * @api\n */\nstatic function promiseToExecute(\n    GraphQL\\Executor\\Promise\\PromiseAdapter $promiseAdapter,\n    GraphQL\\Type\\Schema $schema,\n    GraphQL\\Language\\AST\\DocumentNode $documentNode,\n    $rootValue = null,\n    $contextValue = null,\n    ?array $variableValues = null,\n    ?string $operationName = null,\n    ?callable $fieldResolver = null\n): GraphQL\\Executor\\Promise\\Promise\n
"},{"location":"class-reference/#graphqlexecutorscopedcontext","title":"GraphQL\\Executor\\ScopedContext","text":"

When the object passed as $contextValue to GraphQL execution implements this, its clone() method will be called before passing the context down to a field. This allows passing information to child fields in the query tree without affecting sibling or parent fields.

"},{"location":"class-reference/#graphqlexecutorexecutionresult","title":"GraphQL\\Executor\\ExecutionResult","text":"

Returned after query execution. Represents both - result of successful execution and of a failed one (with errors collected in errors prop).

Could be converted to spec-compliant serializable array using toArray().

@phpstan-type SerializableError array{ message: string, locations?: array, path?: array, extensions?: array } @phpstan-type SerializableErrors array @phpstan-type SerializableResult array{ data?: array, errors?: SerializableErrors, extensions?: array } @phpstan-type ErrorFormatter callable(\\Throwable): SerializableError @phpstan-type ErrorsHandler callable(array $errors, ErrorFormatter $formatter): SerializableErrors

@see \\GraphQL\\Tests\\Executor\\ExecutionResultTest

"},{"location":"class-reference/#graphqlexecutorexecutionresult-props","title":"GraphQL\\Executor\\ExecutionResult Props","text":"
/**\n * Data collected from resolvers during query execution.\n *\n * @api\n *\n * @var array<string, mixed>|null\n */\npublic $data;\n\n/**\n * Errors registered during query execution.\n *\n * If an error was caused by exception thrown in resolver, $error->getPrevious() would\n * contain original exception.\n *\n * @api\n *\n * @var array<Error>\n */\npublic $errors;\n\n/**\n * User-defined serializable array of extensions included in serialized result.\n *\n * @api\n *\n * @var array<string, mixed>|null\n */\npublic $extensions;\n
"},{"location":"class-reference/#graphqlexecutorexecutionresult-methods","title":"GraphQL\\Executor\\ExecutionResult Methods","text":"
/**\n * Define custom error formatting (must conform to http://facebook.github.io/graphql/#sec-Errors).\n *\n * Expected signature is: function (GraphQL\\Error\\Error $error): array\n *\n * Default formatter is \"GraphQL\\Error\\FormattedError::createFromException\"\n *\n * Expected returned value must be an array:\n * array(\n *    'message' => 'errorMessage',\n *    // ... other keys\n * );\n *\n * @phpstan-param ErrorFormatter|null $errorFormatter\n *\n * @api\n */\nfunction setErrorFormatter(?callable $errorFormatter): self\n
/**\n * Define custom logic for error handling (filtering, logging, etc).\n *\n * Expected handler signature is:\n * fn (array $errors, callable $formatter): array\n *\n * Default handler is:\n * fn (array $errors, callable $formatter): array => array_map($formatter, $errors)\n *\n * @phpstan-param ErrorsHandler|null $errorsHandler\n *\n * @api\n */\nfunction setErrorsHandler(?callable $errorsHandler): self\n
/**\n * Converts GraphQL query result to spec-compliant serializable array using provided\n * errors handler and formatter.\n *\n * If debug argument is passed, output of error formatter is enriched which debugging information\n * (\"debugMessage\", \"trace\" keys depending on flags).\n *\n * $debug argument must sum of flags from @see \\GraphQL\\Error\\DebugFlag\n *\n * @phpstan-return SerializableResult\n *\n * @api\n */\nfunction toArray(int $debug = 'GraphQL\\\\Error\\\\DebugFlag::NONE'): array\n
"},{"location":"class-reference/#graphqlexecutorpromisepromiseadapter","title":"GraphQL\\Executor\\Promise\\PromiseAdapter","text":"

Provides a means for integration of async PHP platforms (related docs).

"},{"location":"class-reference/#graphqlexecutorpromisepromiseadapter-methods","title":"GraphQL\\Executor\\Promise\\PromiseAdapter Methods","text":"
/**\n * Is the value a promise or a deferred of the underlying platform?\n *\n * @param mixed $value\n *\n * @api\n */\nfunction isThenable($value): bool\n
/**\n * Converts thenable of the underlying platform into GraphQL\\Executor\\Promise\\Promise instance.\n *\n * @param mixed $thenable\n *\n * @api\n */\nfunction convertThenable($thenable): GraphQL\\Executor\\Promise\\Promise\n
/**\n * Accepts our Promise wrapper, extracts adopted promise out of it and executes actual `then` logic described\n * in Promises/A+ specs. Then returns new wrapped instance of GraphQL\\Executor\\Promise\\Promise.\n *\n * @api\n */\nfunction then(\n    GraphQL\\Executor\\Promise\\Promise $promise,\n    ?callable $onFulfilled = null,\n    ?callable $onRejected = null\n): GraphQL\\Executor\\Promise\\Promise\n
/**\n * Creates a Promise from the given resolver callable.\n *\n * @param callable(callable $resolve, callable $reject): void $resolver\n *\n * @api\n */\nfunction create(callable $resolver): GraphQL\\Executor\\Promise\\Promise\n
/**\n * Creates a fulfilled Promise for a value if the value is not a promise.\n *\n * @param mixed $value\n *\n * @api\n */\nfunction createFulfilled($value = null): GraphQL\\Executor\\Promise\\Promise\n
/**\n * Creates a rejected promise for a reason if the reason is not a promise.\n *\n * If the provided reason is a promise, then it is returned as-is.\n *\n * @api\n */\nfunction createRejected(Throwable $reason): GraphQL\\Executor\\Promise\\Promise\n
/**\n * Given an iterable of promises (or values), returns a promise that is fulfilled when all the\n * items in the iterable are fulfilled.\n *\n * @param iterable<Promise|mixed> $promisesOrValues\n *\n * @api\n */\nfunction all(iterable $promisesOrValues): GraphQL\\Executor\\Promise\\Promise\n
"},{"location":"class-reference/#graphqlvalidatordocumentvalidator","title":"GraphQL\\Validator\\DocumentValidator","text":"

Implements the \"Validation\" section of the spec.

Validation runs synchronously, returning an array of encountered errors, or an empty array if no errors were encountered and the document is valid.

A list of specific validation rules may be provided. If not provided, the default list of rules defined by the GraphQL specification will be used.

Each validation rule is an instance of GraphQL\\Validator\\Rules\\ValidationRule which returns a visitor (see the GraphQL\\Language\\Visitor API).

Visitor methods are expected to return an instance of GraphQL\\Error\\Error, or array of such instances when invalid.

Optionally a custom TypeInfo instance may be provided. If not provided, one will be created from the provided schema.

"},{"location":"class-reference/#graphqlvalidatordocumentvalidator-methods","title":"GraphQL\\Validator\\DocumentValidator Methods","text":"
/**\n * Validate a GraphQL query against a schema.\n *\n * @param array<ValidationRule>|null $rules Defaults to using all available rules\n *\n * @throws \\Exception\n *\n * @return array<int, Error>\n *\n * @api\n */\nstatic function validate(\n    GraphQL\\Type\\Schema $schema,\n    GraphQL\\Language\\AST\\DocumentNode $ast,\n    ?array $rules = null,\n    ?GraphQL\\Utils\\TypeInfo $typeInfo = null\n): array\n
/**\n * Returns all global validation rules.\n *\n * @throws \\InvalidArgumentException\n *\n * @return array<string, ValidationRule>\n *\n * @api\n */\nstatic function allRules(): array\n
/**\n * Returns global validation rule by name.\n *\n * Standard rules are named by class name, so example usage for such rules:\n *\n * @example DocumentValidator::getRule(GraphQL\\Validator\\Rules\\QueryComplexity::class);\n *\n * @api\n *\n * @throws \\InvalidArgumentException\n */\nstatic function getRule(string $name): ?GraphQL\\Validator\\Rules\\ValidationRule\n
/**\n * Add rule to list of global validation rules.\n *\n * @api\n */\nstatic function addRule(GraphQL\\Validator\\Rules\\ValidationRule $rule): void\n
/**\n * Remove rule from list of global validation rules.\n *\n * @api\n */\nstatic function removeRule(GraphQL\\Validator\\Rules\\ValidationRule $rule): void\n
"},{"location":"class-reference/#graphqlerrorerror","title":"GraphQL\\Error\\Error","text":"

Describes an Error found during the parse, validate, or execute phases of performing a GraphQL operation. In addition to a message and stack trace, it also includes information about the locations in a GraphQL document and/or execution result that correspond to the Error.

When the error was caused by an exception thrown in resolver, original exception is available via getPrevious().

Also read related docs on error handling

Class extends standard PHP \\Exception, so all standard methods of base \\Exception class are available in addition to those listed below.

@see \\GraphQL\\Tests\\Error\\ErrorTest

"},{"location":"class-reference/#graphqlerrorerror-methods","title":"GraphQL\\Error\\Error Methods","text":"
/**\n * An array of locations within the source GraphQL document which correspond to this error.\n *\n * Each entry has information about `line` and `column` within source GraphQL document:\n * $location->line;\n * $location->column;\n *\n * Errors during validation often contain multiple locations, for example to\n * point out to field mentioned in multiple fragments. Errors during execution include a\n * single location, the field which produced the error.\n *\n * @return array<int, SourceLocation>\n *\n * @api\n */\nfunction getLocations(): array\n
/**\n * Returns an array describing the path from the root value to the field which produced this error.\n * Only included for execution errors. When fields are aliased, the path includes aliases.\n *\n * @return list<int|string>|null\n *\n * @api\n */\nfunction getPath(): ?array\n
/**\n * Returns an array describing the path from the root value to the field which produced this error.\n * Only included for execution errors. This will never include aliases.\n *\n * @return list<int|string>|null\n *\n * @api\n */\nfunction getUnaliasedPath(): ?array\n
"},{"location":"class-reference/#graphqlerrorwarning","title":"GraphQL\\Error\\Warning","text":"

Encapsulates warnings produced by the library.

Warnings can be suppressed (individually or all) if required. Also, it is possible to override warning handler (which is trigger_error() by default).

@phpstan-type WarningHandler callable(string $errorMessage, int $warningId, ?int $messageLevel): void

"},{"location":"class-reference/#graphqlerrorwarning-constants","title":"GraphQL\\Error\\Warning Constants","text":"
const NONE = 0;\nconst WARNING_ASSIGN = 2;\nconst WARNING_CONFIG = 4;\nconst WARNING_FULL_SCHEMA_SCAN = 8;\nconst WARNING_CONFIG_DEPRECATION = 16;\nconst WARNING_NOT_A_TYPE = 32;\nconst ALL = 63;\n
"},{"location":"class-reference/#graphqlerrorwarning-methods","title":"GraphQL\\Error\\Warning Methods","text":"
/**\n * Sets warning handler which can intercept all system warnings.\n * When not set, trigger_error() is used to notify about warnings.\n *\n * @phpstan-param WarningHandler|null $warningHandler\n *\n * @api\n */\nstatic function setWarningHandler(?callable $warningHandler = null): void\n
/**\n * Suppress warning by id (has no effect when custom warning handler is set).\n *\n * @param bool|int $suppress\n *\n * @example Warning::suppress(Warning::WARNING_NOT_A_TYPE) suppress a specific warning\n * @example Warning::suppress(true) suppresses all warnings\n * @example Warning::suppress(false) enables all warnings\n *\n * @api\n */\nstatic function suppress($suppress = true): void\n
/**\n * Re-enable previously suppressed warning by id (has no effect when custom warning handler is set).\n *\n * @param bool|int $enable\n *\n * @example Warning::suppress(Warning::WARNING_NOT_A_TYPE) re-enables a specific warning\n * @example Warning::suppress(true) re-enables all warnings\n * @example Warning::suppress(false) suppresses all warnings\n *\n * @api\n */\nstatic function enable($enable = true): void\n
"},{"location":"class-reference/#graphqlerrorclientaware","title":"GraphQL\\Error\\ClientAware","text":"

Implementing ClientAware allows graphql-php to decide if this error is safe to be shown to clients.

Only errors that both implement this interface and return true from isClientSafe() will retain their original error message during formatting.

All other errors will have their message replaced with \"Internal server error\".

"},{"location":"class-reference/#graphqlerrorclientaware-methods","title":"GraphQL\\Error\\ClientAware Methods","text":"
/**\n * Is it safe to show the error message to clients?\n *\n * @api\n */\nfunction isClientSafe(): bool\n
"},{"location":"class-reference/#graphqlerrordebugflag","title":"GraphQL\\Error\\DebugFlag","text":"

Collection of flags for error debugging.

"},{"location":"class-reference/#graphqlerrordebugflag-constants","title":"GraphQL\\Error\\DebugFlag Constants","text":"
const NONE = 0;\nconst INCLUDE_DEBUG_MESSAGE = 1;\nconst INCLUDE_TRACE = 2;\nconst RETHROW_INTERNAL_EXCEPTIONS = 4;\nconst RETHROW_UNSAFE_EXCEPTIONS = 8;\n
"},{"location":"class-reference/#graphqlerrorformattederror","title":"GraphQL\\Error\\FormattedError","text":"

This class is used for default error formatting. It converts PHP exceptions to spec-compliant errors and provides tools for error debugging.

@see ExecutionResult

@phpstan-import-type SerializableError from ExecutionResult @phpstan-import-type ErrorFormatter from ExecutionResult

@see \\GraphQL\\Tests\\Error\\FormattedErrorTest

"},{"location":"class-reference/#graphqlerrorformattederror-methods","title":"GraphQL\\Error\\FormattedError Methods","text":"
/**\n * Set default error message for internal errors formatted using createFormattedError().\n * This value can be overridden by passing 3rd argument to `createFormattedError()`.\n *\n * @api\n */\nstatic function setInternalErrorMessage(string $msg): void\n
/**\n * Convert any exception to a GraphQL spec compliant array.\n *\n * This method only exposes the exception message when the given exception\n * implements the ClientAware interface, or when debug flags are passed.\n *\n * For a list of available debug flags @see \\GraphQL\\Error\\DebugFlag constants.\n *\n * @return SerializableError\n *\n * @api\n */\nstatic function createFromException(\n    Throwable $exception,\n    int $debugFlag = 'GraphQL\\\\Error\\\\DebugFlag::NONE',\n    ?string $internalErrorMessage = null\n): array\n
/**\n * Returns error trace as serializable array.\n *\n * @return array<int, array{\n *     file?: string,\n *     line?: int,\n *     function?: string,\n *     call?: string,\n * }>\n *\n * @api\n */\nstatic function toSafeTrace(Throwable $error): array\n
"},{"location":"class-reference/#graphqlserverstandardserver","title":"GraphQL\\Server\\StandardServer","text":"

GraphQL server compatible with both: express-graphql and Apollo Server. Usage Example:.

$server = new StandardServer([\n  'schema' => $mySchema\n]);\n$server->handleRequest();\n

Or using ServerConfig instance:

$config = GraphQL\\Server\\ServerConfig::create()\n    ->setSchema($mySchema)\n    ->setContext($myContext);\n\n$server = new GraphQL\\Server\\StandardServer($config);\n$server->handleRequest();\n

See dedicated section in docs for details.

@see \\GraphQL\\Tests\\Server\\StandardServerTest

"},{"location":"class-reference/#graphqlserverstandardserver-methods","title":"GraphQL\\Server\\StandardServer Methods","text":"
/**\n * @param ServerConfig|array<string, mixed> $config\n *\n * @api\n *\n * @throws InvariantViolation\n */\nfunction __construct($config)\n
/**\n * Parses HTTP request, executes and emits response (using standard PHP `header` function and `echo`).\n *\n * When $parsedBody is not set, it uses PHP globals to parse a request.\n * It is possible to implement request parsing elsewhere (e.g. using framework Request instance)\n * and then pass it to the server.\n *\n * See `executeRequest()` if you prefer to emit the response yourself\n * (e.g. using the Response object of some framework).\n *\n * @param OperationParams|array<OperationParams> $parsedBody\n *\n * @api\n *\n * @throws \\Exception\n * @throws InvariantViolation\n * @throws RequestError\n */\nfunction handleRequest($parsedBody = null): void\n
/**\n * Executes a GraphQL operation and returns an execution result\n * (or promise when promise adapter is different from SyncPromiseAdapter).\n *\n * When $parsedBody is not set, it uses PHP globals to parse a request.\n * It is possible to implement request parsing elsewhere (e.g. using framework Request instance)\n * and then pass it to the server.\n *\n * PSR-7 compatible method executePsrRequest() does exactly this.\n *\n * @param OperationParams|array<OperationParams> $parsedBody\n *\n * @throws \\Exception\n * @throws InvariantViolation\n * @throws RequestError\n *\n * @return ExecutionResult|array<int, ExecutionResult>|Promise\n *\n * @api\n */\nfunction executeRequest($parsedBody = null)\n
/**\n * Executes PSR-7 request and fulfills PSR-7 response.\n *\n * See `executePsrRequest()` if you prefer to create response yourself\n * (e.g. using specific JsonResponse instance of some framework).\n *\n * @throws \\Exception\n * @throws \\InvalidArgumentException\n * @throws \\JsonException\n * @throws \\RuntimeException\n * @throws InvariantViolation\n * @throws RequestError\n *\n * @return ResponseInterface|Promise\n *\n * @api\n */\nfunction processPsrRequest(\n    Psr\\Http\\Message\\RequestInterface $request,\n    Psr\\Http\\Message\\ResponseInterface $response,\n    Psr\\Http\\Message\\StreamInterface $writableBodyStream\n)\n
/**\n * Executes GraphQL operation and returns execution result\n * (or promise when promise adapter is different from SyncPromiseAdapter).\n *\n * @throws \\Exception\n * @throws \\JsonException\n * @throws InvariantViolation\n * @throws RequestError\n *\n * @return ExecutionResult|array<int, ExecutionResult>|Promise\n *\n * @api\n */\nfunction executePsrRequest(Psr\\Http\\Message\\RequestInterface $request)\n
"},{"location":"class-reference/#graphqlserverserverconfig","title":"GraphQL\\Server\\ServerConfig","text":"

Server configuration class. Could be passed directly to server constructor. List of options accepted by create method is described in docs.

Usage example:

$config = GraphQL\\Server\\ServerConfig::create()\n    ->setSchema($mySchema)\n    ->setContext($myContext);\n\n$server = new GraphQL\\Server\\StandardServer($config);\n

@see ExecutionResult

@phpstan-type PersistedQueryLoader callable(string $queryId, OperationParams $operation): (string|DocumentNode) @phpstan-type RootValueResolver callable(OperationParams $operation, DocumentNode $doc, string $operationType): mixed @phpstan-type ValidationRulesOption array|null|callable(OperationParams $operation, DocumentNode $doc, string $operationType): array

@phpstan-import-type ErrorsHandler from ExecutionResult @phpstan-import-type ErrorFormatter from ExecutionResult

@see \\GraphQL\\Tests\\Server\\ServerConfigTest

"},{"location":"class-reference/#graphqlserverserverconfig-methods","title":"GraphQL\\Server\\ServerConfig Methods","text":"
/**\n * Converts an array of options to instance of ServerConfig\n * (or just returns empty config when array is not passed).\n *\n * @param array<string, mixed> $config\n *\n * @api\n *\n * @throws InvariantViolation\n */\nstatic function create(array $config = []): self\n
/**\n * @param mixed|callable $context\n *\n * @api\n */\nfunction setContext($context): self\n
/**\n * @param mixed|callable $rootValue\n *\n * @phpstan-param mixed|RootValueResolver $rootValue\n *\n * @api\n */\nfunction setRootValue($rootValue): self\n
/**\n * @phpstan-param ErrorFormatter $errorFormatter\n *\n * @api\n */\nfunction setErrorFormatter(callable $errorFormatter): self\n
/**\n * @phpstan-param ErrorsHandler $handler\n *\n * @api\n */\nfunction setErrorsHandler(callable $handler): self\n
/**\n * Set validation rules for this server.\n *\n * @param array<ValidationRule>|callable|null $validationRules\n *\n * @phpstan-param ValidationRulesOption $validationRules\n *\n * @api\n */\nfunction setValidationRules($validationRules): self\n
/**\n * @phpstan-param PersistedQueryLoader|null $persistedQueryLoader\n *\n * @api\n */\nfunction setPersistedQueryLoader(?callable $persistedQueryLoader): self\n
/**\n * Set response debug flags.\n *\n * @see \\GraphQL\\Error\\DebugFlag class for a list of all available flags\n *\n * @api\n */\nfunction setDebugFlag(int $debugFlag = 'GraphQL\\\\Error\\\\DebugFlag::INCLUDE_DEBUG_MESSAGE'): self\n
/**\n * Allow batching queries (disabled by default).\n *\n * @api\n */\nfunction setQueryBatching(bool $enableBatching): self\n
"},{"location":"class-reference/#graphqlserverhelper","title":"GraphQL\\Server\\Helper","text":"

Contains functionality that could be re-used by various server implementations.

@see \\GraphQL\\Tests\\Server\\HelperTest

"},{"location":"class-reference/#graphqlserverhelper-methods","title":"GraphQL\\Server\\Helper Methods","text":"
/**\n * Parses HTTP request using PHP globals and returns GraphQL OperationParams\n * contained in this request. For batched requests it returns an array of OperationParams.\n *\n * This function does not check validity of these params\n * (validation is performed separately in validateOperationParams() method).\n *\n * If $readRawBodyFn argument is not provided - will attempt to read raw request body\n * from `php://input` stream.\n *\n * Internally it normalizes input to $method, $bodyParams and $queryParams and\n * calls `parseRequestParams()` to produce actual return value.\n *\n * For PSR-7 request parsing use `parsePsrRequest()` instead.\n *\n * @throws RequestError\n *\n * @return OperationParams|array<int, OperationParams>\n *\n * @api\n */\nfunction parseHttpRequest(?callable $readRawBodyFn = null)\n
/**\n * Parses normalized request params and returns instance of OperationParams\n * or array of OperationParams in case of batch operation.\n *\n * Returned value is a suitable input for `executeOperation` or `executeBatch` (if array)\n *\n * @param array<mixed> $bodyParams\n * @param array<mixed> $queryParams\n *\n * @throws RequestError\n *\n * @return OperationParams|array<int, OperationParams>\n *\n * @api\n */\nfunction parseRequestParams(string $method, array $bodyParams, array $queryParams)\n
/**\n * Checks validity of OperationParams extracted from HTTP request and returns an array of errors\n * if params are invalid (or empty array when params are valid).\n *\n * @return array<int, RequestError>\n *\n * @api\n */\nfunction validateOperationParams(GraphQL\\Server\\OperationParams $params): array\n
/**\n * Executes GraphQL operation with given server configuration and returns execution result\n * (or promise when promise adapter is different from SyncPromiseAdapter).\n *\n * @throws \\Exception\n * @throws InvariantViolation\n *\n * @return ExecutionResult|Promise\n *\n * @api\n */\nfunction executeOperation(GraphQL\\Server\\ServerConfig $config, GraphQL\\Server\\OperationParams $op)\n
/**\n * Executes batched GraphQL operations with shared promise queue\n * (thus, effectively batching deferreds|promises of all queries at once).\n *\n * @param array<OperationParams> $operations\n *\n * @throws \\Exception\n * @throws InvariantViolation\n *\n * @return array<int, ExecutionResult>|Promise\n *\n * @api\n */\nfunction executeBatch(GraphQL\\Server\\ServerConfig $config, array $operations)\n
/**\n * Send response using standard PHP `header()` and `echo`.\n *\n * @param Promise|ExecutionResult|array<ExecutionResult> $result\n *\n * @api\n *\n * @throws \\JsonException\n */\nfunction sendResponse($result): void\n
/**\n * Converts PSR-7 request to OperationParams or an array thereof.\n *\n * @throws RequestError\n *\n * @return OperationParams|array<OperationParams>\n *\n * @api\n */\nfunction parsePsrRequest(Psr\\Http\\Message\\RequestInterface $request)\n
/**\n * Converts query execution result to PSR-7 response.\n *\n * @param Promise|ExecutionResult|array<ExecutionResult> $result\n *\n * @throws \\InvalidArgumentException\n * @throws \\JsonException\n * @throws \\RuntimeException\n *\n * @return Promise|ResponseInterface\n *\n * @api\n */\nfunction toPsrResponse(\n    $result,\n    Psr\\Http\\Message\\ResponseInterface $response,\n    Psr\\Http\\Message\\StreamInterface $writableBodyStream\n)\n
"},{"location":"class-reference/#graphqlserveroperationparams","title":"GraphQL\\Server\\OperationParams","text":"

Structure representing parsed HTTP parameters for GraphQL operation.

The properties in this class are not strictly typed, as this class is only meant to serve as an intermediary representation which is not yet validated.

"},{"location":"class-reference/#graphqlserveroperationparams-props","title":"GraphQL\\Server\\OperationParams Props","text":"
/**\n * Id of the query (when using persisted queries).\n *\n * Valid aliases (case-insensitive):\n * - id\n * - queryId\n * - documentId\n *\n * @api\n *\n * @var mixed should be string|null\n */\npublic $queryId;\n\n/**\n * A document containing GraphQL operations and fragments to execute.\n *\n * @api\n *\n * @var mixed should be string|null\n */\npublic $query;\n\n/**\n * The name of the operation in the document to execute.\n *\n * @api\n *\n * @var mixed should be string|null\n */\npublic $operation;\n\n/**\n * Values for any variables defined by the operation.\n *\n * @api\n *\n * @var mixed should be array<string, mixed>\n */\npublic $variables;\n\n/**\n * Reserved for implementors to extend the protocol however they see fit.\n *\n * @api\n *\n * @var mixed should be array<string, mixed>\n */\npublic $extensions;\n\n/**\n * Executed in read-only context (e.g. via HTTP GET request)?\n *\n * @api\n */\npublic $readOnly;\n\n/**\n * The raw params used to construct this instance.\n *\n * @api\n *\n * @var array<string, mixed>\n */\npublic $originalInput;\n
"},{"location":"class-reference/#graphqlserveroperationparams-methods","title":"GraphQL\\Server\\OperationParams Methods","text":"
/**\n * Creates an instance from given array.\n *\n * @param array<string, mixed> $params\n *\n * @api\n */\nstatic function create(array $params, bool $readonly = false): GraphQL\\Server\\OperationParams\n
"},{"location":"class-reference/#graphqlutilsbuildschema","title":"GraphQL\\Utils\\BuildSchema","text":"

Build instance of @see \\GraphQL\\Type\\Schema out of schema language definition (string or parsed AST).

See schema definition language docs for details.

@phpstan-import-type TypeConfigDecorator from ASTDefinitionBuilder

@phpstan-type BuildSchemaOptions array{ assumeValid?: bool, assumeValidSDL?: bool }

  • assumeValid: When building a schema from a GraphQL service's introspection result, it might be safe to assume the schema is valid. Set to true to assume the produced schema is valid.

Default: false

  • assumeValidSDL: Set to true to assume the SDL is valid.

Default: false

@see \\GraphQL\\Tests\\Utils\\BuildSchemaTest

"},{"location":"class-reference/#graphqlutilsbuildschema-methods","title":"GraphQL\\Utils\\BuildSchema Methods","text":"
/**\n * A helper function to build a GraphQLSchema directly from a source\n * document.\n *\n * @param DocumentNode|Source|string $source\n *\n * @phpstan-param TypeConfigDecorator|null $typeConfigDecorator\n *\n * @param array<string, bool> $options\n *\n * @phpstan-param BuildSchemaOptions $options\n *\n * @api\n *\n * @throws \\Exception\n * @throws \\ReflectionException\n * @throws Error\n * @throws InvariantViolation\n * @throws SyntaxError\n */\nstatic function build($source, ?callable $typeConfigDecorator = null, array $options = []): GraphQL\\Type\\Schema\n
/**\n * This takes the AST of a schema from @see \\GraphQL\\Language\\Parser::parse().\n *\n * If no schema definition is provided, then it will look for types named Query and Mutation.\n *\n * Given that AST it constructs a @see \\GraphQL\\Type\\Schema. The resulting schema\n * has no resolve methods, so execution will use default resolvers.\n *\n * @phpstan-param TypeConfigDecorator|null $typeConfigDecorator\n *\n * @param array<string, bool> $options\n *\n * @phpstan-param BuildSchemaOptions $options\n *\n * @api\n *\n * @throws \\Exception\n * @throws \\ReflectionException\n * @throws Error\n * @throws InvariantViolation\n */\nstatic function buildAST(\n    GraphQL\\Language\\AST\\DocumentNode $ast,\n    ?callable $typeConfigDecorator = null,\n    array $options = []\n): GraphQL\\Type\\Schema\n
"},{"location":"class-reference/#graphqlutilsast","title":"GraphQL\\Utils\\AST","text":"

Various utilities dealing with AST.

"},{"location":"class-reference/#graphqlutilsast-methods","title":"GraphQL\\Utils\\AST Methods","text":"
/**\n * Convert representation of AST as an associative array to instance of GraphQL\\Language\\AST\\Node.\n *\n * For example:\n *\n * ```php\n * AST::fromArray([\n *     'kind' => 'ListValue',\n *     'values' => [\n *         ['kind' => 'StringValue', 'value' => 'my str'],\n *         ['kind' => 'StringValue', 'value' => 'my other str']\n *     ],\n *     'loc' => ['start' => 21, 'end' => 25]\n * ]);\n * ```\n *\n * Will produce instance of `ListValueNode` where `values` prop is a lazily-evaluated `NodeList`\n * returning instances of `StringValueNode` on access.\n *\n * This is a reverse operation for AST::toArray($node)\n *\n * @param array<string, mixed> $node\n *\n * @api\n *\n * @throws \\JsonException\n * @throws InvariantViolation\n */\nstatic function fromArray(array $node): GraphQL\\Language\\AST\\Node\n
/**\n * Convert AST node to serializable array.\n *\n * @return array<string, mixed>\n *\n * @api\n */\nstatic function toArray(GraphQL\\Language\\AST\\Node $node): array\n
/**\n * Produces a GraphQL Value AST given a PHP value.\n *\n * Optionally, a GraphQL type may be provided, which will be used to\n * disambiguate between value primitives.\n *\n * | PHP Value     | GraphQL Value        |\n * | ------------- | -------------------- |\n * | Object        | Input Object         |\n * | Assoc Array   | Input Object         |\n * | Array         | List                 |\n * | Boolean       | Boolean              |\n * | String        | String / Enum Value  |\n * | Int           | Int                  |\n * | Float         | Int / Float          |\n * | Mixed         | Enum Value           |\n * | null          | NullValue            |\n *\n * @param mixed $value\n * @param InputType&Type $type\n *\n * @throws \\JsonException\n * @throws InvariantViolation\n * @throws SerializationError\n *\n * @return (ValueNode&Node)|null\n *\n * @api\n */\nstatic function astFromValue($value, GraphQL\\Type\\Definition\\InputType $type): ?GraphQL\\Language\\AST\\ValueNode\n
/**\n * Produces a PHP value given a GraphQL Value AST.\n *\n * A GraphQL type must be provided, which will be used to interpret different\n * GraphQL Value literals.\n *\n * Returns `null` when the value could not be validly coerced according to\n * the provided type.\n *\n * | GraphQL Value        | PHP Value     |\n * | -------------------- | ------------- |\n * | Input Object         | Assoc Array   |\n * | List                 | Array         |\n * | Boolean              | Boolean       |\n * | String               | String        |\n * | Int / Float          | Int / Float   |\n * | Enum Value           | Mixed         |\n * | Null Value           | null          |\n *\n * @param (ValueNode&Node)|null $valueNode\n * @param array<string, mixed>|null $variables\n *\n * @throws \\Exception\n *\n * @return mixed\n *\n * @api\n */\nstatic function valueFromAST(\n    ?GraphQL\\Language\\AST\\ValueNode $valueNode,\n    GraphQL\\Type\\Definition\\Type $type,\n    ?array $variables = null\n)\n
/**\n * Produces a PHP value given a GraphQL Value AST.\n *\n * Unlike `valueFromAST()`, no type is provided. The resulting PHP value\n * will reflect the provided GraphQL value AST.\n *\n * | GraphQL Value        | PHP Value     |\n * | -------------------- | ------------- |\n * | Input Object         | Assoc Array   |\n * | List                 | Array         |\n * | Boolean              | Boolean       |\n * | String               | String        |\n * | Int / Float          | Int / Float   |\n * | Enum                 | Mixed         |\n * | Null                 | null          |\n *\n * @param array<string, mixed>|null $variables\n *\n * @throws \\Exception\n *\n * @return mixed\n *\n * @api\n */\nstatic function valueFromASTUntyped(GraphQL\\Language\\AST\\Node $valueNode, ?array $variables = null)\n
/**\n * Returns type definition for given AST Type node.\n *\n * @param callable(string): ?Type $typeLoader\n * @param NamedTypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode\n *\n * @throws \\Exception\n *\n * @api\n */\nstatic function typeFromAST(callable $typeLoader, GraphQL\\Language\\AST\\Node $inputTypeNode): ?GraphQL\\Type\\Definition\\Type\n
/**\n * Returns the operation within a document by name.\n *\n * If a name is not provided, an operation is only returned if the document has exactly one.\n *\n * @api\n */\nstatic function getOperationAST(GraphQL\\Language\\AST\\DocumentNode $document, ?string $operationName = null): ?GraphQL\\Language\\AST\\OperationDefinitionNode\n
/**\n * Provided a collection of ASTs, presumably each from different files,\n * concatenate the ASTs together into batched AST, useful for validating many\n * GraphQL source files which together represent one conceptual application.\n *\n * @param array<DocumentNode> $documents\n *\n * @api\n */\nstatic function concatAST(array $documents): GraphQL\\Language\\AST\\DocumentNode\n
"},{"location":"class-reference/#graphqlutilsschemaprinter","title":"GraphQL\\Utils\\SchemaPrinter","text":"

Prints the contents of a Schema in schema definition language.

All sorting options sort alphabetically. If not given or false, the original schema definition order will be used.

@phpstan-type Options array{ sortArguments?: bool, sortEnumValues?: bool, sortFields?: bool, sortInputFields?: bool, sortTypes?: bool, }

@see \\GraphQL\\Tests\\Utils\\SchemaPrinterTest

"},{"location":"class-reference/#graphqlutilsschemaprinter-methods","title":"GraphQL\\Utils\\SchemaPrinter Methods","text":"
/**\n * @param array<string, bool> $options\n *\n * @phpstan-param Options $options\n *\n * @api\n *\n * @throws \\JsonException\n * @throws Error\n * @throws InvariantViolation\n * @throws SerializationError\n */\nstatic function doPrint(GraphQL\\Type\\Schema $schema, array $options = []): string\n
/**\n * @param array<string, bool> $options\n *\n * @phpstan-param Options $options\n *\n * @api\n *\n * @throws \\JsonException\n * @throws Error\n * @throws InvariantViolation\n * @throws SerializationError\n */\nstatic function printIntrospectionSchema(GraphQL\\Type\\Schema $schema, array $options = []): string\n
"},{"location":"complementary-tools/","title":"Complementary Tools","text":""},{"location":"complementary-tools/#server-integrations","title":"Server Integrations","text":"
  • Standard Server \u2013 Out of the box integration with any PSR-7 compatible framework (like Slim or Laminas Mezzio)
  • Lighthouse \u2013 Laravel package, SDL-first
  • Laravel GraphQL - Laravel package, code-first
  • OverblogGraphQLBundle \u2013 Symfony bundle
  • WP-GraphQL - WordPress plugin
  • Siler - Flat files and plain-old PHP functions, supports Swoole
  • API Platform - Creates a GraphQL API from PHP models
  • LDOG Stack - Laravel, Doctrine ORM, and GraphQL application template
"},{"location":"complementary-tools/#server-utilities","title":"Server Utilities","text":"
  • GraphQLite \u2013 Use PHP Annotations to define your schema
  • GraphQL Doctrine \u2013 Define types with Doctrine ORM annotations
  • GraphQL Type Driver for Doctrine ORM \u2013 Includes events, pagination with the Complete Connection Model, and support for all default Doctrine Types
  • DataLoaderPHP \u2013 Implements deferred resolvers
  • GraphQL Upload \u2013 PSR-15 middleware to support file uploads in GraphQL
  • GraphQL Batch Processor \u2013 Provides a builder interface for defining collection, querying, filtering, and post-processing logic of batched data fetches
  • GraphQL Utils \u2013 Objective schema definition builders (no need for arrays anymore)
  • Relay Library \u2013 Helps construct Relay related schema definitions
  • Resonance/GraphQL \u2013 Integrates with Swoole for parallelism. Define your schema code-first with annotations.
  • GraphQL PHP Validation Toolkit - Small library for validation of user input
  • MLL Scalars - Collection of custom scalar types
  • X GraphQL - Provides set of libraries for building http schema, schema gateway, transforming schema, generating PHP code from execution definition, and more.
"},{"location":"complementary-tools/#graphql-clients","title":"GraphQL Clients","text":"
  • GraphiQL \u2013 Graphical interactive in-browser GraphQL IDE
  • GraphQL Playground \u2013 GraphQL IDE for better development workflows (GraphQL Subscriptions, interactive docs & collaboration)
  • Altair GraphQL Client - Beautiful feature-rich GraphQL Client for all platforms
  • Sailor - Typesafe GraphQL client for PHP
"},{"location":"concepts/","title":"Concepts","text":""},{"location":"concepts/#overview","title":"Overview","text":"

GraphQL is data-centric. On the very top level it is built around three major concepts: Schema, Query and Mutation.

You are expected to express your application as a Schema (aka Type System) and expose it as a single HTTP endpoint (e.g. using our standard server). Application clients (e.g. web or mobile clients) send Queries to this endpoint to request structured data and Mutations to perform changes (usually with HTTP POST method).

"},{"location":"concepts/#queries","title":"Queries","text":"

Queries are expressed in simple language that resembles JSON:

{\n  hero {\n    name\n    friends {\n      name\n    }\n  }\n}\n

It was designed to mirror the structure of the expected response:

{\n\"hero\": {\n\"name\": \"R2-D2\",\n\"friends\": [\n{ \"name\": \"Luke Skywalker\" },\n{ \"name\": \"Han Solo\" },\n{ \"name\": \"Leia Organa\" }\n]\n}\n}\n

The graphql-php runtime parses Queries, makes sure that they are valid for a given Type System and executes using data fetching tools provided by you as part of the integration. Queries are supposed to be idempotent.

"},{"location":"concepts/#mutations","title":"Mutations","text":"

Mutations are root fields that are allowed to have side effects, such as creating, updating or deleting data. In contrast to Query fields, the fields within the root Mutation type are executed serially. Otherwise, their definition and execution is identical to all other fields.

mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {\n  createReview(episode: $ep, review: $review) {\n    stars\n    commentary\n  }\n}\n

Variables $ep and $review are sent alongside with the mutation. A full HTTP request might look like this:

// POST /graphql-endpoint\n// Content-Type: application/javascript\n//\n{\n\"query\": \"mutation CreateReviewForEpisode...\",\n\"variables\": {\n\"ep\": \"JEDI\",\n\"review\": {\n\"stars\": 5,\n\"commentary\": \"This is a great movie!\"\n}\n}\n}\n

As you see variables may include complex objects and they will be correctly validated by the graphql-php runtime.

Another nice feature of GraphQL mutations is that they also hold the query for data to be returned after mutation. In our example the mutation will return:

{\n  \"createReview\": {\n    \"stars\": 5,\n    \"commentary\": \"This is a great movie!\"\n  }\n}\n
"},{"location":"concepts/#type-system","title":"Type System","text":"

Conceptually a GraphQL type is a collection of fields. Each field in turn has its own type which allows building complex hierarchies.

Quick example on pseudo-language:

type BlogPost {\n    title: String!\n    author: User\n    body: String\n}\n\ntype User {\n    id: Id!\n    firstName: String\n    lastName: String\n}\n

The type system is at the heart of GraphQL integration. That's where graphql-php comes into play.

It provides the following tools and primitives to describe your App as a hierarchy of types:

  • Primitives for defining objects and interfaces
  • Primitives for defining enumerations and unions
  • Primitives for defining custom scalar types
  • Built-in scalar types: ID, String, Int, Float, Boolean
  • Built-in type modifiers: ListOf and NonNull

Same example expressed in graphql-php:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$userType = new ObjectType([\n    'name' => 'User',\n    'fields' => [\n        'id' => Type::nonNull(Type::id()),\n        'firstName' => Type::string(),\n        'lastName' => Type::string()\n    ]\n]);\n\n$blogPostType = new ObjectType([\n    'name' => 'BlogPost',\n    'fields' => [\n        'title' => Type::nonNull(Type::string()),\n        'author' => $userType\n    ]\n]);\n
"},{"location":"concepts/#further-reading","title":"Further Reading","text":"

To get deeper understanding of GraphQL concepts - read the docs on official GraphQL website

To get started with graphql-php - continue to next section \"Getting Started\"

"},{"location":"data-fetching/","title":"Fetching Data","text":""},{"location":"data-fetching/#overview","title":"Overview","text":"

GraphQL is data-storage agnostic. You can use any underlying data storage engine, including but not limited to SQL or NoSQL databases, plain files or in-memory data structures.

In order to convert the GraphQL query to a PHP array, graphql-php traverses query fields (using depth-first algorithm) and runs the special resolve function on each field. This resolve function is provided by you as a part of the field definition or query execution call.

The result returned by the resolve function is directly included in the response (for scalars and enums) or passed down to nested fields (for objects).

Let's walk through an example. Consider the following GraphQL query:

{\n  lastStory {\n    title\n    author {\n      name\n    }\n  }\n}\n

We need a Schema that can fulfill it. On the very top level, the Schema contains the Query type:

use GraphQL\\Type\\Definition\\ObjectType;\n\n$queryType = new ObjectType([\n    'name' => 'Query',\n        'fields' => [\n        'lastStory' => [\n            'type' => $blogStoryType,\n            'resolve' => fn (): array => [\n                'id' => 1,\n                'title' => 'Example blog post',\n                'authorId' => 1\n            ],\n        ]\n    ]\n]);\n

As we see, the field lastStory has a resolve function that is responsible for fetching data.

In our example, we simply return a static value, but in the real-world application you would query your data source and return the result from there.

Since lastStory is of composite type BlogStory, this result is passed down to fields of this type:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\nconst USERS = [\n    1 => [\n        'id' => 1,\n        'name' => 'Smith'\n    ],\n    2 => [\n        'id' => 2,\n        'name' => 'Anderson'\n    ]\n];\n\n$blogStoryType = new ObjectType([\n    'name' => 'BlogStory',\n    'fields' => [\n        'author' => [\n            'type' => $userType,\n            'resolve' => fn (array $blogStory): array => USERS[$blogStory['authorId']],\n        ],\n        'title' => [\n            'type' => Type::string()\n        ]\n    ]\n]);\n

Here $blogStory is the array returned by the lastStory field above.

Again: in real-world applications you would fetch user data from your data source by authorId and return it. Also, note that you don't have to return arrays. You can return any value, graphql-php will pass it untouched to nested resolvers.

But then the question appears - the field title has no resolve option, how is it resolved? When you define no custom resolver, the default field resolver applies.

"},{"location":"data-fetching/#default-field-resolver","title":"Default Field Resolver","text":"

graphql-php provides the following default field resolver:

use GraphQL\\Type\\Definition\\ResolveInfo;\n\nfunction defaultFieldResolver($objectValue, array $args, $context, ResolveInfo $info)\n{\n    $fieldName = $info->fieldName;\n    $property = null;\n\n    if (is_array($objectValue) || $objectValue instanceof ArrayAccess) {\n        if (isset($objectValue[$fieldName])) {\n            $property = $objectValue[$fieldName];\n        }\n    } elseif (is_object($objectValue)) {\n        if (isset($objectValue->{$fieldName})) {\n            $property = $objectValue->{$fieldName};\n        }\n    }\n\n    return $property instanceof Closure\n        ? $property($objectValue, $args, $contextValue, $info)\n        : $property;\n}\n

It returns value by key (for arrays) or property (for objects). If the value is not set, it returns null.

To override the default resolver, pass it as an argument to executeQuery.

"},{"location":"data-fetching/#default-field-resolver-per-type","title":"Default Field Resolver per Type","text":"

Sometimes it might be convenient to set default field resolver per type. You can do so by providing resolveField option in type config. For example:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\ResolveInfo;\n\n$userType = new ObjectType([\n    'name' => 'User',\n    'fields' => [\n        'name' => Type::string(),\n        'email' => Type::string()\n    ],\n    'resolveField' => function (User $user, array $args, $context, ResolveInfo $info) {\n        switch ($info->fieldName) {\n            case 'name': return $user->getName();\n            case 'email': return $user->getEmail();\n            default: return null;\n        }\n    },\n]);\n

Keep in mind that field resolver has precedence over default field resolver per type which in turn has precedence over default field resolver.

"},{"location":"data-fetching/#optimize-resolvers","title":"Optimize Resolvers","text":"

The 4th argument of resolver functions is an instance of ResolveInfo. It contains information that is useful for the field resolution process.

Depending on which data source is used, knowing which fields the client queried can be used to optimize the performance of a resolver. For example, an SQL query may only need to select the queried fields.

The following example limits which columns are selected from the database:

use GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\ResolveInfo;\n\n$queryType = new ObjectType([\n    'name' => 'Query',\n    'fields' => [\n        'lastStory' => [\n            'type' => $storyType,\n            'resolve' => function ($root, array $args, $context, ResolveInfo $resolveInfo): Story {\n                // Fictitious API, use whatever database access your application/framework provides\n                $builder = Story::builder();\n                foreach ($resolveInfo->getFieldSelection() as $field => $_) {\n                  $builder->addSelect($field);\n                }\n\n                return $builder->last();\n            }\n        ]\n    ]\n]);\n
"},{"location":"data-fetching/#solving-n1-problem","title":"Solving N+1 Problem","text":"

Since: 0.9.0

One of the most annoying problems with data fetching is a so-called N+1 problem. Consider following GraphQL query:

{\n  topStories(limit: 10) {\n    title\n    author {\n      name\n      email\n    }\n  }\n}\n

Naive field resolution process would require up to 10 calls to the underlying data store to fetch authors for all 10 stories.

graphql-php provides tools to mitigate this problem: it allows you to defer actual field resolution to a later stage when one batched query could be executed instead of 10 distinct queries.

Here is an example of BlogStory resolver for field author that uses deferring:

use GraphQL\\Deferred;\n\n'resolve' => function (array $blogStory): Deferred {\n    MyUserBuffer::add($blogStory['authorId']);\n\n    return new Deferred(function () use ($blogStory): User {\n        MyUserBuffer::loadBuffered();\n\n        return MyUserBuffer::get($blogStory['authorId']);\n    });\n}\n

In this example, we fill up the buffer with 10 author ids first. Then graphql-php continues resolving other non-deferred fields until there are none of them left.

After that, it calls closures wrapped by GraphQL\\Deferred which in turn load all buffered ids once (using SQL IN(?), Redis MGET or similar tools) and returns the final field value.

Originally this approach was advocated by Facebook in their Dataloader project. This solution enables very interesting optimizations at no cost. Consider the following query:

{\n  topStories(limit: 10) {\n    author {\n      email\n    }\n  }\n  category {\n    stories(limit: 10) {\n      author {\n        email\n      }\n    }\n  }\n}\n

Even though author field is located on different levels of the query - it can be buffered in the same buffer. In this example, only one query will be executed for all story authors comparing to 20 queries in a naive implementation.

"},{"location":"data-fetching/#async-php","title":"Async PHP","text":"

If your project runs in an environment that supports async operations (like HHVM, ReactPHP, AMPHP, appserver.io, PHP threads, etc) you can leverage the power of your platform to resolve some fields asynchronously.

The only requirement: your platform must support the concept of Promises compatible with Promises A+ specification.

To start using this feature, switch facade method for query execution from executeQuery to promiseToExecute:

use GraphQL\\GraphQL;\nuse GraphQL\\Executor\\ExecutionResult;\n\n$promise = GraphQL::promiseToExecute(\n    $promiseAdapter,\n    $schema,\n    $queryString,\n    $rootValue = null,\n    $contextValue = null,\n    $variableValues = null,\n    $operationName = null,\n    $fieldResolver = null,\n    $validationRules = null\n);\n$promise->then(fn (ExecutionResult $result): array => $result->toArray());\n

Where $promiseAdapter is an instance of:

  • For ReactPHP (requires react/promise as composer dependency): GraphQL\\Executor\\Promise\\Adapter\\ReactPromiseAdapter

  • For AMPHP: GraphQL\\Executor\\Promise\\Adapter\\AmpPromiseAdapter

  • For Swoole or OpenSwoole: You can use an external library: Resonance

  • Other platforms: write your own class implementing interface: GraphQL\\Executor\\Promise\\PromiseAdapter.

Then your resolve functions should return promises of your platform instead of GraphQL\\Deferreds.

"},{"location":"error-handling/","title":"Handling Errors","text":""},{"location":"error-handling/#errors-in-graphql","title":"Errors in GraphQL","text":"

There are 3 types of errors in GraphQL:

  • Syntax: query has invalid syntax and could not be parsed;
  • Validation: query is incompatible with type system (e.g. unknown field is requested);
  • Execution: occurs when some field resolver throws (or returns unexpected value).

When Syntax or Validation errors are detected, an exception is thrown and the query is not executed.

Exceptions thrown during query execution are caught and collected in the result. They are available in $errors prop of GraphQL\\Executor\\ExecutionResult.

GraphQL is forgiving to Execution errors which occur in resolvers of nullable fields. If such field throws or returns unexpected value the value of the field in response will be simply replaced with null and error entry will be registered.

If an exception is thrown in the non-null field - error bubbles up to the first nullable field. This nullable field is replaced with null and error entry is added to the result. If all fields up to the root are non-null - data entry will be removed from the result and only errors key will be presented.

When the result is converted to a serializable array using its toArray() method, all errors are converted to arrays as well using default error formatting (see below).

Alternatively, you can apply custom error filtering and formatting for your specific requirements.

"},{"location":"error-handling/#default-error-formatting","title":"Default Error formatting","text":"

By default, each error entry is converted to an associative array with following structure:

[\n    'message' => 'Error message',\n    'extensions' => [\n        'key' => 'value',\n    ],\n    'locations' => [\n        ['line' => 1, 'column' => 2],\n    ],\n    'path' => [\n        'listField',\n        0,\n        'fieldWithException',\n    ],\n];\n

Entry at key locations points to a character in query string which caused the error. In some cases (like deep fragment fields) locations will include several entries to track down the path to field with the error in query.

Entry at key path exists only for errors caused by exceptions thrown in resolvers. It contains a path from the very root field to actual field value producing an error (including indexes for list types and field names for composite types).

"},{"location":"error-handling/#internal-errors","title":"Internal errors","text":"

As of version 0.10.0, all exceptions thrown in resolvers are reported with generic message \"Internal server error\". This is done to avoid information leak in production environments (e.g. database connection errors, file access errors, etc).

Only exceptions implementing interface GraphQL\\Error\\ClientAware and claiming themselves as safe will be reported with a full error message.

For example:

use GraphQL\\Error\\ClientAware;\n\nclass MySafeException extends \\Exception implements ClientAware\n{\n    public function isClientSafe(): bool\n    {\n        return true;\n    }\n}\n

When such exception is thrown it will be reported with a full error message:

[\n    'message' => 'My reported error',\n    'locations' => [\n        ['line' => 10, 'column' => 2],\n    ],\n    'path' => [\n        'path',\n        'to',\n        'fieldWithException',\n    ]\n];\n

To change default \"Internal server error\" message to something else, use:

GraphQL\\Error\\FormattedError::setInternalErrorMessage(\"Unexpected error\");\n
"},{"location":"error-handling/#debugging-tools","title":"Debugging tools","text":"

During development or debugging, use DebugFlag::INCLUDE_DEBUG_MESSAGE to add hidden error messages each formatted error entry under the key extensions.debugMessage.

use GraphQL\\GraphQL;\nuse GraphQL\\Error\\DebugFlag;\n\n$result = GraphQL::executeQuery(/*args*/)\n    ->toArray(DebugFlag::INCLUDE_DEBUG_MESSAGE);\n

If you also want to add the exception trace, pass DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::INCLUDE_TRACE instead. This will make each error entry look like this:

[\n    'message' => 'Internal server error',\n    'locations' => [\n        ['line' => 10, 'column' => 2],\n    ],\n    'path' => [\n        'listField',\n        0,\n        'fieldWithException',\n    ],\n    'extensions' => [\n        'debugMessage' => 'Actual exception message',\n        'trace' => [\n            /* Formatted original exception trace */\n        ],\n    ],\n]\n

If you prefer the first resolver exception to be re-thrown, use the following flags:

use GraphQL\\GraphQL;\nuse GraphQL\\Error\\DebugFlag;\n\n$executionResult = GraphQL::executeQuery(/*args*/);\n\n// Will throw if there was an exception in resolver during execution\n$executionResult ->toArray(DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::RETHROW_INTERNAL_EXCEPTIONS);\n

If you only want to re-throw Exceptions that are not marked as safe through the ClientAware interface, use DebugFlag::RETHROW_UNSAFE_EXCEPTIONS.

"},{"location":"error-handling/#custom-error-handling-and-formatting","title":"Custom Error Handling and Formatting","text":"

It is possible to define custom formatter and handler for result errors.

Formatter is responsible for converting instances of GraphQL\\Error\\Error to an array. Handler is useful for error filtering and logging.

For example, these are default formatter and handler:

use GraphQL\\GraphQL;\nuse GraphQL\\Error\\Error;\nuse GraphQL\\Error\\FormattedError;\n\n$result = GraphQL::executeQuery(/* $args */)\n    ->setErrorFormatter(fn (Error $error): array => FormattedError::createFromException($error))\n    ->setErrorsHandler(fn (array $errors, callable $formatter): array => array_map($formatter, $errors))\n    ->toArray();\n

Note that when you pass debug flags to toArray() your custom formatter will still be decorated with same debugging information mentioned above.

"},{"location":"error-handling/#schema-errors","title":"Schema Errors","text":"

So far we only covered errors which occur during query execution process. Schema definition can also throw GraphQL\\Error\\InvariantViolation if there is an error in one of type definitions.

Usually such errors mean that there is some logical error in your schema. In this case it makes sense to return a status code 500 (Internal Server Error) for GraphQL endpoint:

use GraphQL\\GraphQL;\nuse GraphQL\\Type\\Schema;\nuse GraphQL\\Error\\FormattedError;\n\ntry {\n    $schema = new Schema([\n        // ...\n    ]);\n\n    $body = GraphQL::executeQuery($schema, $query);\n    $status = 200;\n} catch(\\Exception $e) {\n    $body = [\n        'errors' => [FormattedError::createFromException($e)]\n    ];\n    $status = 500;\n}\n\nheader('Content-Type: application/json', true, $status);\necho json_encode($body, JSON_THROW_ON_ERROR);\n
"},{"location":"executing-queries/","title":"Executing Queries","text":""},{"location":"executing-queries/#using-facade-method","title":"Using Facade Method","text":"

Query execution is a complex process involving multiple steps, including query parsing, validating and finally executing against your schema.

graphql-php provides a convenient facade for this process in class GraphQL\\GraphQL:

use GraphQL\\GraphQL;\n\n$result = GraphQL::executeQuery(\n    $schema,\n    $queryString,\n    $rootValue = null,\n    $context = null,\n    $variableValues = null,\n    $operationName = null,\n    $fieldResolver = null,\n    $validationRules = null\n);\n

It returns an instance of GraphQL\\Executor\\ExecutionResult which can be easily converted to array:

$serializableResult = $result->toArray();\n

Returned array contains data and errors keys, as described by the GraphQL spec. This array is suitable for further serialization (e.g. using json_encode). See also the section on error handling and formatting.

"},{"location":"executing-queries/#method-arguments","title":"Method arguments","text":"

Description of executeQuery method arguments:

Argument Type Notes schema GraphQL\\Type\\Schema Required. Instance of your application Schema queryString string or GraphQL\\Language\\AST\\DocumentNode Required. Actual GraphQL query string to be parsed, validated and executed. If you parse query elsewhere before executing - pass corresponding AST document here to avoid new parsing. rootValue mixed Any value that represents a root of your data graph. It is passed as the 1st argument to field resolvers of Query type. Can be omitted or set to null if actual root values are fetched by Query type itself. context mixed Any value that holds information shared between all field resolvers. Most often they use it to pass currently logged in user, locale details, etc.It will be available as the 3rd argument in all field resolvers. (see section on Field Definitions for reference) graphql-php never modifies this value and passes it as is to all underlying resolvers. variableValues array Map of variable values passed along with query string. See section on query variables on official GraphQL website. Note that while variableValues must be an associative array, the values inside it can be nested using \\stdClass if desired. operationName string Allows the caller to specify which operation in queryString will be run, in cases where queryString contains multiple top-level operations. fieldResolver callable A resolver function to use when one is not provided by the schema. If not provided, the default field resolver is used. validationRules array A set of rules for query validation step. The default value is all available rules. Empty array would allow skipping query validation (may be convenient for persisted queries which are validated before persisting and assumed valid during execution)"},{"location":"executing-queries/#using-server","title":"Using Server","text":"

If you are building HTTP GraphQL API, you may prefer our Standard Server (compatible with express-graphql). It supports more features out of the box, including parsing HTTP requests, producing a spec-compliant response; batched queries; persisted queries.

Usage example (with plain PHP):

use GraphQL\\Server\\StandardServer;\n\n$server = new StandardServer([/* server options, see below */]);\n$server->handleRequest(); // parses PHP globals and emits response\n

Server also supports PSR-7 request/response interfaces:

use GraphQL\\Server\\StandardServer;\nuse GraphQL\\Executor\\ExecutionResult;\nuse Psr\\Http\\Message\\RequestInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\StreamInterface;\n\n/** @var RequestInterface $psrRequest */\n/** @var ResponseInterface $psrResponse */\n/** @var StreamInterface $psrBodyStream */\n$server = new StandardServer([/* server options, see below */]);\n$psrResponse = $server->processPsrRequest($psrRequest, $psrResponse, $psrBodyStream);\n\n\n// Alternatively create PSR-7 response yourself:\n\n/** @var ExecutionResult|ExecutionResult[] $result */\n$result = $server->executePsrRequest($psrRequest);\n$jsonResult = json_encode($result, JSON_THROW_ON_ERROR);\n$psrResponse = new SomePsr7ResponseImplementation($jsonResult );\n

PSR-7 is useful when you want to integrate the server into existing framework:

  • PSR-7 for Laravel
  • Symfony PSR-7 Bridge
  • Slim
  • Laminas Mezzio
"},{"location":"executing-queries/#server-configuration-options","title":"Server configuration options","text":"Argument Type Notes schema Schema Required. Instance of your application Schema rootValue mixed Any value that represents a root of your data graph. It is passed as the 1st argument to field resolvers of Query type. Can be omitted or set to null if actual root values are fetched by Query type itself. context mixed Any value that holds information shared between all field resolvers. Most often they use it to pass currently logged in user, locale details, etc.It will be available as the 3rd argument in all field resolvers. (see section on Field Definitions for reference) graphql-php never modifies this value and passes it as is to all underlying resolvers. fieldResolver callable A resolver function to use when one is not provided by the schema. If not provided, the default field resolver is used. validationRules array or callable A set of rules for query validation step. The default value is all available rules. The empty array would allow skipping query validation (may be convenient for persisted queries which are validated before persisting and assumed valid during execution).Pass callable to return different validation rules for different queries (e.g. empty array for persisted query and a full list of rules for regular queries). When passed, it is expected to have the following signature: function (OperationParams $params, DocumentNode $node, $operationType): array queryBatching bool Flag indicating whether this server supports query batching (apollo-style). Defaults to false debugFlag int Debug flags. See docs on error debugging (flag values are the same). persistedQueryLoader callable A function which is called to fetch actual query when server encounters a queryId. The server does not implement persistence part (which you will have to build on your own), but it allows you to execute queries which were persisted previously. Expected function signature: function ($queryId, OperationParams $params) Function is expected to return query string or parsed DocumentNode Read more about persisted queries. errorFormatter callable Custom error formatter. See error handling docs. errorsHandler callable Custom errors handler. See error handling docs. promiseAdapter PromiseAdapter Required for Async PHP only."},{"location":"executing-queries/#using-config-class","title":"Using config class","text":"

If you prefer fluid interface for config with autocomplete in IDE and static time validation, use GraphQL\\Server\\ServerConfig instead of an array:

use GraphQL\\Server\\ServerConfig;\nuse GraphQL\\Server\\StandardServer;\n\n$config = ServerConfig::create()\n    ->setSchema($schema)\n    ->setErrorFormatter($myFormatter)\n    ->setDebugFlag($debug)\n;\n\n$server = new StandardServer($config);\n
"},{"location":"executing-queries/#query-batching","title":"Query batching","text":"

Standard Server supports query batching (apollo-style).

One of the major benefits of Server over a sequence of executeQuery() calls is that Deferred resolvers won't be isolated in queries. So for example following batch will require single DB request (if user field is deferred):

[\n{\n\"query\": \"{user(id: 1) { id }}\"\n},\n{\n\"query\": \"{user(id: 2) { id }}\"\n},\n{\n\"query\": \"{user(id: 3) { id }}\"\n}\n]\n

To enable query batching, pass queryBatching option in server config:

use GraphQL\\Server\\StandardServer;\n\n$server = new StandardServer([\n    'queryBatching' => true\n]);\n
"},{"location":"executing-queries/#custom-validation-rules","title":"Custom Validation Rules","text":"

Before execution, a query is validated using a set of standard rules defined by the GraphQL spec. It is possible to override standard set of rules globally or per execution.

Add rules globally:

use GraphQL\\Validator\\Rules;\nuse GraphQL\\Validator\\DocumentValidator;\n\n// Add to standard set of rules globally:\nDocumentValidator::addRule(new Rules\\DisableIntrospection());\n

Custom rules per execution:

use GraphQL\\GraphQL;\nuse GraphQL\\Validator\\Rules;\n\n$myValidationRules = array_merge(\n    GraphQL::getStandardValidationRules(),\n    [\n        new Rules\\QueryComplexity(100),\n        new Rules\\DisableIntrospection()\n    ]\n);\n\n$result = GraphQL::executeQuery(\n    $schema,\n    $queryString,\n    $rootValue = null,\n    $context = null,\n    $variableValues = null,\n    $operationName = null,\n    $fieldResolver = null,\n    $myValidationRules // <-- this will override global validation rules for this request\n);\n

Or with a standard server:

use GraphQL\\Server\\StandardServer;\n\n$server = new StandardServer([\n    'validationRules' => $myValidationRules\n]);\n
"},{"location":"getting-started/","title":"Getting Started","text":""},{"location":"getting-started/#prerequisites","title":"Prerequisites","text":"

This documentation assumes your familiarity with GraphQL concepts. If it is not the case - first learn about GraphQL on the official website.

"},{"location":"getting-started/#installation","title":"Installation","text":"

Using composer, run:

composer require webonyx/graphql-php\n
"},{"location":"getting-started/#upgrading","title":"Upgrading","text":"

We try to keep library releases backwards compatible when possible. For breaking changes we provide upgrade instructions.

"},{"location":"getting-started/#install-tools-optional","title":"Install Tools (optional)","text":"

While it is possible to communicate with GraphQL API using regular HTTP tools it is way more convenient for humans to use GraphiQL - an in-browser IDE for exploring GraphQL APIs.

It provides syntax-highlighting, auto-completion and auto-generated documentation for GraphQL API.

The easiest way to use it is to install one of the existing Google Chrome extensions:

  • ChromeiQL
  • GraphiQL Feen

Alternatively, you can follow instructions on the GraphiQL page and install it locally.

"},{"location":"getting-started/#hello-world","title":"Hello World","text":"

Let's create a type system that will be capable to process the following simple query:

query {\n  echo(message: \"Hello World\")\n}\n

We need an object type with the field echo:

use GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\Type;\n\n$queryType = new ObjectType([\n    'name' => 'Query',\n    'fields' => [\n        'echo' => [\n            'type' => Type::string(),\n            'args' => [\n                'message' => Type::nonNull(Type::string()),\n            ],\n            'resolve' => fn ($rootValue, array $args): string => $rootValue['prefix'] . $args['message'],\n        ],\n    ],\n]);\n

(Note: type definition can be expressed in different styles)

The interesting piece here is the resolve option of the field definition. It is responsible for returning a value of our field. Values of scalar fields will be directly included in the response while values of composite fields (objects, interfaces, unions) will be passed down to nested field resolvers (not in this example though).

Now when our type is ready, let's create a GraphQL endpoint file for it graphql.php:

use GraphQL\\GraphQL;\nuse GraphQL\\Type\\Schema;\n\n$schema = new Schema([\n    'query' => $queryType\n]);\n\n$rawInput = file_get_contents('php://input');\n$input = json_decode($rawInput, true);\n$query = $input['query'];\n$variableValues = isset($input['variables']) ? $input['variables'] : null;\n\ntry {\n    $rootValue = ['prefix' => 'You said: '];\n    $result = GraphQL::executeQuery($schema, $query, $rootValue, null, $variableValues);\n    $output = $result->toArray();\n} catch (\\Exception $e) {\n    $output = [\n        'errors' => [\n            [\n                'message' => $e->getMessage()\n            ]\n        ]\n    ];\n}\nheader('Content-Type: application/json');\necho json_encode($output, JSON_THROW_ON_ERROR);\n

Our example is finished. Try it by running:

php -S localhost:8080 graphql.php\ncurl http://localhost:8080 -d '{\"query\": \"query { echo(message: \\\"Hello World\\\") }\" }'\n

Check out the full source code of this example which also includes simple mutation.

Check out the blog example for something which is closer to real-world apps or read about the details of schema definition.

"},{"location":"getting-started/#next-steps","title":"Next Steps","text":"

Obviously hello world only scratches the surface of what is possible.

To learn by example, check out the blog example which is quite close to real-world GraphQL hierarchies.

For a deeper understanding of GraphQL in general, check out concepts.

To delve right into the implementation, see schema definition.

"},{"location":"schema-definition-language/","title":"Schema Definition Language","text":"

Since 0.9.0

The schema definition language is a convenient way to define your schema, especially with IDE autocompletion and syntax validation.

You can define this separate from your PHP code, e.g. in a schema.graphql file:

schema {\n  query: Query\n  mutation: Mutation\n}\n\ntype Query {\n  greetings(input: HelloInput!): String!\n}\n\ninput HelloInput {\n  firstName: String!\n  lastName: String\n}\n

In order to create schema instance out of this file, use GraphQL\\Utils\\BuildSchema:

use GraphQL\\Utils\\BuildSchema;\n\n$contents = file_get_contents('schema.graphql');\n$schema = BuildSchema::build($contents);\n

By default, such schema is created without any resolvers.

We have to rely on default field resolver and root value in order to execute a query against this schema.

"},{"location":"schema-definition-language/#defining-resolvers","title":"Defining resolvers","text":"

Since 0.10.0

In order to enable Interfaces, Unions and custom field resolvers you can pass the second argument: type config decorator to schema builder.

It accepts default type config produced by the builder and is expected to add missing options like resolveType for interface types or resolveField for object types.

use GraphQL\\Utils\\BuildSchema;\nuse GraphQL\\Language\\AST\\TypeDefinitionNode;\n\n$typeConfigDecorator = function (array $typeConfig, TypeDefinitionNode $typeDefinitionNode): array {\n    $name = $typeConfig['name'];\n    // ... add missing options to $typeConfig based on type $name\n    return $typeConfig;\n};\n\n$contents = file_get_contents('schema.graphql');\n$schema = BuildSchema::build($contents, $typeConfigDecorator);\n
"},{"location":"schema-definition-language/#performance-considerations","title":"Performance considerations","text":"

Since 0.10.0

Method build() produces a lazy schema automatically, so it works efficiently even with very large schemas.

But parsing type definition file on each request is suboptimal, so it is recommended to cache intermediate parsed representation of the schema for the production environment:

use GraphQL\\Language\\Parser;\nuse GraphQL\\Utils\\BuildSchema;\nuse GraphQL\\Utils\\AST;\n\n$cacheFilename = 'cached_schema.php';\n\nif (!file_exists($cacheFilename)) {\n    $document = Parser::parse(file_get_contents('./schema.graphql'));\n    file_put_contents($cacheFilename, \"<?php\\nreturn \" . var_export(AST::toArray($document), true) . \";\\n\");\n} else {\n    $document = AST::fromArray(require $cacheFilename); // fromArray() is a lazy operation as well\n}\n\n$typeConfigDecorator = function () {};\n$schema = BuildSchema::build($document, $typeConfigDecorator);\n
"},{"location":"schema-definition/","title":"Schema Definition","text":"

The schema is a container of your type hierarchy, which accepts root types in a constructor and provides methods for receiving information about your types to internal GraphQL tools.

In graphql-php, the schema is an instance of GraphQL\\Type\\Schema:

use GraphQL\\Type\\Schema;\n\n$schema = new Schema([\n    'query' => $queryType,\n    'mutation' => $mutationType,\n]);\n

See possible constructor options below.

"},{"location":"schema-definition/#query-and-mutation-types","title":"Query and Mutation types","text":"

The schema consists of two root types:

  • Query type is a surface of your read API
  • Mutation type (optional) exposes write API by declaring all possible mutations in your app.

Query and Mutation types are regular object types containing root-level fields of your API:

use GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\Type;\n\n$queryType = new ObjectType([\n    'name' => 'Query',\n    'fields' => [\n        'hello' => [\n            'type' => Type::string(),\n            'resolve' => fn () => 'Hello World!',\n        ],\n        'hero' => [\n            'type' => $characterInterface,\n            'args' => [\n                'episode' => [\n                    'type' => $episodeEnum,\n                ],\n            ],\n            'resolve' => fn ($rootValue, array $args): Hero => StarWarsData::getHero($args['episode'] ?? null),\n        ]\n    ]\n]);\n\n$mutationType = new ObjectType([\n    'name' => 'Mutation',\n    'fields' => [\n        'createReview' => [\n            'type' => $createReviewOutput,\n            'args' => [\n                'episode' => Type::nonNull($episodeEnum),\n                'review' => Type::nonNull($reviewInputObject),\n            ],\n            // TODO\n            'resolve' => fn ($rootValue, array $args): Review => StarWarsData::createReview($args['episode'], $args['review']),\n        ]\n    ]\n]);\n

Keep in mind that other than the special meaning of declaring a surface area of your API, those types are the same as any other object type, and their fields work exactly the same way.

Mutation type is also just a regular object type. The difference is in semantics. Field names of Mutation type are usually verbs and they almost always have arguments - quite often with complex input values (see Mutations and Input Types for details).

"},{"location":"schema-definition/#configuration-options","title":"Configuration Options","text":"

The schema constructor expects an instance of GraphQL\\Type\\SchemaConfig or an array with the following options:

Option Type Notes query ObjectType or callable(): ?ObjectType or null Required. Object type (usually named Query) containing root-level fields of your read API mutation ObjectType or callable(): ?ObjectType or null Object type (usually named Mutation) containing root-level fields of your write API subscription ObjectType or callable(): ?ObjectType or null Reserved for future subscriptions implementation. Currently presented for compatibility with introspection query of graphql-js, used by various clients (like Relay or GraphiQL) directives array<Directive> A full list of directives supported by your schema. By default, contains built-in @skip and @include directives. If you pass your own directives and still want to use built-in directives - add them explicitly. For example: array_merge(GraphQL::getStandardDirectives(), [$myCustomDirective]); types array<ObjectType> List of object types which cannot be detected by graphql-php during static schema analysis.Most often this happens when the object type is never referenced in fields directly but is still a part of a schema because it implements an interface which resolves to this object type in its resolveType callable. Note that you are not required to pass all of your types here - it is simply a workaround for a concrete use-case. typeLoader callable(string $name): Type Expected to return a type instance given the name. Must always return the same instance if called multiple times, see lazy loading. See section below on lazy type loading."},{"location":"schema-definition/#using-config-class","title":"Using config class","text":"

If you prefer a fluid interface for the config with auto-completion in IDE and static time validation, use GraphQL\\Type\\SchemaConfig instead of an array:

use GraphQL\\Type\\SchemaConfig;\nuse GraphQL\\Type\\Schema;\n\n$config = SchemaConfig::create()\n    ->setQuery($myQueryType)\n    ->setTypeLoader($myTypeLoader);\n\n$schema = new Schema($config);\n
"},{"location":"schema-definition/#lazy-loading-of-types","title":"Lazy loading of types","text":"

If your schema makes use of a large number of complex or dynamically-generated types, they can become a performance concern. There are a few best practices that can lessen their impact:

  1. Use a type registry. This will put you in a position to implement your own caching and lookup strategies, and GraphQL won't need to preload a map of all known types to do its work.

  2. Define each custom type as a callable that returns a type, rather than an object instance. Then, the work of instantiating them will only happen as they are needed by each query.

  3. Define all of your object fields as callbacks. If you're already doing #2 then this isn't needed, but it's a quick and easy precaution.

It is recommended to centralize this kind of functionality in a type registry. A typical example might look like the following:

// StoryType.php\nuse GraphQL\\Type\\Definition\\ObjectType;\n\nfinal class StoryType extends ObjectType\n{\n    public function __construct()\n    {\n        parent::__construct([\n            'fields' => static fn (): array => [\n                'author' => [\n                    'type' => Types::author(),\n                    'resolve' => static fn (Story $story): ?Author => DataSource::findUser($story->authorId),\n                ],\n            ],\n        ]);\n    }\n}\n\n// AuthorType.php\nuse GraphQL\\Type\\Definition\\ObjectType;\n\nfinal class AuthorType extends ObjectType\n{\n    public function __construct()\n    {\n        parent::__construct([\n            'description' => 'Writer of books',\n            'fields' => static fn (): array => [\n                'firstName' => Types::string(),\n            ],\n        ]);\n    }\n}\n\n// Types.php\nuse GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\NamedType;\n\nfinal class Types\n{\n    /** @var array<string, Type&NamedType> */\n    private static array $types = [];\n\n    /** @return Type&NamedType */\n    public static function load(string $typeName): Type\n    {\n        if (isset(self::$types[$typeName])) {\n            return self::$types[$typeName];\n        }\n\n        // For every type, this class must define a method with the same name\n        // but the first letter is in lower case.\n        $methodName = match ($typeName) {\n            'ID' => 'id',\n            default => lcfirst($typeName),\n        };\n        if (! method_exists(self::class, $methodName)) {\n            throw new \\Exception(\"Unknown GraphQL type: {$typeName}.\");\n        }\n\n        $type = self::{$methodName}(); // @phpstan-ignore-line variable static method call\n        if (is_callable($type)) {\n            $type = $type();\n        }\n\n        return self::$types[$typeName] = $type;\n    }\n\n    /** @return Type&NamedType */\n    private static function byClassName(string $className): Type\n    {\n        $classNameParts = explode('\\\\', $className);\n        $baseClassName = end($classNameParts);\n        // All type classes must use the suffix Type.\n        // This prevents name collisions between types and PHP keywords.\n        $typeName = preg_replace('~Type$~', '', $baseClassName);\n\n        // Type loading is very similar to PHP class loading, but keep in mind\n        // that the **typeLoader** must always return the same instance of a type.\n        // We can enforce that in our type registry by caching known types.\n        return self::$types[$typeName] ??= new $className;\n    }\n\n    /** @return \\Closure(): (Type&NamedType) */\n    private static function lazyByClassName(string $className): \\Closure\n    {\n        return static fn () => self::byClassName($className);\n    }\n\n    public static function boolean(): ScalarType { return Type::boolean(); }\n    public static function float(): ScalarType { return Type::float(); }\n    public static function id(): ScalarType { return Type::id(); }\n    public static function int(): ScalarType { return Type::int(); }\n    public static function string(): ScalarType { return Type::string(); }\n    public static function author(): callable { return self::lazyByClassName(AuthorType::class); }\n    public static function story(): callable { return self::lazyByClassName(StoryType::class); }\n    ...\n}\n\n// api/index.php\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$schema = new Schema([\n    'query' => new ObjectType([\n        'name' => 'Query',\n        'fields' => static fn() => [\n            'story' => [\n                'args'=>[\n                  'id' => Types::int(),\n                ],\n                'type' => Types::story(),\n                'description' => 'Returns my A',\n                'resolve' => static fn ($rootValue, array $args): ?Story => DataSource::findStory($args['id']),\n            ],\n        ],\n    ]),\n    'typeLoader' => Types::load(...),\n]);\n

A working demonstration of this kind of architecture can be found in the 01-blog sample.

"},{"location":"schema-definition/#schema-validation","title":"Schema Validation","text":"

By default, the schema is created with only shallow validation of type and field definitions (because validation requires a full schema scan and is very costly on bigger schemas).

There is a special method assertValid() on the schema instance which throws GraphQL\\Error\\InvariantViolation exception when it encounters any error, like:

  • Invalid types used for fields/arguments
  • Missing interface implementations
  • Invalid interface implementations
  • Other schema errors...

Schema validation is supposed to be used in CLI commands or during a build step of your app. Don't call it in web requests in production.

Usage example:

try {\n    $schema = new GraphQL\\Type\\Schema([\n        'query' => $myQueryType\n    ]);\n    $schema->assertValid();\n} catch (GraphQL\\Error\\InvariantViolation $e) {\n    echo $e->getMessage();\n}\n
"},{"location":"security/","title":"Security","text":""},{"location":"security/#protection-against-malicious-queries","title":"Protection Against Malicious Queries","text":"

GraphQL allows a large degree of dynamism and flexibility for clients to control what happens on the server during query execution. Malicious clients may abuse this by sending very deep and complex queries whose execution exhausts server resources.

At a basic level, it is recommended to limit the resources a single HTTP request can use through PHP settings such as:

  • max_execution_time
  • memory_limit
  • post_max_size

In addition, graphql-php offers security mechanisms that are specific to GraphQL.

"},{"location":"security/#query-complexity-analysis","title":"Query Complexity Analysis","text":"

This is a port of Query Complexity Analysis in Sangria.

Complexity analysis is a separate validation rule which calculates query complexity score before execution. Every field in the query gets a default score 1 (including ObjectType nodes). Total complexity of the query is the sum of all field scores. For example, the complexity of introspection query is 109.

If this score exceeds a threshold, a query is not executed and an error is returned instead.

Complexity analysis is disabled by default. You may enable it by setting a maximum query complexity:

use GraphQL\\GraphQL;\nuse GraphQL\\Validator\\Rules\\QueryComplexity;\nuse GraphQL\\Validator\\DocumentValidator;\n\n$rule = new QueryComplexity(100);\nDocumentValidator::addRule($rule);\n\nGraphQL::executeQuery(/*...*/);\n

This will set the rule globally. Alternatively, you can provide validation rules per execution.

To customize field score add complexity function to field definition:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$type = new ObjectType([\n    'name' => 'MyType',\n    'fields' => [\n        'someList' => [\n            'type' => Type::listOf(Type::string()),\n            'args' => [\n                'limit' => [\n                    'type' => Type::int(),\n                    'defaultValue' => 10\n                ]\n            ],\n            'complexity' => fn (int $childrenComplexity, array $args): int => $childrenComplexity * $args['limit'],\n        ]\n    ]\n]);\n
"},{"location":"security/#limiting-query-depth","title":"Limiting Query Depth","text":"

This is a port of Limiting Query Depth in Sangria.

This is a simpler approach that limits the nesting depth a query can have. For example, the depth of the default introspection query is 7.

This rule is disabled by default. You may enable it by setting a maximum query depth:

use GraphQL\\GraphQL;\nuse GraphQL\\Validator\\Rules\\QueryDepth;\nuse GraphQL\\Validator\\DocumentValidator;\n\n$rule = new QueryDepth(10);\nDocumentValidator::addRule($rule);\n\nGraphQL::executeQuery(/*...*/);\n

This will set the rule globally. Alternatively, you can provide validation rules per execution.

"},{"location":"security/#disabling-introspection","title":"Disabling Introspection","text":"

Introspection is a mechanism for fetching schema structure. It is used by tools like GraphiQL for auto-completion, query validation, etc.

Introspection is enabled by default. It means that anybody can get a full description of your schema by sending a special query containing meta fields __type and __schema .

If you are not planning to expose your API to the general public, it makes sense to disable this feature.

GraphQL PHP provides you separate validation rule which prohibits queries that contain __type or __schema fields. To disable introspection, add following rule:

use GraphQL\\GraphQL;\nuse GraphQL\\Validator\\Rules\\DisableIntrospection;\nuse GraphQL\\Validator\\DocumentValidator;\n\n$rule = new DisableIntrospection(DisableIntrospection::ENABLED);\nDocumentValidator::addRule($rule);\n\nGraphQL::executeQuery(/*...*/);\n

This will set the rule globally. Alternatively, you can provide validation rules per execution.

"},{"location":"type-definitions/","title":"Type Definitions","text":"

graphql-php represents a type as a class instance from the GraphQL\\Type\\Definition namespace:

  • ObjectType
  • InterfaceType
  • UnionType
  • InputObjectType
  • ScalarType
  • EnumType
"},{"location":"type-definitions/#input-vs-output-types","title":"Input vs. Output Types","text":"

All types in GraphQL are of two categories: input and output.

  • Output types (or field types) are: Scalar, Enum, Object, Interface, Union

  • Input types (or argument types) are: Scalar, Enum, Inputs

Obviously, NonNull and List types belong to both categories depending on their inner type.

"},{"location":"type-definitions/#definition-styles","title":"Definition styles","text":"

Several styles of type definitions are supported depending on your preferences.

"},{"location":"type-definitions/#inline-definitions","title":"Inline definitions","text":"
use GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\Type;\n\n$myType = new ObjectType([\n    'name' => 'MyType',\n    'fields' => [\n        'id' => Type::id()\n    ]\n]);\n
"},{"location":"type-definitions/#class-per-type","title":"Class per type","text":"
use GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\Type;\n\nclass MyType extends ObjectType\n{\n    public function __construct()\n    {\n        $config = [\n            // Note: 'name' is not needed in this form:\n            // it will be inferred from class name by omitting namespace and dropping \"Type\" suffix\n            'fields' => [\n                'id' => Type::id()\n            ]\n        ];\n        parent::__construct($config);\n    }\n}\n
"},{"location":"type-definitions/#schema-definition-language","title":"Schema definition language","text":"
schema {\n  query: Query\n  mutation: Mutation\n}\n\ntype Query {\n  greetings(input: HelloInput!): String!\n}\n\ninput HelloInput {\n  firstName: String!\n  lastName: String\n}\n

Read more about building an executable schema using schema definition language.

"},{"location":"type-definitions/directives/","title":"Directives","text":""},{"location":"type-definitions/directives/#built-in-directives","title":"Built-in directives","text":"

The directive is a way for a client to give GraphQL server additional context and hints on how to execute the query. The directive can be attached to a field or fragment and can affect the execution of the query in any way the server desires.

GraphQL specification includes two built-in directives:

  • @include(if: Boolean) Only include this field or fragment in the result if the argument is true
  • @skip(if: Boolean) Skip this field or fragment if the argument is true

For example:

query Hero($episode: Episode, $withFriends: Boolean!) {\n  hero(episode: $episode) {\n    name\n    friends @include(if: $withFriends) {\n      name\n    }\n  }\n}\n

Here if $withFriends variable is set to false - friends section will be ignored and excluded from the response. Important implementation detail: those fields will never be executed (not just removed from response after execution).

"},{"location":"type-definitions/directives/#custom-directives","title":"Custom directives","text":"

graphql-php supports custom directives even though their presence does not affect the execution of fields. You can use GraphQL\\Type\\Definition\\ResolveInfo in field resolvers to modify the output depending on those directives or perform statistics collection.

Other use case is your own query validation rules relying on custom directives.

In graphql-php custom directive is an instance of GraphQL\\Type\\Definition\\Directive (or one of its subclasses) which accepts an array of following options:

use GraphQL\\Language\\DirectiveLocation;\nuse GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\Directive;\n\n$trackDirective = new Directive([\n    'name' => 'track',\n    'description' => 'Instruction to record usage of the field by client',\n    'locations' => [\n        DirectiveLocation::FIELD,\n    ],\n    'args' => [\n        'details' => [\n            'type' => Type::string(),\n            'description' => 'String with additional details of field usage scenario',\n            'defaultValue' => ''\n        ]\n    ]\n]);\n

See possible directive locations in GraphQL\\Language\\DirectiveLocation.

"},{"location":"type-definitions/enums/","title":"Enum Type Definition","text":"

Enumeration types are a special kind of scalar that is restricted to a particular set of allowed values.

"},{"location":"type-definitions/enums/#writing-enum-types","title":"Writing Enum Types","text":"

In graphql-php enum type is an instance of GraphQL\\Type\\Definition\\EnumType which accepts configuration array in constructor:

use GraphQL\\Type\\Definition\\EnumType;\n\n$episodeEnum = new EnumType([\n    'name' => 'Episode',\n    'description' => 'One of the films in the Star Wars Trilogy',\n    'values' => [\n        'NEWHOPE' => [\n            'value' => 4,\n            'description' => 'Released in 1977.'\n        ],\n        'EMPIRE' => [\n            'value' => 5,\n            'description' => 'Released in 1980.'\n        ],\n        'JEDI' => [\n            'value' => 6,\n            'description' => 'Released in 1983.'\n        ],\n    ]\n]);\n

This example uses an inline style for Enum Type definition, but you can also use inheritance or schema definition language.

"},{"location":"type-definitions/enums/#configuration-options","title":"Configuration options","text":"

Enum Type constructor accepts an array with following options:

Option Type Notes name string Required. Name of the type. When not set - inferred from array key (read about shorthand field definition below) description string Plain-text description of the type for clients (e.g. used by GraphiQL for auto-generated documentation) values array List of enumerated items, see below for expected structure of each entry

Each entry of values array in turn accepts following options:

Option Type Notes name string Required. Name of the item. When not set - inferred from array key (read about shorthand field definition below) value mixed Internal representation of enum item in your application (could be any value, including complex objects or callbacks) description string Plain-text description of enum value for clients (e.g. used by GraphiQL for auto-generated documentation) deprecationReason string Text describing why this enum value is deprecated. When not empty - item will not be returned by introspection queries (unless forced)"},{"location":"type-definitions/enums/#construction-from-php-enum","title":"Construction from PHP enum","text":"

You can reuse your existing PHP enums in GraphQL. Leverage PHP attributes to add descriptions and deprecate values:

use GraphQL\\Type\\Definition\\Deprecated;\nuse GraphQL\\Type\\Definition\\Description;\n\n#[Description(description: 'Sweet and juicy.')]\nenum Fruit\n{\n    #[Description(description: 'Rich in potassium.')]\n    case BANANA;\n\n    #[Deprecated(reason: 'Too sour.')]\n    case CITRON;\n}\n

PhpDocs will be used as descriptions if present, but are overridden by the Description attribute.

Wrap them with GraphQL\\Type\\Definition\\PhpEnumType to use them in a GraphQL schema:

use GraphQL\\Type\\Definition\\PhpEnumType;\n\n$fruitEnumType = new PhpEnumType(Fruit::class);\n

The following type will be deduced from Fruit:

\"\"\"\nSweet and juicy.\n\"\"\"\nenum Fruit {\n  \"\"\"\n  Rich in potassium.\n  \"\"\"\n  BANANA\n\n  CITRON @deprecated(reason: \"Too sour.\")\n}\n

Conversion rules:

  • Client-given arguments will be converted to enum instances
  • Returned output values are expected to be enum instances
"},{"location":"type-definitions/enums/#shorthand-definitions","title":"Shorthand definitions","text":"

If internal representation of enumerated item is the same as item name, then you can use following shorthand for definition:

use GraphQL\\Type\\Definition\\EnumType;\n\n$episodeEnum = new EnumType([\n    'name' => 'Episode',\n    'description' => 'One of the films in the Star Wars Trilogy',\n    'values' => ['NEWHOPE', 'EMPIRE', 'JEDI']\n]);\n

which is equivalent of:

use GraphQL\\Type\\Definition\\EnumType;\n\n$episodeEnum = new EnumType([\n    'name' => 'Episode',\n    'description' => 'One of the films in the Star Wars Trilogy',\n    'values' => [\n        'NEWHOPE' => ['value' => 'NEWHOPE'],\n        'EMPIRE' => ['value' => 'EMPIRE'],\n        'JEDI' => ['value' => 'JEDI']\n    ]\n]);\n

which is in turn equivalent of the full form:

use GraphQL\\Type\\Definition\\EnumType;\n\n$episodeEnum = new EnumType([\n    'name' => 'Episode',\n    'description' => 'One of the films in the Star Wars Trilogy',\n    'values' => [\n        ['name' => 'NEWHOPE', 'value' => 'NEWHOPE'],\n        ['name' => 'EMPIRE', 'value' => 'EMPIRE'],\n        ['name' => 'JEDI', 'value' => 'JEDI']\n    ]\n]);\n
"},{"location":"type-definitions/enums/#field-resolution","title":"Field Resolution","text":"

When object field is of Enum Type, field resolver is expected to return an internal representation of corresponding Enum item (value in config). graphql-php will then serialize this value to name to include in response:

use GraphQL\\Type\\Definition\\EnumType;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$episodeEnum = new EnumType([\n    'name' => 'Episode',\n    'description' => 'One of the films in the Star Wars Trilogy',\n    'values' => [\n        'NEWHOPE' => [\n            'value' => 4,\n            'description' => 'Released in 1977.'\n        ],\n        'EMPIRE' => [\n            'value' => 5,\n            'description' => 'Released in 1980.'\n        ],\n        'JEDI' => [\n            'value' => 6,\n            'description' => 'Released in 1983.'\n        ],\n    ]\n]);\n\n$heroType = new ObjectType([\n    'name' => 'Hero',\n    'fields' => [\n        'appearsIn' => [\n            'type' => $episodeEnum,\n             // Actual entry in response will be 'appearsIn' => 'EMPIRE'\n            'resolve' => fn (): int => 5,\n        ]\n    ]\n]);\n

The Reverse is true when the enum is used as input type (e.g. as field argument). GraphQL will treat enum input as name and convert it into value before passing to your app.

For example, given object type definition:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$heroType = new ObjectType([\n    'name' => 'Hero',\n    'fields' => [\n        'appearsIn' => [\n            'type' => Type::boolean(),\n            'args' => [\n                'episode' => Type::nonNull($enumType)\n            ],\n            'resolve' => fn ($hero, array $args): bool => $args['episode'] === 5,\n        ]\n    ]\n]);\n

Then following query:

fragment on Hero {\n    appearsInNewHope: appearsIn(NEWHOPE)\n    appearsInEmpire: appearsIn(EMPIRE)\n}\n

will return:

[\n    'appearsInNewHope' => false,\n    'appearsInEmpire' => true\n]\n
"},{"location":"type-definitions/inputs/","title":"Input Object Type Definition","text":"

The GraphQL specification defines Input Object Type for complex inputs. It is similar to ObjectType except that it's fields have no args or resolve options and their type must be input type.

"},{"location":"type-definitions/inputs/#writing-input-object-types","title":"Writing Input Object Types","text":"

In graphql-php Input Object Type is an instance of GraphQL\\Type\\Definition\\InputObjectType (or one of its subclasses) which accepts configuration array in its constructor:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\InputObjectType;\n\n$filters = new InputObjectType([\n    'name' => 'StoryFiltersInput',\n    'fields' => [\n        'author' => [\n            'type' => Type::id(),\n            'description' => 'Only show stories with this author id'\n        ],\n        'popular' => [\n            'type' => Type::boolean(),\n            'description' => 'Only show popular stories (liked by several people)'\n        ],\n        'tags' => [\n            'type' => Type::listOf(Type::string()),\n            'description' => 'Only show stories which contain all of those tags'\n        ]\n    ]\n]);\n

Every field may be of other InputObjectType (thus complex hierarchies of inputs are possible)

"},{"location":"type-definitions/inputs/#configuration-options","title":"Configuration options","text":"

The constructor of InputObjectType accepts an array with the following options:

Option Type Notes name string Required. Unique name of this object type within Schema fields array or callable Required. An array describing object fields or callable returning such an array (see below). description string Plain-text description of this type for clients (e.g. used by GraphiQL for auto-generated documentation) parseValue callable(array<string, mixed>): mixed Converts incoming values from their array representation to something else (e.g. a value object)

Every field is an array with following entries:

Option Type Notes name string Required. Name of the input field. When not set - inferred from fields array key type Type Required. Instance of one of Input Types (Scalar, Enum, InputObjectType + any combination of those with nonNull and listOf modifiers) description string Plain-text description of this input field for clients (e.g. used by GraphiQL for auto-generated documentation) defaultValue scalar Default value of this input field. Use the internal value if specifying a default for an enum type"},{"location":"type-definitions/inputs/#using-input-object-type","title":"Using Input Object Type","text":"

In the example above we defined our InputObjectType. Now let's use it in one of field arguments:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$queryType = new ObjectType([\n    'name' => 'Query',\n    'fields' => [\n        'stories' => [\n            'type' => Type::listOf($storyType),\n            'args' => [\n                'filters' => [\n                    'type' => $filters,\n                    'defaultValue' => [\n                        'popular' => true\n                    ]\n                ]\n            ],\n            'resolve' => fn ($rootValue, array $args): array => DataSource::filterStories($args['filters']),\n        ]\n    ]\n]);\n

(note that you can define defaultValue for fields with complex inputs as associative array).

Then GraphQL query could include filters as literal value:

{\n  stories(filters: { author: \"1\", popular: false })\n}\n

Or as query variable:

query ($filters: StoryFiltersInput!) {\n  stories(filters: $filters)\n}\n
$variables = [\n    'filters' => [\n        \"author\" => \"1\",\n        \"popular\" => false\n    ]\n];\n

graphql-php will validate the input against your InputObjectType definition and pass it to your resolver as $args['filters']

"},{"location":"type-definitions/inputs/#converting-input-object-array-to-value-object","title":"Converting input object array to value object","text":"

If you want more type safety you can choose to parse the input array into a value object.

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\InputObjectType;\n\nfinal class StoryFiltersInput\n{\n    public string $author;\n    public ?bool $popular;\n    public array $tags;\n\n    public function __construct(string $author, ?bool $popular, array $tags)\n    {\n        $this->author = $author;\n        $this->popular = $popular;\n        $this->tag = $tag;\n    }\n}\n\n$filters = new InputObjectType([\n    'name' => 'StoryFiltersInput',\n    'fields' => [\n        'author' => [\n            'type' => Type::nonNull(Type::string()),\n        ],\n        'popular' => [\n            'type' => Type::boolean(),\n        ],\n        'tags' => [\n            'type' => Type::nonNull(Type::listOf(Type::string())),\n        ]\n    ],\n    'parseValue' => fn(array $values) => new StoryFiltersInput(\n        $values['author'],\n        $values['popular'] ?? null,\n        $values['tags']\n    ),\n]);\n

The value of $args['filters'] will now be an instance of StoryFiltersInput.

The incoming values are converted using a depth-first traversal. Thus, nested input values will be passed through their respective parseValue functions before the parent receives their value.

"},{"location":"type-definitions/interfaces/","title":"Interface Type Definition","text":"

An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.

"},{"location":"type-definitions/interfaces/#writing-interface-types","title":"Writing Interface Types","text":"

In graphql-php interface type is an instance of GraphQL\\Type\\Definition\\InterfaceType (or one of its subclasses) which accepts configuration array in a constructor:

use GraphQL\\Type\\Definition\\InterfaceType;\nuse GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\Type;\n\n$character = new InterfaceType([\n    'name' => 'Character',\n    'description' => 'A character in the Star Wars Trilogy',\n    'fields' => [\n        'id' => [\n            'type' => Type::nonNull(Type::string()),\n            'description' => 'The id of the character.',\n        ],\n        'name' => [\n            'type' => Type::string(),\n            'description' => 'The name of the character.'\n        ]\n    ],\n    'resolveType' => function ($value): ObjectType {\n        switch ($value->type ?? null) {\n            case 'human': return MyTypes::human();\n            case 'droid': return MyTypes::droid();\n            default: throw new Exception(\"Unknown Character type: {$value->type ?? null}\");\n        }\n    }\n]);\n

This example uses inline style for Interface definition, but you can also use inheritance or schema definition language.

"},{"location":"type-definitions/interfaces/#configuration-options","title":"Configuration options","text":"

The constructor of InterfaceType accepts an array. Below is a full list of allowed options:

Option Type Notes name string Required. Unique name of this interface type within Schema fields array Required. List of fields required to be defined by interface implementors. Same as Fields for Object Type description string Plain-text description of this type for clients (e.g. used by GraphiQL for auto-generated documentation) resolveType callback function ($value, $context, ResolveInfo $info) Receives $value from resolver of the parent field and returns concrete interface implementor for this $value."},{"location":"type-definitions/interfaces/#implementing-interface","title":"Implementing interface","text":"

To implement the Interface simply add it to interfaces array of Object Type definition:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$humanType = new ObjectType([\n    'name' => 'Human',\n    'fields' => [\n        'id' => [\n            'type' => Type::nonNull(Type::string()),\n            'description' => 'The id of the character.',\n        ],\n        'name' => [\n            'type' => Type::string(),\n            'description' => 'The name of the character.'\n        ]\n    ],\n    'interfaces' => [\n        $character\n    ]\n]);\n

Note that Object Type must include all fields of interface with exact same types (including nonNull specification) and arguments.

The only exception is when object's field type is more specific than the type of this field defined in interface (see Covariant return types for interface fields below)

"},{"location":"type-definitions/interfaces/#covariant-return-types-for-interface-fields","title":"Covariant return types for interface fields","text":"

Object types implementing interface may change the field type to more specific. Example:

interface A {\n  field1: A\n}\n\ntype B implements A {\n  field1: B\n}\n
"},{"location":"type-definitions/interfaces/#sharing-interface-fields","title":"Sharing Interface fields","text":"

Since every Object Type implementing an Interface must have the same set of fields - it often makes sense to reuse field definitions of Interface in Object Types:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$humanType = new ObjectType([\n    'name' => 'Human',\n    'interfaces' => [\n        $character\n    ],\n    'fields' => [\n        $character->getField('id'),\n        $character->getField('name'),\n        [\n            'name' => 'height',\n            'type' => Type::float(),\n        ],\n    ]\n]);\n

In this case, field definitions are created only once (as a part of Interface Type) and then reused by all interface implementors. It can save several microseconds and kilobytes + ensures that field definitions of Interface and implementors are always in sync.

Yet it creates a problem with the resolution of such fields. There are two ways how shared fields could be resolved:

  1. If field resolution algorithm is the same for all Interface implementors - you can simply add resolve option to field definition in Interface itself.

  2. If field resolution varies for different implementations - you can specify resolveField option in Object Type config and handle field resolutions there (Note: resolve option in field definition has precedence over resolveField option in object type definition)

"},{"location":"type-definitions/interfaces/#interface-role-in-data-fetching","title":"Interface role in data fetching","text":"

The only responsibility of interface in Data Fetching process is to return concrete Object Type for given $value in resolveType. Then resolution of fields is delegated to resolvers of this concrete Object Type.

If a resolveType option is omitted, graphql-php will loop through all interface implementors and use their isTypeOf callback to pick the first suitable one. This is obviously less efficient than single resolveType call. So it is recommended to define resolveType whenever possible.

"},{"location":"type-definitions/interfaces/#prevent-invisible-types","title":"Prevent invisible types","text":"

When object types that implement an interface are not directly referenced by a field, they cannot be discovered during schema introspection. For example:

type Query {\n    animal: Animal\n}\n\ninterface Animal {...}\n\ntype Cat implements Animal {...}\ntype Dog implements Animal {...}\n

In this example, Cat and Dog would be considered invisible types. Querying the animal field would fail, since no possible implementing types for Animal can be found.

There are two possible solutions:

  1. Add fields that reference the invisible types directly, e.g.:
type Query {\n  dog: Dog\n  cat: Cat\n}\n
  1. Pass the invisible types during schema construction, e.g.:
new GraphQLSchema([\n    'query' => ...,\n    'types' => [$cat, $dog]\n]);\n
"},{"location":"type-definitions/lists-and-nonnulls/","title":"Lists and Non-Nulls","text":""},{"location":"type-definitions/lists-and-nonnulls/#lists","title":"Lists","text":"

graphql-php provides built-in support for lists. In order to create list type - wrap existing type with GraphQL\\Type\\Definition\\Type::listOf() modifier:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$userType = new ObjectType([\n    'name' => 'User',\n    'fields' => [\n        'emails' => [\n            'type' => Type::listOf(Type::string()),\n            'resolve' => fn (): array => [\n                'jon@example.com',\n                'jonny@example.com'\n            ],\n        ]\n    ]\n]);\n

Resolvers for such fields are expected to return array or instance of PHP's built-in Traversable interface (null is allowed by default too).

If returned value is not of one of these types - graphql-php will add an error to result and set the field value to null (only if the field is nullable, see below for non-null fields).

"},{"location":"type-definitions/lists-and-nonnulls/#non-nulls","title":"Non-Nulls","text":"

By default, every field or argument can have a null value. To indicate the value must be non-null use the GraphQL\\Type\\Definition\\Type::nonNull() modifier:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$humanType = new ObjectType([\n    'name' => 'User',\n    'fields' => [\n        'id' => [\n            'type' => Type::nonNull(Type::id()),\n            'resolve' => fn (): string => uniqid(),\n        ],\n        'emails' => [\n            'type' => Type::nonNull(Type::listOf(Type::string())),\n            'resolve' => fn (): array => [\n                'jon@example.com',\n                'jonny@example.com'\n            ],\n        ]\n    ]\n]);\n

If resolver of non-null field returns null, graphql-php will add an error to result and exclude the whole object from the output (an error will bubble to first nullable parent field which will be set to null).

Read the section on Data Fetching for details.

"},{"location":"type-definitions/object-types/","title":"Object Type Definition","text":"

Object Type is the most frequently used primitive in a typical GraphQL application.

Conceptually Object Type is a collection of Fields. Each field, in turn, has its own type which allows building complex hierarchies.

"},{"location":"type-definitions/object-types/#writing-object-types","title":"Writing Object Types","text":"

In graphql-php object type is an instance of GraphQL\\Type\\Definition\\ObjectType (or one of its subclasses) which accepts a configuration array in its constructor:

use GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Examples\\Blog\\Data\\DataSource;\nuse GraphQL\\Examples\\Blog\\Data\\Story;\n\n$userType = new ObjectType([\n    'name' => 'User',\n    'description' => 'Our blog visitor',\n    'fields' => [\n        'firstName' => [\n            'type' => Type::string(),\n            'description' => 'User first name'\n        ],\n        'email' => Type::string()\n    ]\n]);\n\n$blogStory = new ObjectType([\n    'name' => 'Story',\n    'fields' => [\n        'body' => Type::string(),\n        'author' => [\n            'type' => $userType,\n            'description' => 'Story author',\n            'resolve' => fn (Story $blogStory): ?User => DataSource::findUser($blogStory->authorId),\n        ],\n        'likes' => [\n            'type' => Type::listOf($userType),\n            'description' => 'List of users who liked the story',\n            'args' => [\n                'limit' => [\n                    'type' => Type::int(),\n                    'description' => 'Limit the number of recent likes returned',\n                    'defaultValue' => 10\n                ]\n            ],\n            'resolve' => fn (Story $blogStory, array $args): array => DataSource::findLikes($blogStory->id, $args['limit']),\n        ]\n    ]\n]);\n

This example uses inline style for Object Type definitions, but you can also use inheritance or schema definition language.

"},{"location":"type-definitions/object-types/#configuration-options","title":"Configuration options","text":"Option Type Notes name string Required. Unique name of this object type within Schema fields array or callable Required. An array describing object fields or callable returning such an array. See field configuration options section below for expected structure of each array entry. See also the section on Circular types for an explanation of when to use callable for this option. description string Plain-text description of this type for clients (e.g. used by GraphiQL for auto-generated documentation) interfaces array or callable List of interfaces implemented by this type or callable returning such a list. See Interface Types for details. See also the section on Circular types for an explanation of when to use callable for this option. isTypeOf callable function ($value, $context, ResolveInfo $info): bool Expected to return true if $value qualifies for this type (see section about Abstract Type Resolution for explanation). resolveField callable function ($value, array $args, $context, ResolveInfo $info): mixed Given the $value of this type, it is expected to return value for a field defined in $info->fieldName. A good place to define a type-specific strategy for field resolution. See section on Data Fetching for details. visible bool or callable Defaults to true. The given callable receives no arguments and is expected to return a bool, it is called once when the field may be accessed. The field is treated as if it were not defined at all when this is false."},{"location":"type-definitions/object-types/#field-configuration-options","title":"Field configuration options","text":"Option Type Notes name string Required. Name of the field. When not set - inferred from fields array key (read about shorthand field definition below) type Type Required. An instance of internal or custom type. Note: type must be represented by a single instance within one schema (see also lazy loading of types) args array An array describing any number of possible field arguments, each element being an array. See field argument configuration options. resolve callable function ($objectValue, array $args, $context, ResolveInfo $info): mixed Given the $objectValue of this type, it is expected to return actual value of the current field. See section on Data Fetching for details complexity callable function (int $childrenComplexity, array $args): int Used to restrict query complexity. The feature is disabled by default, read about Security to use it. description string Plain-text description of this field for clients (e.g. used by GraphiQL for auto-generated documentation) deprecationReason string Text describing why this field is deprecated. When not empty - field will not be returned by introspection queries (unless forced)"},{"location":"type-definitions/object-types/#field-argument-configuration-options","title":"Field argument configuration options","text":"Option Type Notes name string Required. Name of the argument. When not set - inferred from args array key type Type Required. Instance of one of Input Types (scalar, enum, InputObjectType + any combination of those with nonNull and listOf modifiers) description string Plain-text description of this argument for clients (e.g. used by GraphiQL for auto-generated documentation) defaultValue scalar Default value for this argument. Use the internal value if specifying a default for an enum type"},{"location":"type-definitions/object-types/#shorthand-field-definitions","title":"Shorthand field definitions","text":"

Fields can be also defined in shorthand notation (with only name and type options):

'fields' => [\n    'id' => Type::id(),\n    'fieldName' => $fieldType\n]\n

which is equivalent of:

'fields' => [\n    'id' => ['type' => Type::id()],\n    'fieldName' => ['type' => $fieldName]\n]\n

which is in turn equivalent of the full form:

'fields' => [\n    ['name' => 'id', 'type' => Type::id()],\n    ['name' => 'fieldName', 'type' => $fieldName]\n]\n

Same shorthand notation applies to field arguments as well.

"},{"location":"type-definitions/object-types/#recurring-and-circular-types","title":"Recurring and circular types","text":"

Almost all real-world applications contain recurring or circular types. Think user friends or nested comments for example.

graphql-php allows such types, but you have to use callable in option fields (and/or interfaces).

For example:

use GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\n$userType = new ObjectType([\n    'name' => 'User',\n    'fields' => function () use (&$userType): array {\n        return [\n            'email' => [\n                'type' => Type::string()\n            ],\n            'friends' => [\n                'type' => Type::listOf($userType)\n            ]\n        ];\n    },\n]);\n

Same example for inheritance style of type definitions using a type registry (see lazy loading of types):

use GraphQL\\Type\\Definition\\ListOfType;\nuse GraphQL\\Type\\Definition\\ScalarType;\nuse GraphQL\\Type\\Definition\\Type;\nuse GraphQL\\Type\\Definition\\ObjectType;\n\nclass UserType extends ObjectType\n{\n    public function __construct()\n    {\n        parent::__construct([\n            'fields' => [\n                'email' => fn (): ScalarType => MyTypes::string(),\n                'friends' => fn (): ListOfType => MyTypes::listOf(MyTypes::user())\n            ],\n        ]);\n    }\n}\n\nclass MyTypes\n{\n    private static UserType $user;\n\n    public static function user(): UserType\n    {\n        return self::$user ??= new UserType();\n    }\n\n    public static function string(): ScalarType\n    {\n        return Type::string();\n    }\n\n    public static function listOf($type): ListOfType\n    {\n        return Type::listOf($type);\n    }\n}\n
"},{"location":"type-definitions/object-types/#field-resolution","title":"Field Resolution","text":"

Field resolution is the primary mechanism in graphql-php for returning actual data for your fields. It is implemented using resolveField callable in type definition or resolve callable in field definition (which has precedence).

Read the section on Data Fetching for a complete description of this process.

"},{"location":"type-definitions/object-types/#custom-metadata","title":"Custom Metadata","text":"

All types in graphql-php accept configuration array. In some cases, you may be interested in passing your own metadata for type or field definition.

graphql-php preserves original configuration array in every type or field instance in public property $config. Use it to implement app-level mappings and definitions.

"},{"location":"type-definitions/scalars/","title":"Scalar Type Definition","text":"

Scalar types represent primitive leaf values in a GraphQL type system. When object fields have to resolve to some concrete data, that's where the scalar types come in.

"},{"location":"type-definitions/scalars/#built-in-scalar-types","title":"Built-in Scalar Types","text":"

The GraphQL specification describes several built-in scalar types. In graphql-php they are exposed as static methods of the class GraphQL\\Type\\Definition\\Type:

use GraphQL\\Type\\Definition\\Type;\n\n// Built-in Scalar types:\nType::string();  // String type\nType::int();     // Int type\nType::float();   // Float type\nType::boolean(); // Boolean type\nType::id();      // ID type\n

Those methods return instances of a subclass of GraphQL\\Type\\Definition\\ScalarType. Use them directly in type definitions or wrapped in a type registry (see lazy loading of types).

"},{"location":"type-definitions/scalars/#writing-custom-scalar-types","title":"Writing Custom Scalar Types","text":"

In addition to built-in scalars, you can define your own scalar types with additional validation. Typical examples of such types are Email, Date, Url, etc.

In order to implement your own type, you must understand how scalars are handled in GraphQL. GraphQL deals with scalars in the following cases:

  1. Convert the internal representation of a value, returned by your app (e.g. stored in a database or hardcoded in the source code), to a serialized representation included in the response.

  2. Convert an input value, passed by a client in variables along with a GraphQL query, to its internal representation used in your application.

  3. Convert an input literal value, hardcoded in a GraphQL query (e.g. field argument value), to its internal representation used in your application.

Those cases are covered by the methods serialize, parseValue and parseLiteral of the abstract class ScalarType respectively.

Here is an example of a simple Email type:

use GraphQL\\Error\\Error;\nuse GraphQL\\Error\\InvariantViolation;\nuse GraphQL\\Language\\AST\\StringValueNode;\nuse GraphQL\\Type\\Definition\\ScalarType;\nuse GraphQL\\Utils\\Utils;\n\nclass EmailType extends ScalarType\n{\n    // Note: name can be omitted. In this case it will be inferred from class name\n    // (suffix \"Type\" will be dropped)\n    public string $name = 'Email';\n\n    public function serialize($value)\n    {\n        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {\n            throw new InvariantViolation(\"Could not serialize following value as email: \" . Utils::printSafe($value));\n        }\n\n        return $this->parseValue($value);\n    }\n\n    public function parseValue($value)\n    {\n        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {\n            throw new Error(\"Cannot represent following value as email: \" . Utils::printSafeJson($value));\n        }\n\n        return $value;\n    }\n\n    public function parseLiteral(Node $valueNode, ?array $variables = null)\n    {\n        // Throw GraphQL\\Error\\Error vs \\UnexpectedValueException to locate the error in the query\n        if (!$valueNode instanceof StringValueNode) {\n            throw new Error('Query error: Can only parse strings got: ' . $valueNode->kind, [$valueNode]);\n        }\n\n        if (!filter_var($valueNode->value, FILTER_VALIDATE_EMAIL)) {\n            throw new Error(\"Not a valid email\", [$valueNode]);\n        }\n\n        return $valueNode->value;\n    }\n}\n

Or with inline style:

use GraphQL\\Type\\Definition\\CustomScalarType;\n\n$emailType = new CustomScalarType([\n    'name' => 'Email',\n    'serialize' => static function ($value) {/* See function body above */},\n    'parseValue' => static function ($value) {/* See function body above */},\n    'parseLiteral' => static function (Node $valueNode, ?array $variables = null) {/* See function body above */},\n]);\n

Keep in mind the passed functions will be called statically, so a passed in callable such as [Foo::class, 'bar'] should only reference static class methods.

"},{"location":"type-definitions/unions/","title":"Union Type Definition","text":"

A Union is an abstract type that simply enumerates other Object Types. The value of Union Type is actually a value of one of included Object Types.

"},{"location":"type-definitions/unions/#writing-union-types","title":"Writing Union Types","text":"

In graphql-php union type is an instance of GraphQL\\Type\\Definition\\UnionType (or one of its subclasses) which accepts configuration array in a constructor:

use GraphQL\\Type\\Definition\\ObjectType;\nuse GraphQL\\Type\\Definition\\UnionType;\n\n$searchResultType = new UnionType([\n    'name' => 'SearchResult',\n    'types' => [\n        MyTypes::story(),\n        MyTypes::user()\n    ],\n    'resolveType' => function ($value): ObjectType {\n        switch ($value->type ?? null) {\n            case 'story': return MyTypes::story();\n            case 'user': return MyTypes::user();\n            default: throw new Exception(\"Unexpected SearchResult type: {$value->type ?? null}\");\n        }\n    },\n]);\n

This example uses inline style for Union definition, but you can also use inheritance or schema definition language.

"},{"location":"type-definitions/unions/#configuration-options","title":"Configuration options","text":"

The constructor of UnionType accepts an array. Below is a full list of allowed options:

Option Type Notes name string Required. Unique name of this interface type within Schema types array Required. List of Object Types included in this Union. Note that you can't create a Union type out of Interfaces or other Unions. description string Plain-text description of this type for clients (e.g. used by GraphiQL for auto-generated documentation) resolveType callback function ($value, $context, ResolveInfo $info): ObjectType Receives $value from resolver of the parent field and returns concrete Object Type for this $value."}]} \ No newline at end of file diff --git a/security/index.html b/security/index.html new file mode 100644 index 000000000..0af6c3399 --- /dev/null +++ b/security/index.html @@ -0,0 +1,788 @@ + + + + + + + + + + + + + + + + + + + + + + + + Security - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Security

+ +

Protection Against Malicious Queries

+

GraphQL allows a large degree of dynamism and flexibility for clients to control what happens on the server during query execution. +Malicious clients may abuse this by sending very deep and complex queries whose execution exhausts server resources.

+

At a basic level, it is recommended to limit the resources a single HTTP request can use through PHP settings such as:

+ +

In addition, graphql-php offers security mechanisms that are specific to GraphQL.

+

Query Complexity Analysis

+

This is a port of Query Complexity Analysis in Sangria.

+

Complexity analysis is a separate validation rule which calculates query complexity score before execution. +Every field in the query gets a default score 1 (including ObjectType nodes). Total complexity of the +query is the sum of all field scores. For example, the complexity of introspection query is 109.

+

If this score exceeds a threshold, a query is not executed and an error is returned instead.

+

Complexity analysis is disabled by default. You may enable it by setting a maximum query complexity:

+
use GraphQL\GraphQL;
+use GraphQL\Validator\Rules\QueryComplexity;
+use GraphQL\Validator\DocumentValidator;
+
+$rule = new QueryComplexity(100);
+DocumentValidator::addRule($rule);
+
+GraphQL::executeQuery(/*...*/);
+
+

This will set the rule globally. Alternatively, you can provide validation rules per execution.

+

To customize field score add complexity function to field definition:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$type = new ObjectType([
+    'name' => 'MyType',
+    'fields' => [
+        'someList' => [
+            'type' => Type::listOf(Type::string()),
+            'args' => [
+                'limit' => [
+                    'type' => Type::int(),
+                    'defaultValue' => 10
+                ]
+            ],
+            'complexity' => fn (int $childrenComplexity, array $args): int => $childrenComplexity * $args['limit'],
+        ]
+    ]
+]);
+
+

Limiting Query Depth

+

This is a port of Limiting Query Depth in Sangria.

+

This is a simpler approach that limits the nesting depth a query can have. +For example, the depth of the default introspection query is 7.

+

This rule is disabled by default. You may enable it by setting a maximum query depth:

+
use GraphQL\GraphQL;
+use GraphQL\Validator\Rules\QueryDepth;
+use GraphQL\Validator\DocumentValidator;
+
+$rule = new QueryDepth(10);
+DocumentValidator::addRule($rule);
+
+GraphQL::executeQuery(/*...*/);
+
+

This will set the rule globally. Alternatively, you can provide validation rules per execution.

+

Disabling Introspection

+

Introspection is a mechanism for fetching schema structure. +It is used by tools like GraphiQL for auto-completion, query validation, etc.

+

Introspection is enabled by default. It means that anybody can get a full description of your schema by +sending a special query containing meta fields __type and __schema .

+

If you are not planning to expose your API to the general public, it makes sense to disable this feature.

+

GraphQL PHP provides you separate validation rule which prohibits queries that contain +__type or __schema fields. To disable introspection, add following rule:

+
use GraphQL\GraphQL;
+use GraphQL\Validator\Rules\DisableIntrospection;
+use GraphQL\Validator\DocumentValidator;
+
+$rule = new DisableIntrospection(DisableIntrospection::ENABLED);
+DocumentValidator::addRule($rule);
+
+GraphQL::executeQuery(/*...*/);
+
+

This will set the rule globally. Alternatively, you can provide validation rules per execution.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 000000000..96ff8cfb7 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,103 @@ + + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + + None + 2024-06-19 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 000000000..31b9e8c44 Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/type-definitions/directives/index.html b/type-definitions/directives/index.html new file mode 100644 index 000000000..79b17062e --- /dev/null +++ b/type-definitions/directives/index.html @@ -0,0 +1,732 @@ + + + + + + + + + + + + + + + + + + + + + + + + Directives - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Directives

+ +

Built-in directives

+

The directive is a way for a client to give GraphQL server additional context and hints on how to execute +the query. The directive can be attached to a field or fragment and can affect the execution of the +query in any way the server desires.

+

GraphQL specification includes two built-in directives:

+
    +
  • @include(if: Boolean) Only include this field or fragment in the result if the argument is true
  • +
  • @skip(if: Boolean) Skip this field or fragment if the argument is true
  • +
+

For example:

+
query Hero($episode: Episode, $withFriends: Boolean!) {
+  hero(episode: $episode) {
+    name
+    friends @include(if: $withFriends) {
+      name
+    }
+  }
+}
+
+

Here if $withFriends variable is set to false - friends section will be ignored and excluded +from the response. Important implementation detail: those fields will never be executed +(not just removed from response after execution).

+

Custom directives

+

graphql-php supports custom directives even though their presence does not affect the execution of fields. +You can use GraphQL\Type\Definition\ResolveInfo +in field resolvers to modify the output depending on those directives or perform statistics collection.

+

Other use case is your own query validation rules relying on custom directives.

+

In graphql-php custom directive is an instance of GraphQL\Type\Definition\Directive +(or one of its subclasses) which accepts an array of following options:

+
use GraphQL\Language\DirectiveLocation;
+use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\Directive;
+
+$trackDirective = new Directive([
+    'name' => 'track',
+    'description' => 'Instruction to record usage of the field by client',
+    'locations' => [
+        DirectiveLocation::FIELD,
+    ],
+    'args' => [
+        'details' => [
+            'type' => Type::string(),
+            'description' => 'String with additional details of field usage scenario',
+            'defaultValue' => ''
+        ]
+    ]
+]);
+
+

See possible directive locations in +GraphQL\Language\DirectiveLocation.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/type-definitions/enums/index.html b/type-definitions/enums/index.html new file mode 100644 index 000000000..e7b9810a6 --- /dev/null +++ b/type-definitions/enums/index.html @@ -0,0 +1,962 @@ + + + + + + + + + + + + + + + + + + + + + + + + Enums - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Enum Type Definition

+

Enumeration types are a special kind of scalar that is restricted to a particular set +of allowed values.

+

Writing Enum Types

+

In graphql-php enum type is an instance of GraphQL\Type\Definition\EnumType +which accepts configuration array in constructor:

+
use GraphQL\Type\Definition\EnumType;
+
+$episodeEnum = new EnumType([
+    'name' => 'Episode',
+    'description' => 'One of the films in the Star Wars Trilogy',
+    'values' => [
+        'NEWHOPE' => [
+            'value' => 4,
+            'description' => 'Released in 1977.'
+        ],
+        'EMPIRE' => [
+            'value' => 5,
+            'description' => 'Released in 1980.'
+        ],
+        'JEDI' => [
+            'value' => 6,
+            'description' => 'Released in 1983.'
+        ],
+    ]
+]);
+
+

This example uses an inline style for Enum Type definition, but you can also use +inheritance or schema definition language.

+

Configuration options

+

Enum Type constructor accepts an array with following options:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Name of the type. When not set - inferred from array key (read about shorthand field definition below)
descriptionstringPlain-text description of the type for clients (e.g. used by GraphiQL for auto-generated documentation)
valuesarrayList of enumerated items, see below for expected structure of each entry
+

Each entry of values array in turn accepts following options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Name of the item. When not set - inferred from array key (read about shorthand field definition below)
valuemixedInternal representation of enum item in your application (could be any value, including complex objects or callbacks)
descriptionstringPlain-text description of enum value for clients (e.g. used by GraphiQL for auto-generated documentation)
deprecationReasonstringText describing why this enum value is deprecated. When not empty - item will not be returned by introspection queries (unless forced)
+

Construction from PHP enum

+

You can reuse your existing PHP enums in GraphQL. +Leverage PHP attributes to add descriptions and deprecate values:

+
use GraphQL\Type\Definition\Deprecated;
+use GraphQL\Type\Definition\Description;
+
+#[Description(description: 'Sweet and juicy.')]
+enum Fruit
+{
+    #[Description(description: 'Rich in potassium.')]
+    case BANANA;
+
+    #[Deprecated(reason: 'Too sour.')]
+    case CITRON;
+}
+
+

PhpDocs will be used as descriptions if present, but are overridden by the Description attribute.

+

Wrap them with GraphQL\Type\Definition\PhpEnumType to use them in a GraphQL schema:

+
use GraphQL\Type\Definition\PhpEnumType;
+
+$fruitEnumType = new PhpEnumType(Fruit::class);
+
+

The following type will be deduced from Fruit:

+
"""
+Sweet and juicy.
+"""
+enum Fruit {
+  """
+  Rich in potassium.
+  """
+  BANANA
+
+  CITRON @deprecated(reason: "Too sour.")
+}
+
+

Conversion rules:

+
    +
  • Client-given arguments will be converted to enum instances
  • +
  • Returned output values are expected to be enum instances
  • +
+

Shorthand definitions

+

If internal representation of enumerated item is the same as item name, then you can use +following shorthand for definition:

+
use GraphQL\Type\Definition\EnumType;
+
+$episodeEnum = new EnumType([
+    'name' => 'Episode',
+    'description' => 'One of the films in the Star Wars Trilogy',
+    'values' => ['NEWHOPE', 'EMPIRE', 'JEDI']
+]);
+
+

which is equivalent of:

+
use GraphQL\Type\Definition\EnumType;
+
+$episodeEnum = new EnumType([
+    'name' => 'Episode',
+    'description' => 'One of the films in the Star Wars Trilogy',
+    'values' => [
+        'NEWHOPE' => ['value' => 'NEWHOPE'],
+        'EMPIRE' => ['value' => 'EMPIRE'],
+        'JEDI' => ['value' => 'JEDI']
+    ]
+]);
+
+

which is in turn equivalent of the full form:

+
use GraphQL\Type\Definition\EnumType;
+
+$episodeEnum = new EnumType([
+    'name' => 'Episode',
+    'description' => 'One of the films in the Star Wars Trilogy',
+    'values' => [
+        ['name' => 'NEWHOPE', 'value' => 'NEWHOPE'],
+        ['name' => 'EMPIRE', 'value' => 'EMPIRE'],
+        ['name' => 'JEDI', 'value' => 'JEDI']
+    ]
+]);
+
+

Field Resolution

+

When object field is of Enum Type, field resolver is expected to return an internal +representation of corresponding Enum item (value in config). graphql-php will +then serialize this value to name to include in response:

+
use GraphQL\Type\Definition\EnumType;
+use GraphQL\Type\Definition\ObjectType;
+
+$episodeEnum = new EnumType([
+    'name' => 'Episode',
+    'description' => 'One of the films in the Star Wars Trilogy',
+    'values' => [
+        'NEWHOPE' => [
+            'value' => 4,
+            'description' => 'Released in 1977.'
+        ],
+        'EMPIRE' => [
+            'value' => 5,
+            'description' => 'Released in 1980.'
+        ],
+        'JEDI' => [
+            'value' => 6,
+            'description' => 'Released in 1983.'
+        ],
+    ]
+]);
+
+$heroType = new ObjectType([
+    'name' => 'Hero',
+    'fields' => [
+        'appearsIn' => [
+            'type' => $episodeEnum,
+             // Actual entry in response will be 'appearsIn' => 'EMPIRE'
+            'resolve' => fn (): int => 5,
+        ]
+    ]
+]);
+
+

The Reverse is true when the enum is used as input type (e.g. as field argument). +GraphQL will treat enum input as name and convert it into value before passing to your app.

+

For example, given object type definition:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$heroType = new ObjectType([
+    'name' => 'Hero',
+    'fields' => [
+        'appearsIn' => [
+            'type' => Type::boolean(),
+            'args' => [
+                'episode' => Type::nonNull($enumType)
+            ],
+            'resolve' => fn ($hero, array $args): bool => $args['episode'] === 5,
+        ]
+    ]
+]);
+
+

Then following query:

+
fragment on Hero {
+    appearsInNewHope: appearsIn(NEWHOPE)
+    appearsInEmpire: appearsIn(EMPIRE)
+}
+
+

will return:

+
[
+    'appearsInNewHope' => false,
+    'appearsInEmpire' => true
+]
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/type-definitions/index.html b/type-definitions/index.html new file mode 100644 index 000000000..1413449e4 --- /dev/null +++ b/type-definitions/index.html @@ -0,0 +1,811 @@ + + + + + + + + + + + + + + + + + + + + + + + + Introduction - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Type Definitions

+

graphql-php represents a type as a class instance from the GraphQL\Type\Definition namespace:

+ +

Input vs. Output Types

+

All types in GraphQL are of two categories: input and output.

+ +

Obviously, NonNull and List types belong to both categories depending on their +inner type.

+

Definition styles

+

Several styles of type definitions are supported depending on your preferences.

+

Inline definitions

+
use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\Type;
+
+$myType = new ObjectType([
+    'name' => 'MyType',
+    'fields' => [
+        'id' => Type::id()
+    ]
+]);
+
+

Class per type

+
use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\Type;
+
+class MyType extends ObjectType
+{
+    public function __construct()
+    {
+        $config = [
+            // Note: 'name' is not needed in this form:
+            // it will be inferred from class name by omitting namespace and dropping "Type" suffix
+            'fields' => [
+                'id' => Type::id()
+            ]
+        ];
+        parent::__construct($config);
+    }
+}
+
+

Schema definition language

+
schema {
+  query: Query
+  mutation: Mutation
+}
+
+type Query {
+  greetings(input: HelloInput!): String!
+}
+
+input HelloInput {
+  firstName: String!
+  lastName: String
+}
+
+

Read more about building an executable schema using schema definition language.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/type-definitions/inputs/index.html b/type-definitions/inputs/index.html new file mode 100644 index 000000000..2fe07c986 --- /dev/null +++ b/type-definitions/inputs/index.html @@ -0,0 +1,892 @@ + + + + + + + + + + + + + + + + + + + + + + + + Inputs - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Input Object Type Definition

+

The GraphQL specification defines Input Object Type for complex inputs. It is similar to ObjectType +except that it's fields have no args or resolve options and their type must be input type.

+

Writing Input Object Types

+

In graphql-php Input Object Type is an instance of GraphQL\Type\Definition\InputObjectType +(or one of its subclasses) which accepts configuration array in its constructor:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\InputObjectType;
+
+$filters = new InputObjectType([
+    'name' => 'StoryFiltersInput',
+    'fields' => [
+        'author' => [
+            'type' => Type::id(),
+            'description' => 'Only show stories with this author id'
+        ],
+        'popular' => [
+            'type' => Type::boolean(),
+            'description' => 'Only show popular stories (liked by several people)'
+        ],
+        'tags' => [
+            'type' => Type::listOf(Type::string()),
+            'description' => 'Only show stories which contain all of those tags'
+        ]
+    ]
+]);
+
+

Every field may be of other InputObjectType (thus complex hierarchies of inputs are possible)

+

Configuration options

+

The constructor of InputObjectType accepts an array with the following options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Unique name of this object type within Schema
fieldsarray or callableRequired. An array describing object fields or callable returning such an array (see below).
descriptionstringPlain-text description of this type for clients (e.g. used by GraphiQL for auto-generated documentation)
parseValuecallable(array<string, mixed>): mixedConverts incoming values from their array representation to something else (e.g. a value object)
+

Every field is an array with following entries:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Name of the input field. When not set - inferred from fields array key
typeTypeRequired. Instance of one of Input Types (Scalar, Enum, InputObjectType + any combination of those with nonNull and listOf modifiers)
descriptionstringPlain-text description of this input field for clients (e.g. used by GraphiQL for auto-generated documentation)
defaultValuescalarDefault value of this input field. Use the internal value if specifying a default for an enum type
+

Using Input Object Type

+

In the example above we defined our InputObjectType. Now let's use it in one of field arguments:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$queryType = new ObjectType([
+    'name' => 'Query',
+    'fields' => [
+        'stories' => [
+            'type' => Type::listOf($storyType),
+            'args' => [
+                'filters' => [
+                    'type' => $filters,
+                    'defaultValue' => [
+                        'popular' => true
+                    ]
+                ]
+            ],
+            'resolve' => fn ($rootValue, array $args): array => DataSource::filterStories($args['filters']),
+        ]
+    ]
+]);
+
+

(note that you can define defaultValue for fields with complex inputs as associative array).

+

Then GraphQL query could include filters as literal value:

+
{
+  stories(filters: { author: "1", popular: false })
+}
+
+

Or as query variable:

+
query ($filters: StoryFiltersInput!) {
+  stories(filters: $filters)
+}
+
+
$variables = [
+    'filters' => [
+        "author" => "1",
+        "popular" => false
+    ]
+];
+
+

graphql-php will validate the input against your InputObjectType definition and pass it to your +resolver as $args['filters']

+

Converting input object array to value object

+

If you want more type safety you can choose to parse the input array into a value object.

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\InputObjectType;
+
+final class StoryFiltersInput
+{
+    public string $author;
+    public ?bool $popular;
+    public array $tags;
+
+    public function __construct(string $author, ?bool $popular, array $tags)
+    {
+        $this->author = $author;
+        $this->popular = $popular;
+        $this->tag = $tag;
+    }
+}
+
+$filters = new InputObjectType([
+    'name' => 'StoryFiltersInput',
+    'fields' => [
+        'author' => [
+            'type' => Type::nonNull(Type::string()),
+        ],
+        'popular' => [
+            'type' => Type::boolean(),
+        ],
+        'tags' => [
+            'type' => Type::nonNull(Type::listOf(Type::string())),
+        ]
+    ],
+    'parseValue' => fn(array $values) => new StoryFiltersInput(
+        $values['author'],
+        $values['popular'] ?? null,
+        $values['tags']
+    ),
+]);
+
+

The value of $args['filters'] will now be an instance of StoryFiltersInput.

+

The incoming values are converted using a depth-first traversal. Thus, nested input values +will be passed through their respective parseValue functions before the parent receives their value.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/type-definitions/interfaces/index.html b/type-definitions/interfaces/index.html new file mode 100644 index 000000000..eef5d8928 --- /dev/null +++ b/type-definitions/interfaces/index.html @@ -0,0 +1,936 @@ + + + + + + + + + + + + + + + + + + + + + + + + Interfaces - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

Interface Type Definition

+

An Interface is an abstract type that includes a certain set of fields that a +type must include to implement the interface.

+

Writing Interface Types

+

In graphql-php interface type is an instance of GraphQL\Type\Definition\InterfaceType +(or one of its subclasses) which accepts configuration array in a constructor:

+
use GraphQL\Type\Definition\InterfaceType;
+use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\Type;
+
+$character = new InterfaceType([
+    'name' => 'Character',
+    'description' => 'A character in the Star Wars Trilogy',
+    'fields' => [
+        'id' => [
+            'type' => Type::nonNull(Type::string()),
+            'description' => 'The id of the character.',
+        ],
+        'name' => [
+            'type' => Type::string(),
+            'description' => 'The name of the character.'
+        ]
+    ],
+    'resolveType' => function ($value): ObjectType {
+        switch ($value->type ?? null) {
+            case 'human': return MyTypes::human();
+            case 'droid': return MyTypes::droid();
+            default: throw new Exception("Unknown Character type: {$value->type ?? null}");
+        }
+    }
+]);
+
+

This example uses inline style for Interface definition, but you can also use
+inheritance or schema definition language.

+

Configuration options

+

The constructor of InterfaceType accepts an array. Below is a full list of allowed options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Unique name of this interface type within Schema
fieldsarrayRequired. List of fields required to be defined by interface implementors. Same as Fields for Object Type
descriptionstringPlain-text description of this type for clients (e.g. used by GraphiQL for auto-generated documentation)
resolveTypecallbackfunction ($value, $context, ResolveInfo $info)
Receives $value from resolver of the parent field and returns concrete interface implementor for this $value.
+

Implementing interface

+

To implement the Interface simply add it to interfaces array of Object Type definition:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$humanType = new ObjectType([
+    'name' => 'Human',
+    'fields' => [
+        'id' => [
+            'type' => Type::nonNull(Type::string()),
+            'description' => 'The id of the character.',
+        ],
+        'name' => [
+            'type' => Type::string(),
+            'description' => 'The name of the character.'
+        ]
+    ],
+    'interfaces' => [
+        $character
+    ]
+]);
+
+

Note that Object Type must include all fields of interface with exact same types +(including nonNull specification) and arguments.

+

The only exception is when object's field type is more specific than the type of this field defined in interface +(see Covariant return types for interface fields below)

+

Covariant return types for interface fields

+

Object types implementing interface may change the field type to more specific. +Example:

+
interface A {
+  field1: A
+}
+
+type B implements A {
+  field1: B
+}
+
+

Sharing Interface fields

+

Since every Object Type implementing an Interface must have the same set of fields - it often makes +sense to reuse field definitions of Interface in Object Types:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$humanType = new ObjectType([
+    'name' => 'Human',
+    'interfaces' => [
+        $character
+    ],
+    'fields' => [
+        $character->getField('id'),
+        $character->getField('name'),
+        [
+            'name' => 'height',
+            'type' => Type::float(),
+        ],
+    ]
+]);
+
+

In this case, field definitions are created only once (as a part of Interface Type) and then +reused by all interface implementors. It can save several microseconds and kilobytes + ensures that +field definitions of Interface and implementors are always in sync.

+

Yet it creates a problem with the resolution of such fields. There are two ways how shared fields could +be resolved:

+
    +
  1. +

    If field resolution algorithm is the same for all Interface implementors - you can simply add + resolve option to field definition in Interface itself.

    +
  2. +
  3. +

    If field resolution varies for different implementations - you can specify resolveField + option in Object Type config and handle field + resolutions there + (Note: resolve option in field definition has precedence over resolveField option in object type definition)

    +
  4. +
+

Interface role in data fetching

+

The only responsibility of interface in Data Fetching process is to return concrete Object Type +for given $value in resolveType. Then resolution of fields is delegated to resolvers of this +concrete Object Type.

+

If a resolveType option is omitted, graphql-php will loop through all interface implementors and +use their isTypeOf callback to pick the first suitable one. This is obviously less efficient +than single resolveType call. So it is recommended to define resolveType whenever possible.

+

Prevent invisible types

+

When object types that implement an interface are not directly referenced by a field, they cannot +be discovered during schema introspection. For example:

+
type Query {
+    animal: Animal
+}
+
+interface Animal {...}
+
+type Cat implements Animal {...}
+type Dog implements Animal {...}
+
+

In this example, Cat and Dog would be considered invisible types. Querying the animal field +would fail, since no possible implementing types for Animal can be found.

+

There are two possible solutions:

+
    +
  1. Add fields that reference the invisible types directly, e.g.:
  2. +
+
type Query {
+  dog: Dog
+  cat: Cat
+}
+
+
    +
  1. Pass the invisible types during schema construction, e.g.:
  2. +
+
new GraphQLSchema([
+    'query' => ...,
+    'types' => [$cat, $dog]
+]);
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/type-definitions/lists-and-nonnulls/index.html b/type-definitions/lists-and-nonnulls/index.html new file mode 100644 index 000000000..f1c27530d --- /dev/null +++ b/type-definitions/lists-and-nonnulls/index.html @@ -0,0 +1,732 @@ + + + + + + + + + + + + + + + + + + + + + + + + Lists and Non-Nulls - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Lists and Non-Nulls

+ +

Lists

+

graphql-php provides built-in support for lists. In order to create list type - wrap +existing type with GraphQL\Type\Definition\Type::listOf() modifier:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$userType = new ObjectType([
+    'name' => 'User',
+    'fields' => [
+        'emails' => [
+            'type' => Type::listOf(Type::string()),
+            'resolve' => fn (): array => [
+                'jon@example.com',
+                'jonny@example.com'
+            ],
+        ]
+    ]
+]);
+
+

Resolvers for such fields are expected to return array or instance of PHP's built-in Traversable +interface (null is allowed by default too).

+

If returned value is not of one of these types - graphql-php will add an error to result +and set the field value to null (only if the field is nullable, see below for non-null fields).

+

Non-Nulls

+

By default, every field or argument can have a null value. +To indicate the value must be non-null use the GraphQL\Type\Definition\Type::nonNull() modifier:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$humanType = new ObjectType([
+    'name' => 'User',
+    'fields' => [
+        'id' => [
+            'type' => Type::nonNull(Type::id()),
+            'resolve' => fn (): string => uniqid(),
+        ],
+        'emails' => [
+            'type' => Type::nonNull(Type::listOf(Type::string())),
+            'resolve' => fn (): array => [
+                'jon@example.com',
+                'jonny@example.com'
+            ],
+        ]
+    ]
+]);
+
+

If resolver of non-null field returns null, graphql-php will add an error to +result and exclude the whole object from the output (an error will bubble to first +nullable parent field which will be set to null).

+

Read the section on Data Fetching for details.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/type-definitions/object-types/index.html b/type-definitions/object-types/index.html new file mode 100644 index 000000000..d0007baea --- /dev/null +++ b/type-definitions/object-types/index.html @@ -0,0 +1,1051 @@ + + + + + + + + + + + + + + + + + + + + + + + + Object Types - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

Object Type Definition

+

Object Type is the most frequently used primitive in a typical GraphQL application.

+

Conceptually Object Type is a collection of Fields. Each field, in turn, +has its own type which allows building complex hierarchies.

+

Writing Object Types

+

In graphql-php object type is an instance of GraphQL\Type\Definition\ObjectType +(or one of its subclasses) which accepts a configuration array in its constructor:

+
use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\Type;
+use GraphQL\Examples\Blog\Data\DataSource;
+use GraphQL\Examples\Blog\Data\Story;
+
+$userType = new ObjectType([
+    'name' => 'User',
+    'description' => 'Our blog visitor',
+    'fields' => [
+        'firstName' => [
+            'type' => Type::string(),
+            'description' => 'User first name'
+        ],
+        'email' => Type::string()
+    ]
+]);
+
+$blogStory = new ObjectType([
+    'name' => 'Story',
+    'fields' => [
+        'body' => Type::string(),
+        'author' => [
+            'type' => $userType,
+            'description' => 'Story author',
+            'resolve' => fn (Story $blogStory): ?User => DataSource::findUser($blogStory->authorId),
+        ],
+        'likes' => [
+            'type' => Type::listOf($userType),
+            'description' => 'List of users who liked the story',
+            'args' => [
+                'limit' => [
+                    'type' => Type::int(),
+                    'description' => 'Limit the number of recent likes returned',
+                    'defaultValue' => 10
+                ]
+            ],
+            'resolve' => fn (Story $blogStory, array $args): array => DataSource::findLikes($blogStory->id, $args['limit']),
+        ]
+    ]
+]);
+
+

This example uses inline style for Object Type definitions, but you can also use
+inheritance or schema definition language.

+

Configuration options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Unique name of this object type within Schema
fieldsarray or callableRequired. An array describing object fields or callable returning such an array. See field configuration options section below for expected structure of each array entry. See also the section on Circular types for an explanation of when to use callable for this option.
descriptionstringPlain-text description of this type for clients (e.g. used by GraphiQL for auto-generated documentation)
interfacesarray or callableList of interfaces implemented by this type or callable returning such a list. See Interface Types for details. See also the section on Circular types for an explanation of when to use callable for this option.
isTypeOfcallablefunction ($value, $context, ResolveInfo $info): bool
Expected to return true if $value qualifies for this type (see section about Abstract Type Resolution for explanation).
resolveFieldcallablefunction ($value, array $args, $context, ResolveInfo $info): mixed
Given the $value of this type, it is expected to return value for a field defined in $info->fieldName. A good place to define a type-specific strategy for field resolution. See section on Data Fetching for details.
visiblebool or callableDefaults to true. The given callable receives no arguments and is expected to return a bool, it is called once when the field may be accessed. The field is treated as if it were not defined at all when this is false.
+

Field configuration options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Name of the field. When not set - inferred from fields array key (read about shorthand field definition below)
typeTypeRequired. An instance of internal or custom type. Note: type must be represented by a single instance within one schema (see also lazy loading of types)
argsarrayAn array describing any number of possible field arguments, each element being an array. See field argument configuration options.
resolvecallablefunction ($objectValue, array $args, $context, ResolveInfo $info): mixed
Given the $objectValue of this type, it is expected to return actual value of the current field. See section on Data Fetching for details
complexitycallablefunction (int $childrenComplexity, array $args): int
Used to restrict query complexity. The feature is disabled by default, read about Security to use it.
descriptionstringPlain-text description of this field for clients (e.g. used by GraphiQL for auto-generated documentation)
deprecationReasonstringText describing why this field is deprecated. When not empty - field will not be returned by introspection queries (unless forced)
+

Field argument configuration options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Name of the argument. When not set - inferred from args array key
typeTypeRequired. Instance of one of Input Types (scalar, enum, InputObjectType + any combination of those with nonNull and listOf modifiers)
descriptionstringPlain-text description of this argument for clients (e.g. used by GraphiQL for auto-generated documentation)
defaultValuescalarDefault value for this argument. Use the internal value if specifying a default for an enum type
+

Shorthand field definitions

+

Fields can be also defined in shorthand notation (with only name and type options):

+
'fields' => [
+    'id' => Type::id(),
+    'fieldName' => $fieldType
+]
+
+

which is equivalent of:

+
'fields' => [
+    'id' => ['type' => Type::id()],
+    'fieldName' => ['type' => $fieldName]
+]
+
+

which is in turn equivalent of the full form:

+
'fields' => [
+    ['name' => 'id', 'type' => Type::id()],
+    ['name' => 'fieldName', 'type' => $fieldName]
+]
+
+

Same shorthand notation applies to field arguments as well.

+

Recurring and circular types

+

Almost all real-world applications contain recurring or circular types. +Think user friends or nested comments for example.

+

graphql-php allows such types, but you have to use callable in +option fields (and/or interfaces).

+

For example:

+
use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+$userType = new ObjectType([
+    'name' => 'User',
+    'fields' => function () use (&$userType): array {
+        return [
+            'email' => [
+                'type' => Type::string()
+            ],
+            'friends' => [
+                'type' => Type::listOf($userType)
+            ]
+        ];
+    },
+]);
+
+

Same example for inheritance style of type definitions +using a type registry (see lazy loading of types):

+
use GraphQL\Type\Definition\ListOfType;
+use GraphQL\Type\Definition\ScalarType;
+use GraphQL\Type\Definition\Type;
+use GraphQL\Type\Definition\ObjectType;
+
+class UserType extends ObjectType
+{
+    public function __construct()
+    {
+        parent::__construct([
+            'fields' => [
+                'email' => fn (): ScalarType => MyTypes::string(),
+                'friends' => fn (): ListOfType => MyTypes::listOf(MyTypes::user())
+            ],
+        ]);
+    }
+}
+
+class MyTypes
+{
+    private static UserType $user;
+
+    public static function user(): UserType
+    {
+        return self::$user ??= new UserType();
+    }
+
+    public static function string(): ScalarType
+    {
+        return Type::string();
+    }
+
+    public static function listOf($type): ListOfType
+    {
+        return Type::listOf($type);
+    }
+}
+
+

Field Resolution

+

Field resolution is the primary mechanism in graphql-php for returning actual data for your fields. +It is implemented using resolveField callable in type definition or resolve +callable in field definition (which has precedence).

+

Read the section on Data Fetching for a complete description of this process.

+

Custom Metadata

+

All types in graphql-php accept configuration array. In some cases, you may be interested in +passing your own metadata for type or field definition.

+

graphql-php preserves original configuration array in every type or field instance in +public property $config. Use it to implement app-level mappings and definitions.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/type-definitions/scalars/index.html b/type-definitions/scalars/index.html new file mode 100644 index 000000000..549275d9a --- /dev/null +++ b/type-definitions/scalars/index.html @@ -0,0 +1,782 @@ + + + + + + + + + + + + + + + + + + + + + + + + Scalars - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Scalar Type Definition

+

Scalar types represent primitive leaf values in a GraphQL type system. +When object fields have to resolve to some concrete data, that's where the scalar types come in.

+

Built-in Scalar Types

+

The GraphQL specification describes several built-in scalar types. In graphql-php they are +exposed as static methods of the class GraphQL\Type\Definition\Type:

+
use GraphQL\Type\Definition\Type;
+
+// Built-in Scalar types:
+Type::string();  // String type
+Type::int();     // Int type
+Type::float();   // Float type
+Type::boolean(); // Boolean type
+Type::id();      // ID type
+
+

Those methods return instances of a subclass of GraphQL\Type\Definition\ScalarType. +Use them directly in type definitions or wrapped in a type registry (see lazy loading of types).

+

Writing Custom Scalar Types

+

In addition to built-in scalars, you can define your own scalar types with additional validation. +Typical examples of such types are Email, Date, Url, etc.

+

In order to implement your own type, you must understand how scalars are handled in GraphQL. +GraphQL deals with scalars in the following cases:

+
    +
  1. +

    Convert the internal representation of a value, returned by your app (e.g. stored in a database + or hardcoded in the source code), to a serialized representation included in the response.

    +
  2. +
  3. +

    Convert an input value, passed by a client in variables along with a GraphQL query, to + its internal representation used in your application.

    +
  4. +
  5. +

    Convert an input literal value, hardcoded in a GraphQL query (e.g. field argument value), to + its internal representation used in your application.

    +
  6. +
+

Those cases are covered by the methods serialize, parseValue and parseLiteral of the +abstract class ScalarType respectively.

+

Here is an example of a simple Email type:

+
use GraphQL\Error\Error;
+use GraphQL\Error\InvariantViolation;
+use GraphQL\Language\AST\StringValueNode;
+use GraphQL\Type\Definition\ScalarType;
+use GraphQL\Utils\Utils;
+
+class EmailType extends ScalarType
+{
+    // Note: name can be omitted. In this case it will be inferred from class name
+    // (suffix "Type" will be dropped)
+    public string $name = 'Email';
+
+    public function serialize($value)
+    {
+        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
+            throw new InvariantViolation("Could not serialize following value as email: " . Utils::printSafe($value));
+        }
+
+        return $this->parseValue($value);
+    }
+
+    public function parseValue($value)
+    {
+        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
+            throw new Error("Cannot represent following value as email: " . Utils::printSafeJson($value));
+        }
+
+        return $value;
+    }
+
+    public function parseLiteral(Node $valueNode, ?array $variables = null)
+    {
+        // Throw GraphQL\Error\Error vs \UnexpectedValueException to locate the error in the query
+        if (!$valueNode instanceof StringValueNode) {
+            throw new Error('Query error: Can only parse strings got: ' . $valueNode->kind, [$valueNode]);
+        }
+
+        if (!filter_var($valueNode->value, FILTER_VALIDATE_EMAIL)) {
+            throw new Error("Not a valid email", [$valueNode]);
+        }
+
+        return $valueNode->value;
+    }
+}
+
+

Or with inline style:

+
use GraphQL\Type\Definition\CustomScalarType;
+
+$emailType = new CustomScalarType([
+    'name' => 'Email',
+    'serialize' => static function ($value) {/* See function body above */},
+    'parseValue' => static function ($value) {/* See function body above */},
+    'parseLiteral' => static function (Node $valueNode, ?array $variables = null) {/* See function body above */},
+]);
+
+

Keep in mind the passed functions will be called statically, so a passed in callable +such as [Foo::class, 'bar'] should only reference static class methods.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/type-definitions/unions/index.html b/type-definitions/unions/index.html new file mode 100644 index 000000000..e54479619 --- /dev/null +++ b/type-definitions/unions/index.html @@ -0,0 +1,745 @@ + + + + + + + + + + + + + + + + + + + + + + + + Unions - graphql-php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Union Type Definition

+

A Union is an abstract type that simply enumerates other Object Types. +The value of Union Type is actually a value of one of included Object Types.

+

Writing Union Types

+

In graphql-php union type is an instance of GraphQL\Type\Definition\UnionType +(or one of its subclasses) which accepts configuration array in a constructor:

+
use GraphQL\Type\Definition\ObjectType;
+use GraphQL\Type\Definition\UnionType;
+
+$searchResultType = new UnionType([
+    'name' => 'SearchResult',
+    'types' => [
+        MyTypes::story(),
+        MyTypes::user()
+    ],
+    'resolveType' => function ($value): ObjectType {
+        switch ($value->type ?? null) {
+            case 'story': return MyTypes::story();
+            case 'user': return MyTypes::user();
+            default: throw new Exception("Unexpected SearchResult type: {$value->type ?? null}");
+        }
+    },
+]);
+
+

This example uses inline style for Union definition, but you can also use
+inheritance or schema definition language.

+

Configuration options

+

The constructor of UnionType accepts an array. Below is a full list of allowed options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionTypeNotes
namestringRequired. Unique name of this interface type within Schema
typesarrayRequired. List of Object Types included in this Union. Note that you can't create a Union type out of Interfaces or other Unions.
descriptionstringPlain-text description of this type for clients (e.g. used by GraphiQL for auto-generated documentation)
resolveTypecallbackfunction ($value, $context, ResolveInfo $info): ObjectType
Receives $value from resolver of the parent field and returns concrete Object Type for this $value.
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file