Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
f45ac75
lazy load types
shmax Oct 17, 2019
6ae8227
fix introspection resolve
shmax Oct 18, 2019
eeec7a7
tests, linting, stanning
shmax Oct 18, 2019
84d6514
fix annotation
shmax Oct 19, 2019
8aa3770
remove annotation, debug stuff
shmax Oct 19, 2019
9f11df8
cleanup
shmax Oct 20, 2019
5d98f7e
fix annotations
shmax Oct 20, 2019
5acedf8
start on stanning
shmax Nov 3, 2019
889083c
rename to Schema::resolveType
shmax Nov 5, 2019
a540519
remove some more useless resolve calls
shmax Nov 5, 2019
2609b27
remove another unneeded resolve call
shmax Nov 5, 2019
b704d54
remove unused
shmax Nov 5, 2019
9a2486a
workaround for Scrutinizer
shmax Nov 5, 2019
c85a7d5
ws
shmax Nov 5, 2019
f38d9b3
make assertType 0 cost
shmax Nov 5, 2019
935cab2
use getType in assertValid
shmax Nov 5, 2019
5e1bb4a
add lazy load to blog sample
shmax Nov 10, 2019
d2dedf7
fix scrutinizer
shmax Nov 16, 2019
9c354e3
use InvariantViolation
shmax Nov 22, 2019
1646126
resolve abstract type
shmax Dec 9, 2019
cf2b0da
remove whoopsie
shmax Dec 9, 2019
6fde27a
remove annotation
shmax Apr 15, 2020
3b88752
lint
shmax Apr 16, 2020
92bf81c
linting and stanning
shmax Apr 16, 2020
922991a
fix completeAbstractValue
shmax Apr 17, 2020
32fadac
fix tests
shmax Apr 18, 2020
35c2fef
lint
shmax Apr 18, 2020
8316564
cast
shmax Apr 18, 2020
0be4525
fix stan issues
shmax Apr 18, 2020
4e04cad
use trait
shmax Apr 20, 2020
81a924a
remove assert stuff
shmax Apr 20, 2020
2342766
lock sniffer
shmax Apr 20, 2020
6af0e9b
stanning
shmax Apr 20, 2020
fdcce6e
remove unused
shmax Apr 20, 2020
f89c842
remove unused
shmax Apr 20, 2020
730e117
remove unused
shmax Apr 20, 2020
48106ac
remove assert
shmax Apr 20, 2020
e1b363e
remove cast
shmax Apr 20, 2020
1e48d6d
seal class
shmax Apr 20, 2020
51caeef
add typehint
shmax Apr 20, 2020
c085169
rename, change visibility
shmax Apr 20, 2020
9f2cf34
add typehint
shmax Apr 20, 2020
a8b6ce3
Merge branch 'master' into lazy-load-type
shmax Apr 28, 2020
4e6e442
Merge branch 'master' into lazy-load-type
shmax Jun 9, 2020
f6839dd
stanning
shmax Jun 9, 2020
a8f870d
expand type
shmax Jun 9, 2020
e73353c
expand type
shmax Jun 9, 2020
d214aa5
remove unused
shmax Jun 9, 2020
07b9297
reorganize
shmax Jun 9, 2020
88db0b3
simplify
shmax Jun 10, 2020
b02d8ce
use isset
shmax Jun 10, 2020
cfc5dff
touch
shmax Jun 11, 2020
ae79c77
revert
shmax Jun 11, 2020
cfb3d3a
disable specific rule
shmax Jun 12, 2020
7aba47f
cast type
shmax Jun 12, 2020
3551138
broaden type
shmax Jun 12, 2020
72ccaa3
annotate return type
shmax Jun 12, 2020
375fd91
remove redundant code
shmax Jun 12, 2020
6a1fad4
skip method call
shmax Jun 12, 2020
484d2dd
move execution of lazy type to typeloader
shmax Jun 13, 2020
b2a50d6
remove pointless test
shmax Jun 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions examples/01-blog/Blog/Type/Scalar/EmailType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Utils\Utils;

