Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial idea for Intersection/Union types #569

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

kschatzle
Copy link
Contributor

@kschatzle kschatzle commented Sep 28, 2022

Not close to finishing, but what do you think about the direction before I get everything ironed out? (Not all unit tests pass yet).

#535

Adding void return type fixture and test.
Removing naming normalization of types from node to ClassMirror.php.
/**
* @param NamedTypeNode ...$types
*/
public function __construct(bool $allowsNull = false, NamedTypeNode ...$types)
Copy link
Member

Choose a reason for hiding this comment

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

I would make it an array rather than a variadic argument. Otherwise, we have no way to add more parameters in the signature in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure

{
$string = '';
foreach ($this->types as $type) {
$string .= $type . '|';
Copy link
Member

Choose a reason for hiding this comment

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

The code would be much simpler by using return implode('|', $this->types)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure.

/**
* @see \ReflectionNamedType
*/
class NamedTypeNode extends TypeNodeAbstract implements Type
Copy link
Member

Choose a reason for hiding this comment

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

extending TypeNodeAbstract without calling the parent constructor is a no-go to me, as it does not initialize the parent properties properly. To me, there is no reason to extend TypeNodeAbstract here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored some of the Type's. Now the TypeNodeAbstract does not have a __constrcut.

class UnionTypeNode extends TypeNodeAbstract implements Type
{
/**
* @var NamedTypeNode[]
Copy link
Member

Choose a reason for hiding this comment

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

This should be Type[] as PHP 8.2 supports putting intersections inside unions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was targeted for PHP81 intersection at the moment. The extra pull request for DNF is pretty easy and would include this.

Copy link
Member

Choose a reason for hiding this comment

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

Well, I would work on both at the same time actually (otherwise, your PR needs to include a nice handling of DNF to fail with a good error message, which is not worth it IMO). Thus, as those classes are not marked as internal, you would still need to do the work in the same release to avoid having to introduce BC layers.

return '';
}

// When we require PHP 8 we can stop generating ?foo nullables and remove this first block
if ($typeNode->canUseNullShorthand()) {
return sprintf( '?%s', $typeNode->getNonNullTypes()[0]);
return sprintf( '?%s', $typeNode->getType()->getName());
Copy link
Member

Choose a reason for hiding this comment

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

this is broken if $typeNode->getType() is not a NamedTypeNode`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's baked into $typeNode->canUseNullShorthand() since only NamedType can be shorthand.

foreach ($this->types as $type) {
$string .= $type . '&';
}
return '(' . rtrim($string, '&') . ')';
Copy link
Member

Choose a reason for hiding this comment

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

those braces will be broken in PHP 8.1, and will be useless in PHP 8.2+ when the intersection is not part of a union

if (isset($this->types['never']) && count($this->types) !== 1) {
throw new DoubleException('never cannot be part of a union');
if ($this->type instanceof UnionTypeNode) {
/** @var NamedTypeNode $type */
Copy link
Member

Choose a reason for hiding this comment

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

Assuming this here will not support DNF types. Instead, you should have a check if (!$type instanceof NamedTypeNode) continue;

/**
* @see \ReflectionIntersectionType
*/
class IntersectionTypeNode extends TypeNodeAbstract implements Type
Copy link
Member

Choose a reason for hiding this comment

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

I would remove Node from the names of those classes representing types.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure.

return $this->allowsNull;
}

public function getTypes(): array
Copy link
Member

Choose a reason for hiding this comment

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

I would not add this method here. It makes no sense for NamedTypeNode to define it. And having it only in specific classes would force to check the type before using it, which is good because subtypes of a union or of an intersection should almost never be used the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's been removed during a refactor.


namespace Prophecy\Doubler\Generator\Node;

interface Type
Copy link
Member

Choose a reason for hiding this comment

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

this interface needs to enforce types being stringable, as the code relies on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure

@kschatzle
Copy link
Contributor Author

Awesome, thanks for the feedback. Seems like you're ok with the BC breaks in signatures.


public function __toString(): string
{
return $this->name;
Copy link
Member

Choose a reason for hiding this comment

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

NamedTypeNode should not require passing the leading \ IMO. Otherwise, it makes it impossible to use it with Foo::class, which would be a confusing API when building class patches.

@stof
Copy link
Member

stof commented Sep 29, 2022

@kschatzle it would be better if ArgumentTypeNode and ReturnTypeNode could have a BC layer for their getTypes method (this BC layer can throw an exception when an intersection type is found, hinting at using the new API to support it). And even better if we can provide a BC layer for the existing constructor signature (with deprecation warnings).

@ciaranmcnulty ciaranmcnulty changed the title Initial idea for Issues/535 Initial idea for Intersection/Union types Nov 29, 2022
@smyhill
Copy link

smyhill commented Jul 31, 2023

@kschatzle Is this something still actively being worked on. Can I help out in any way?

@kschatzle
Copy link
Contributor Author

I need to get back to this eventually when I have time. Some IRL stuff.

@dannyvw
Copy link

dannyvw commented Mar 27, 2024

Doctrine has changed the return type from ObjectRepository to EntityRepository https://github.com/doctrine/DoctrineBundle/blob/2.12.x/src/Repository/RepositoryFactoryCompatibility.php (in ORM 3.x). This triggers now the following error in phpspec "doubling intersection types is not supported"

@stof
Copy link
Member

stof commented Mar 27, 2024

@dannyvw how is this related ? There is no union type nor intersection type in this trait

@dannyvw
Copy link

dannyvw commented Mar 27, 2024

@stof The issue is related to the changes inside EntityRepository (https://github.com/doctrine/orm/blob/3.1.x/src/EntityRepository.php) and the return type change. If you have a test case like below then it triggers this error "doubling intersection types is not supported".

    public function let(EntityManagerInterface $entityManager, EntityRepository $repository): void
    {
        $entityManager->getRepository(SomeEntity::class)->willReturn($repository);
    }

@stof
Copy link
Member

stof commented Mar 27, 2024

I doubt the code you linked is relevant then, as there is no intersection type anywhere in this file.

Anyway, this PR is not ready to be merged. If you want this to move forward without waiting for the author to get back to it, an option is to take over the work to finish it.

@dannyvw
Copy link

dannyvw commented Mar 27, 2024

Not sure either but the EntityRepository was the class inside the error. This error "doubling intersection types is not supported" is only triggered when upgrading doctrine/orm from 2.19 to 3.x.

@jdreesen
Copy link
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants