Skip to content

Commit bbb6e60

Browse files
authored
Document that CustomScalarType requires static callables (#905)
1 parent c886d2b commit bbb6e60

File tree

2 files changed

+44
-83
lines changed

2 files changed

+44
-83
lines changed

docs/type-definitions/scalars.md

Lines changed: 31 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Built-in Scalar Types
2-
GraphQL specification describes several built-in scalar types. In **graphql-php** they are
3-
exposed as static methods of [`GraphQL\Type\Definition\Type`](../class-reference.md#graphqltypedefinitiontype) class:
2+
3+
The GraphQL specification describes several built-in scalar types. In **graphql-php** they are
4+
exposed as static methods of the class [`GraphQL\Type\Definition\Type`](../class-reference.md#graphqltypedefinitiontype):
45

56
```php
6-
<?php
77
use GraphQL\Type\Definition\Type;
88

99
// Built-in Scalar types:
@@ -13,34 +13,33 @@ Type::float(); // Float type
1313
Type::boolean(); // Boolean type
1414
Type::id(); // ID type
1515
```
16-
Those methods return instances of `GraphQL\Type\Definition\ScalarType` (actually one of subclasses).
16+
17+
Those methods return instances of a subclass of `GraphQL\Type\Definition\ScalarType`.
1718
Use them directly in type definitions or wrapped in a type registry (see [lazy loading of types](../schema-definition.md#lazy-loading-of-types)).
1819

1920
# Writing Custom Scalar Types
21+
2022
In addition to built-in scalars, you can define your own scalar types with additional validation.
2123
Typical examples of such types are **Email**, **Date**, **Url**, etc.
2224

23-
In order to implement your own type, you must understand how scalars are presented in GraphQL.
24-
GraphQL deals with scalars in following cases:
25+
In order to implement your own type, you must understand how scalars are handled in GraphQL.
26+
GraphQL deals with scalars in the following cases:
2527

26-
1. When converting **internal representation** of value returned by your app (e.g. stored in a database
27-
or hardcoded in the source code) to **serialized** representation included in the response.
28+
1. Convert the **internal representation** of a value, returned by your app (e.g. stored in a database
29+
or hardcoded in the source code), to a **serialized representation** included in the response.
2830

29-
2. When converting **input value** passed by a client in variables along with GraphQL query to
30-
**internal representation** of your app.
31+
2. Convert an **input value**, passed by a client in variables along with a GraphQL query, to
32+
its **internal representation** used in your application.
3133

32-
3. When converting **input literal value** hardcoded in GraphQL query (e.g. field argument value) to
33-
the **internal representation** of your app.
34+
3. Convert an **input literal value**, hardcoded in a GraphQL query (e.g. field argument value), to
35+
its **internal representation** used in your application.
3436

35-
Those cases are covered by methods `serialize`, `parseValue` and `parseLiteral` of abstract `ScalarType`
36-
class respectively.
37+
Those cases are covered by the methods `serialize`, `parseValue` and `parseLiteral` of the
38+
abstract class `ScalarType` respectively.
3739

3840
Here is an example of a simple **Email** type:
3941

4042
```php
41-
<?php
42-
namespace MyApp;
43-
4443
use GraphQL\Error\Error;
4544
use GraphQL\Error\InvariantViolation;
4645
use GraphQL\Language\AST\StringValueNode;
@@ -53,62 +52,35 @@ class EmailType extends ScalarType
5352
// (suffix "Type" will be dropped)
5453
public $name = 'Email';
5554

56-
/**
57-
* Serializes an internal value to include in a response.
58-
*
59-
* @param string $value
60-
* @return string
61-
*/
6255
public function serialize($value)
6356
{
64-
// Assuming internal representation of email is always correct:
65-
return $value;
66-
67-
// If it might be incorrect and you want to make sure that only correct values are included
68-
// in response - use following line instead:
69-
// if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
70-
// throw new InvariantViolation("Could not serialize following value as email: " . Utils::printSafe($value));
71-
// }
72-
// return $this->parseValue($value);
57+
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
58+
throw new InvariantViolation("Could not serialize following value as email: " . Utils::printSafe($value));
59+
}
60+
61+
return $this->parseValue($value);
7362
}
7463

75-
/**
76-
* Parses an externally provided value (query variable) to use as an input
77-
*
78-
* @param mixed $value
79-
* @return mixed
80-
*/
8164
public function parseValue($value)
8265
{
8366
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
8467
throw new Error("Cannot represent following value as email: " . Utils::printSafeJson($value));
8568
}
69+
8670
return $value;
8771
}
8872

89-
/**
90-
* Parses an externally provided literal value (hardcoded in GraphQL query) to use as an input.
91-
*
92-
* E.g.
93-
* {
94-
* user(email: "[email protected]")
95-
* }
96-
*
97-
* @param \GraphQL\Language\AST\Node $valueNode
98-
* @param array|null $variables
99-
* @return string
100-
* @throws Error
101-
*/
10273
public function parseLiteral(Node $valueNode, ?array $variables = null)
10374
{
104-
// Note: throwing GraphQL\Error\Error vs \UnexpectedValueException to benefit from GraphQL
105-
// error location in query:
75+
// Throw GraphQL\Error\Error vs \UnexpectedValueException to locate the error in the query
10676
if (!$valueNode instanceof StringValueNode) {
10777
throw new Error('Query error: Can only parse strings got: ' . $valueNode->kind, [$valueNode]);
10878
}
79+
10980
if (!filter_var($valueNode->value, FILTER_VALIDATE_EMAIL)) {
11081
throw new Error("Not a valid email", [$valueNode]);
11182
}
83+
11284
return $valueNode->value;
11385
}
11486
}
@@ -117,13 +89,15 @@ class EmailType extends ScalarType
11789
Or with inline style:
11890

11991
```php
120-
<?php
12192
use GraphQL\Type\Definition\CustomScalarType;
12293

12394
$emailType = new CustomScalarType([
12495
'name' => 'Email',
125-
'serialize' => function($value) {/* See function body above */},
126-
'parseValue' => function($value) {/* See function body above */},
127-
'parseLiteral' => function($valueNode, array $variables = null) {/* See function body above */},
96+
'serialize' => static function($value) {/* See function body above */},
97+
'parseValue' => static function($value) {/* See function body above */},
98+
'parseLiteral' => static function(Node $valueNode, ?array $variables = null) {/* See function body above */},
12899
]);
129100
```
101+
102+
Keep in mind the passed functions will be called statically, so a passed in `callable`
103+
such as `[Foo::class, 'bar']` should only reference static class methods.

tests/Type/DefinitionTest.php

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,44 +33,31 @@ class DefinitionTest extends TestCase
3333
{
3434
use ArraySubsetAsserts;
3535

36-
/** @var ObjectType */
37-
public $blogImage;
36+
public ObjectType $blogImage;
3837

39-
/** @var ObjectType */
40-
public $blogArticle;
38+
public ObjectType $blogArticle;
4139

42-
/** @var ObjectType */
43-
public $blogAuthor;
40+
public ObjectType $blogAuthor;
4441

45-
/** @var ObjectType */
46-
public $blogMutation;
42+
public ObjectType $blogMutation;
4743

48-
/** @var ObjectType */
49-
public $blogQuery;
44+
public ObjectType $blogQuery;
5045

51-
/** @var ObjectType */
52-
public $blogSubscription;
46+
public ObjectType $blogSubscription;
5347

54-
/** @var ObjectType */
55-
public $objectType;
48+
public ObjectType $objectType;
5649

57-
/** @var ObjectType */
58-
public $objectWithIsTypeOf;
50+
public ObjectType $objectWithIsTypeOf;
5951

60-
/** @var InterfaceType */
61-
public $interfaceType;
52+
public InterfaceType $interfaceType;
6253

63-
/** @var UnionType */
64-
public $unionType;
54+
public UnionType $unionType;
6555

66-
/** @var EnumType */
67-
public $enumType;
56+
public EnumType $enumType;
6857

69-
/** @var InputObjectType */
70-
public $inputObjectType;
58+
public InputObjectType $inputObjectType;
7159

72-
/** @var CustomScalarType */
73-
public $scalarType;
60+
public CustomScalarType $scalarType;
7461

7562
public function setUp(): void
7663
{

0 commit comments

Comments
 (0)