/**
* @param $parentFieldsAreMutuallyExclusive
* @param $responseName
* @param [FieldNode, GraphQLFieldDefinition] $pair1
* @param [FieldNode, GraphQLFieldDefinition] $pair2
* @param ValidationContext $context
* @return array|null
*/
private function findConflict($parentFieldsAreMutuallyExclusive, $responseName, array $pair1, array $pair2, ValidationContext $context)
{
list($parentType1, $ast1, $def1) = $pair1;
list($parentType2, $ast2, $def2) = $pair2;
// Not a pair.
if ($ast1 === $ast2) {
return null;
}
// Memoize, do not report the same issue twice.
// Note: Two overlapping ASTs could be encountered both when
// `parentFieldsAreMutuallyExclusive` is true and is false, which could
// produce different results (when `true` being a subset of `false`).
// However we do not need to include this piece of information when
// memoizing since this rule visits leaf fields before their parent fields,
// ensuring that `parentFieldsAreMutuallyExclusive` is `false` the first
// time two overlapping fields are encountered, ensuring that the full
// set of validation rules are always checked when necessary.
if ($this->comparedSet->has($ast1, $ast2)) {
return null;
}
$this->comparedSet->add($ast1, $ast2);
// The return type for each field.
$type1 = isset($def1) ? $def1->getType() : null;
$type2 = isset($def2) ? $def2->getType() : null;
// If it is known that two fields could not possibly apply at the same
// time, due to the parent types, then it is safe to permit them to diverge
// in aliased field or arguments used as they will not present any ambiguity
// by differing.
// It is known that two parent types could never overlap if they are
// different Object types. Interface or Union types might overlap - if not
// in the current state of the schema, then perhaps in some future version,
// thus may not safely diverge.
$fieldsAreMutuallyExclusive = $parentFieldsAreMutuallyExclusive || $parentType1 !== $parentType2 && $parentType1 instanceof ObjectType && $parentType2 instanceof ObjectType;
if (!$fieldsAreMutuallyExclusive) {
$name1 = $ast1->name->value;
$name2 = $ast2->name->value;
if ($name1 !== $name2) {
return [[$responseName, "{$name1} and {$name2} are different fields"], [$ast1], [$ast2]];
}
$args1 = isset($ast1->arguments) ? $ast1->arguments : [];
$args2 = isset($ast2->arguments) ? $ast2->arguments : [];
if (!$this->sameArguments($args1, $args2)) {
return [[$responseName, 'they have differing arguments'], [$ast1], [$ast2]];
}
}
if ($type1 && $type2 && $this->doTypesConflict($type1, $type2)) {
return [[$responseName, "they return conflicting types {$type1} and {$type2}"], [$ast1], [$ast2]];
}
$subfieldMap = $this->getSubfieldMap($ast1, $type1, $ast2, $type2, $context);
if ($subfieldMap) {
$conflicts = $this->findConflicts($fieldsAreMutuallyExclusive, $subfieldMap, $context);
return $this->subfieldConflicts($conflicts, $responseName, $ast1, $ast2);
}
return null;
}