Defining fields in type by trait from different namespace ends with error for relative type declarations
josefsabl opened this issue · comments
Let's assume this class/trait structure:
MyTrait
Type1
Type2
SomeNamespace\Type3
TypeX
are Graphqlite types.
MyTrait
defines field that returns array of Type2
and is used both in Type1
and Type3
.
Let's say it looks like this:
trait MyTrait
{
/**
* @return Type2[]
*/
#[Graph\Field]
public function type2s(): array;
}
This trait works in Type1
but throws an error in Type3
.
ReflectionException: Class "\SomeNamespace\Type2" does not exist
in /var/www/app/vendor/thecodingmachine/graphqlite/src/Mappers/Root/IteratorTypeMapper.php:50
Workaround is to declare the return type in trait using absolute path.
For native typehints there is no problem.
@josefsabl This looks like a normal PHP namespacing misunderstanding. Traits are effectively copied into the class they're evaluated within at runtime. So, Type3
is in the SomeNamespace
namespace. Therefore, the annotation you've provided for the @return
would mean it's evaluated as SomeNamespace\Type2
. This is not a GraphQLite issue and more of a misunderstanding for how namespacing works.
I am sorry, but that is not exactly true. There is no problem with namespaces or understanding them.
Of course that typehints in traits are evaluated from the context (namespace) where the trait is defined and not from the context where the trait is used.
What you say would mean that either a) No relative typehints can be used in traits or b) Traits can be used only in namespace where they are defined. And neither is true.
Like I already said, native relative typehints in same trait work as expected.
Let's see:
namespace App
trait MyTrait
{
public function foo(): Type2 { return new Type2; }
}
namespace App;
final class Type1
{
use MyTrait;
}
namespace App\SomeNamespace;
final class Type3
{
use \App\MyTrait;
}
echo (new \ReflectionClass(\App\Type1::class))
->getMethod('foo')
->getReturnType();
echo (new \ReflectionClass(\App\SomeNamespace\Type3::class))
->getMethod('foo')
->getReturnType();
Both of these echos print same correct class of course: \App\Type2
.
BUT !!!
I know where are you coming from. The problem lies in the reflection that is used to parse the docblock and the fact that there is no sane way to find out if the method is declared in the class or in the trait.
\ReflectionClass
has getTraits
method and I believe it could be used, but I understand this would be hassle and the problem has easy workaround.
Maybe the issue should be left open for others, I still believe this is a bug.
Anyway, thanks for great library!