class EmailType
class EmailType extends CustomScalarType
{
public static function create()
public function __construct(array $config = [])
{
return new CustomScalarType([
'name' => 'Email',
'serialize' => [__CLASS__, 'serialize'],
'parseValue' => [__CLASS__, 'parseValue'],
'parseLiteral' => [__CLASS__, 'parseLiteral'],
parent::__construct([
'serialize' => [__CLASS__, 's_serialize'],
'parseValue' => [__CLASS__, 's_parseValue'],
'parseLiteral' => [__CLASS__, 's_parseLiteral'],
]);
}

Expand All @@ -24,7 +23,7 @@ public static function create()
* @param string $value
* @return string
*/
public static function serialize($value)
public static function s_serialize($value)
{
// Assuming internal representation of email is always correct:
return $value;
Expand All @@ -40,7 +39,7 @@ public static function serialize($value)
* @param mixed $value
* @return mixed
*/
public static function parseValue($value)
public static function s_parseValue($value)
{
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \UnexpectedValueException("Cannot represent value as email: " . Utils::printSafe($value));
Expand All @@ -55,7 +54,7 @@ public static function parseValue($value)
* @return string
* @throws Error
*/
public static function parseLiteral($valueNode)
public static function s_parseLiteral($valueNode)
{
// Note: throwing GraphQL\Error\Error vs \UnexpectedValueException to benefit from GraphQL
// error location in query:
Expand Down
139 changes: 46 additions & 93 deletions examples/01-blog/Blog/Types.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?php
namespace GraphQL\Examples\Blog;

use Exception;
use GraphQL\Examples\Blog\Type\CommentType;
use GraphQL\Examples\Blog\Type\Enum\ContentFormatEnum;
use GraphQL\Examples\Blog\Type\Enum\ImageSizeEnumType;
use GraphQL\Examples\Blog\Type\Field\HtmlField;
use GraphQL\Examples\Blog\Type\SearchResultType;
use GraphQL\Examples\Blog\Type\NodeType;
use GraphQL\Examples\Blog\Type\QueryType;
use GraphQL\Examples\Blog\Type\Scalar\EmailType;
use GraphQL\Examples\Blog\Type\StoryType;
use GraphQL\Examples\Blog\Type\Scalar\UrlType;
Expand All @@ -25,113 +25,66 @@
*/
class Types
{
// Object types:
private static $user;
private static $story;
private static $comment;
private static $image;
private static $query;

/**
* @return UserType
*/
public static function user()
{
return self::$user ?: (self::$user = new UserType());
}

/**
* @return StoryType
*/
public static function story()
{
return self::$story ?: (self::$story = new StoryType());
}

/**
* @return CommentType
*/
public static function comment()
{
return self::$comment ?: (self::$comment = new CommentType());
}

/**
* @return ImageType
*/
public static function image()
{
return self::$image ?: (self::$image = new ImageType());
}

/**
* @return QueryType
*/
public static function query()
{
return self::$query ?: (self::$query = new QueryType());
}
private static $types = [];
const LAZY_LOAD_GRAPHQL_TYPES = true;

public static function user() : callable { return static::get(UserType::class); }
public static function story() : callable { return static::get(StoryType::class); }
public static function comment() : callable { return static::get(CommentType::class); }
public static function image() : callable { return static::get(ImageType::class); }
public static function node() : callable { return static::get(NodeType::class); }
public static function mention() : callable { return static::get(SearchResultType::class); }
public static function imageSizeEnum() : callable { return static::get(ImageSizeEnumType::class); }
public static function contentFormatEnum() : callable { return static::get(ContentFormatEnum::class); }
public static function email() : callable { return static::get(EmailType::class); }
public static function url() : callable { return static::get(UrlType::class); }

// Interface types
private static $node;

/**
* @return NodeType
*/
public static function node()
public static function get($classname)
{
return self::$node ?: (self::$node = new NodeType());
return static::LAZY_LOAD_GRAPHQL_TYPES ? function() use ($classname) {
return static::byClassName($classname);
} : static::byClassName($classname);
}

protected static function byClassName($classname) {
$parts = explode("\\", $classname);
$cacheName = strtolower(preg_replace('~Type$~', '', $parts[count($parts) - 1]));
$type = null;

// Unions types:
private static $mention;

/**
* @return SearchResultType
*/
public static function mention()
{
return self::$mention ?: (self::$mention = new SearchResultType());
}
if (!isset(self::$types[$cacheName])) {
if (class_exists($classname)) {
$type = new $classname();
}

self::$types[$cacheName] = $type;
}

// Enum types
private static $imageSizeEnum;
private static $contentFormatEnum;
$type = self::$types[$cacheName];

/**
* @return ImageSizeEnumType
*/
public static function imageSizeEnum()
{
return self::$imageSizeEnum ?: (self::$imageSizeEnum = new ImageSizeEnumType());
if (!$type) {
throw new Exception("Unknown graphql type: " . $classname);
}
return $type;
}

/**
* @return ContentFormatEnum
*/
public static function contentFormatEnum()
public static function byTypeName($shortName, $removeType=true)
{
return self::$contentFormatEnum ?: (self::$contentFormatEnum = new ContentFormatEnum());
}
$cacheName = strtolower($shortName);
$type = null;

// Custom Scalar types:
private static $urlType;
private static $emailType;
if (isset(self::$types[$cacheName])) {
return self::$types[$cacheName];
}

public static function email()
{
return self::$emailType ?: (self::$emailType = EmailType::create());
}
$method = lcfirst($shortName);
if(method_exists(get_called_class(), $method)) {
$type = self::{$method}();
}

/**
* @return UrlType
*/
public static function url()
{
return self::$urlType ?: (self::$urlType = new UrlType());
if(!$type) {
throw new Exception("Unknown graphql type: " . $shortName);
}
return $type;
}

/**
Expand Down
6 changes: 5 additions & 1 deletion examples/01-blog/graphql.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// php -S localhost:8080 ./graphql.php
require_once __DIR__ . '/../../vendor/autoload.php';

use GraphQL\Examples\Blog\Type\QueryType;
use \GraphQL\Examples\Blog\Types;
use \GraphQL\Examples\Blog\AppContext;
use \GraphQL\Examples\Blog\Data\DataSource;
Expand Down Expand Up @@ -48,7 +49,10 @@

// GraphQL schema to be passed to query executor:
$schema = new Schema([
'query' => Types::query()
'query' => new QueryType(),
'typeLoader' => function($name) {
return Types::byTypeName($name, true);
}
]);

$result = GraphQL::executeQuery(
Expand Down
13 changes: 9 additions & 4 deletions src/Executor/ReferenceExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
use function array_values;
use function get_class;
use function is_array;
use function is_callable;
use function is_string;
use function sprintf;

Expand Down Expand Up @@ -528,7 +529,6 @@ private function resolveField(ObjectType $parentType, $rootValue, $fieldNodes, $
// The resolve function's optional 3rd argument is a context value that
// is provided to every resolve function within an execution. It is commonly
// used to represent an authenticated user, or request-specific caches.
$context = $exeContext->contextValue;
// The resolve function's optional 4th argument is a collection of
// information about the current execution state.
$info = new ResolveInfo(
Expand Down Expand Up @@ -938,10 +938,15 @@ private function completeLeafValue(LeafType $returnType, &$result)
*/
private function completeAbstractValue(AbstractType $returnType, $fieldNodes, ResolveInfo $info, $path, &$result)
{
$exeContext = $this->exeContext;
$runtimeType = $returnType->resolveType($result, $exeContext->contextValue, $info);
if ($runtimeType === null) {
$exeContext = $this->exeContext;
$typeCandidate = $returnType->resolveType($result, $exeContext->contextValue, $info);

if ($typeCandidate === null) {
$runtimeType = self::defaultTypeResolver($result, $exeContext->contextValue, $info, $returnType);
} elseif (is_callable($typeCandidate)) {
$runtimeType = Schema::resolveType($typeCandidate);
} else {
$runtimeType = $typeCandidate;
}
$promise = $this->getPromise($runtimeType);
if ($promise !== null) {
Expand Down
6 changes: 2 additions & 4 deletions src/Type/Definition/FieldArgument.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Type\Schema;
use GraphQL\Utils\Utils;
use function array_key_exists;
use function is_array;
Expand Down Expand Up @@ -77,12 +78,9 @@ public static function createMap(array $config) : array
return $map;
}

/**
* @return InputType&Type
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should stay, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like that, but without some kind of assert my only other option is a cast, which sort of defeats the point of checking.

Copy link
Collaborator

@simPod simPod Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so can we assert / ignore in static analysis for now? This can be IMO resolved better through templates later. This changes would unnecessarily lower type coverage

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My next PR will be a full native assert overhaul across the board. You can consider that the price of merging this PR.

Copy link
Collaborator

@spawnia spawnia Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to remove this, though? As a user of this library + PHPStan, i would appreciate the precise type, even if there is a slim chance that it might be wrong (not sure how that might even happen).

Adding an assert() later seems like a great idea, too - i think we can have both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to remove this, though?

Because if I restore it, it conflicts with the known return type of Schema::resolveType, which is Type. I believe the best way to make everybody happy is going to be with native asserts, but setting those up properly will involve more work and more discussion, and I really think it warrants a dedicated PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH the type doc can be IMO kept as is and the type incompatibility within this method ignored until the next PR

*/
public function getType() : Type
{
return $this->type;
return Schema::resolveType($this->type);
}

public function defaultValueExists() : bool
Expand Down
10 changes: 4 additions & 6 deletions src/Type/Definition/FieldDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Type\Schema;
use GraphQL\Utils\Utils;
use function is_array;
use function is_callable;
Expand Down Expand Up @@ -58,7 +59,7 @@ class FieldDefinition
*/
public $config;

/** @var OutputType&Type */
/** @var callable|(OutputType&Type) */
public $type;

/** @var callable|string */
Expand Down Expand Up @@ -174,12 +175,9 @@ public function getArg($name)
return null;
}

/**
* @return OutputType&Type
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be kept in order not to lower type coverage

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed previously, nothing I can do here without asserts.

*/
public function getType() : Type
{
return $this->type;
return Schema::resolveType($this->type);
}

/**
Expand Down Expand Up @@ -217,7 +215,7 @@ public function assertValid(Type $parentType)
)
);

$type = $this->type;
$type = $this->getType();
if ($type instanceof WrappingType) {
$type = $type->getWrappedType(true);
}
Expand Down
8 changes: 7 additions & 1 deletion src/Type/Definition/InputObjectField.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Type\Schema;
use GraphQL\Utils\Utils;
use function array_key_exists;
use function sprintf;
Expand Down Expand Up @@ -55,7 +56,12 @@ public function __construct(array $opts)
*/
public function getType() : Type
{
return $this->type;
/**
* TODO: Replace this cast with native assert
*
* @var Type&InputType
*/
return Schema::resolveType($this->type);
}

public function defaultValueExists() : bool
Expand Down
Loading