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

Adds metadata field type validation against Entity property type #10946

Merged
merged 1 commit into from
Sep 23, 2023

Conversation

greg0ire
Copy link
Member

@greg0ire greg0ire commented Sep 15, 2023

Copied from #10662

This avoid situations where you might map a decimal to a float, when it
should really be mapped to a string. This is a big pitfall because in
that situation, the ORM will consider the field changed every time.

Closes #10662 , closes #10661

@greg0ire

This comment was marked as resolved.

This avoid situations where you might map a decimal to a float, when it
should really be mapped to a string. This is a big pitfall because in
that situation, the ORM will consider the field changed every time.
@greg0ire greg0ire marked this pull request as ready for review September 15, 2023 09:30
$ce = $this->validator->validateClass($class);

self::assertEquals(
["The field 'Doctrine\Tests\ORM\Functional\Ticket\GH10661\InvalidEntity#property1' has the property type 'float' that differs from the metadata field type 'string' returned by the 'decimal' DBAL type."],
Copy link
Member Author

@greg0ire greg0ire Sep 15, 2023

Choose a reason for hiding this comment

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

I added the "returned by the 'decimal' DBAL type" part, so that people understand where we get the second type from.

/**
* @Column(type="decimal")
*/
protected float $property1;
Copy link
Member Author

Choose a reason for hiding this comment

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

I switched to decimal vs float here because I think it is a more common mistake, and it was the one I had in mind so I wanted to see how it looked for that case.

@@ -304,4 +349,66 @@ public function getUpdateSchemaList(): array

return $schemaTool->getUpdateSchemaSql($allMetadata, true);
}

/** @return list<string> containing the found issues */
private function validatePropertiesTypes(ClassMetadataInfo $class): array
Copy link
Member Author

Choose a reason for hiding this comment

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

We have no choice but to use ClassMetadataInfo here for now because of the calling code, and that should be fine, although I considered using if (PHP_VERSION_ID >= 70400 && $class instanceof ClassMetadata) {… that would be a bit weird.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand this comment. Is this because of a possible different Persistence version and validateClass(ClassMetadataInfo $class)?

Copy link
Member Author

@greg0ire greg0ire Sep 18, 2023

Choose a reason for hiding this comment

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

I'm saying that in the unique call site of this method, we have no guarantee that we have a CM and not just a CMI (so yes, it does have to do with validateClass(ClassMetadataInfo $class). This explains why we use this type declaration here.

@greg0ire
Copy link
Member Author

Cc @DavideBicego

SmallIntType::class => 'int',
StringType::class => 'string',
TextType::class => 'string',
];
Copy link
Member Author

Choose a reason for hiding this comment

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

IMO we should get rid of this harcoded map when DBAL v4 is out and switch to reflection on the DBAL type instead. The signatures will be typed.

Copy link
Member

Choose a reason for hiding this comment

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

This list is intentional though: instanceof is NOT sufficient, because subclassing allows for types to be customized in userland.

If doctrine/dbal:^4 has final on all types, then we can use instanceof

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I understand what you mean now: convertToPhpType(): T, so we look at T?

Copy link
Member Author

@greg0ire greg0ire Sep 15, 2023

Choose a reason for hiding this comment

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

Yes. It could go in a property that is wider than T, or even narrower but that's kind of dangerous I'd say. At the very least, the intersection of both types must not be empty.

@greg0ire
Copy link
Member Author

ping @derrabus @SenseException 🙂

@greg0ire greg0ire added this to the 2.17.0 milestone Sep 23, 2023
@greg0ire greg0ire merged commit 633ce41 into doctrine:2.17.x Sep 23, 2023
41 checks passed
@greg0ire greg0ire deleted the improved-validation branch September 23, 2023 10:12
@greg0ire
Copy link
Member Author

Thanks @DavideBicego !

@cuberinooo
Copy link

any recommendation for using type mixed ? For resource we used the type mixed, but unfortunatly php does not have type hint for resource therefore we used mixed. But now the validation gives the following error:
"... has the property type 'mixed' that differs from the metadata field type 'string' returned by the 'string' DBAL type. " We save the resource directly to the db as bytea type and it is typed as string on dbal type.

@norkunas
Copy link
Contributor

norkunas commented Dec 1, 2023

@cuberinooo I was asked in #11076 to check for the mixed type and ignore it always, so it will be resolved when it get's merged (if get's).

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

Successfully merging this pull request may close these issues.

Metadata field type validation against Entity property type
6 participants