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

[2.0] Add type hints #1846

Merged
merged 1 commit into from
Aug 9, 2018
Merged

[2.0] Add type hints #1846

merged 1 commit into from
Aug 9, 2018

Conversation

caciobanu
Copy link
Contributor

Q A
Type improvement
BC Break yes
Fixed issues #1704

@caciobanu
Copy link
Contributor Author

This is work in progress and only UnitOfWork & DocumentManager are done for now.

Some @param & @return docs are invalid so I think that a decision has to be taken which are supported and which not. Take a look at https://github.com/doctrine/mongodb-odm/pull/1846/files#diff-c5b64e39a058b30afece06cc7cd217b7R387 which must support https://github.com/doctrine/mongodb-odm/blob/master/lib/Doctrine/ODM/MongoDB/Query/Builder.php#L109

Copy link
Member

@alcaeus alcaeus left a comment

Choose a reason for hiding this comment

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

Early code review. As a general rule, don't rely on the null default value to make a type nullable, instead make it explicit.

It may also be beneficial to re-enable these two sniffs in our coding standard to help you with the transition: https://github.com/doctrine/mongodb-odm/blob/215e5e667474b230d41f40fac25a623106bacfed/phpcs.xml.dist#L27,,L28. Mainly, this will apply "easy" type hints when they are already clear-cut due to the @param typehint, potentially saving you tons of manual work.

Speaking of saving you from work, you can leave out any type hints from classes involved with Proxies: these will be replaced as part of #1813 so there's no point adding type hints now.

Last but not least, if you want to have separate commits for your changes, please make them descriptive. However, as it is now we'll most likely end up squashing this into one large commit.

@@ -524,25 +499,16 @@ public function merge($document)
* @param int $lockVersion
* @throws \InvalidArgumentException
*/
public function lock($document, $lockMode, $lockVersion = null)
public function lock(object $document, int $lockMode, int $lockVersion = null): void
Copy link
Member

Choose a reason for hiding this comment

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

?int $lockVersion = null please.

* @param object|null $document
*/
public function __construct($msg, $document = null)
public function __construct(string $msg, object $document = null)
Copy link
Member

Choose a reason for hiding this comment

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

?object $document = null

*/
public function findBy(array $criteria, ?array $sort = null, $limit = null, $skip = null)
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
Copy link
Member

Choose a reason for hiding this comment

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

$orderBy and $offset are incorrect - they are called $sort and $skip respectively to reflect the naming in MongoDB: db.collection.find().sort(...).limit(...).skip(...).

*/
public function ensureIndexes($timeout = null)
public function ensureIndexes(int $timeout = null): void
Copy link
Member

Choose a reason for hiding this comment

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

?int $timeout = null

*/
public function updateIndexes($timeout = null)
public function updateIndexes(int $timeout = null): void
Copy link
Member

Choose a reason for hiding this comment

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

?int $timeout = null

*/
public function getDocumentState($document, $assume = null)
public function getDocumentState(object $document, int $assume = null): int
Copy link
Member

Choose a reason for hiding this comment

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

?int $assume = null

* @throws InvalidArgumentException If the entity instance is NEW.
* @throws LockException If the document uses optimistic locking through a
* version attribute and the version check against the
* managed copy fails.
*/
private function doMerge($document, array &$visited, $prevManagedCopy = null, $assoc = null)
private function doMerge(object $document, array &$visited, object $prevManagedCopy = null, array $assoc = null): object
Copy link
Member

Choose a reason for hiding this comment

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

?object $prevManagedCopy = null, ?array $assoc = null

* @throws LockException
* @throws \InvalidArgumentException
*/
public function lock($document, $lockMode, $lockVersion = null)
public function lock(object $document, int $lockMode, int $lockVersion = null): void
Copy link
Member

Choose a reason for hiding this comment

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

?int $lockVersion = null

*/
public function clear($documentName = null)
public function clear(string $documentName = null): void
Copy link
Member

Choose a reason for hiding this comment

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

?string $documentName = null

* @internal Highly performance-sensitive method.
*/
public function getOrCreateDocument($className, $data, &$hints = [], $document = null)
public function getOrCreateDocument(string $className, array $data, array &$hints = [], object $document = null): object
Copy link
Member

Choose a reason for hiding this comment

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

?object $document = null

@alcaeus alcaeus changed the title [2.0] Add type hints [2.0] [WIP] Add type hints Aug 7, 2018
@caciobanu
Copy link
Contributor Author

Thanks for the feedback!

I'll do a rebase when I'll finish. I'm doing this step by step to be easier to fix failing tests or to update type hints. And yes, I know about Doctrine\Common\Proxy being replaced with ocramius/proxy-manager so I will ignore involved classes.

@caciobanu
Copy link
Contributor Author

@alcaeus I'm not sure about the code in lib/Doctrine/ODM/MongoDB/Aggregation. Is it worth it to add type hints there ? For some classes I couldn't find any usages.

@alcaeus
Copy link
Member

alcaeus commented Aug 7, 2018

Most of the arguments to the methods in aggregation pipeline stages accept multiple arguments: a scalar value, an array containing a MongoDB expression or an aggregation Expr object - type hinting is currently impossible unless we introduce value objects which I wouldn't do on short notice.

Do you have any specific issues with aggregation classes?

@caciobanu
Copy link
Contributor Author

@alcaeus Can you take a look at \Doctrine\ODM\MongoDB\Aggregation\Stage\GraphLookup\Match::getExpression ? I'm not sure that the method body is correct because \Doctrine\ODM\MongoDB\Aggregation\Stage::getExpression says that the return type should be an array.

@caciobanu
Copy link
Contributor Author

I think I've understood why the method body is like that: search for restrictSearchWithMatch https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/

@caciobanu
Copy link
Contributor Author

@alcaeus finished adding as much type hints as possible. There are some tests failing but I have no ideea how to fix them.

@alcaeus
Copy link
Member

alcaeus commented Aug 7, 2018

Regarding Doctrine\ODM\MongoDB\Aggregation\Stage\GraphLookup\Match::getExpression, its typehint is wrong - it should be @return array|object. The problem is that we need to be specific about arrays vs. objects for the driver. For non-empty values this is relatively easy, but for empty values we need to explicitly convert them to arrays. This might be something that might remain non-strictly typed until we can deal with it properly.

As for tests I can take a look at the two failing tests and why they fail. Step-debugging should prove helpful here to see where they go wrong. Once we've done that and fixed issues found by our static analysers (phpcs and PHPStan) I'll give this another review.

@caciobanu
Copy link
Contributor Author

I've made a minor change to Doctrine\ODM\MongoDB\Aggregation\Stage\GraphLookup\Match::getExpression so we can have only 1 return type: array. Take a look at https://github.com/doctrine/mongodb-odm/pull/1846/files#diff-149e2cae007283dbcfb76c37c91fe524R27 and https://github.com/doctrine/mongodb-odm/pull/1846/files#diff-40007aeae56972528d22a8a3f15acf17R175

@caciobanu
Copy link
Contributor Author

@alcaeus I've managed to track down the issue with the failing tests. As you can see in commit 6bdd9c6 when I changed the typehint for dm->clear() to string I couldn't provide the $user object as before so I provided the class but that made the uow to clear the objects used later in the ->contains checks. Providing an invalid string value mimics the behavior before the type hints where added.

@caciobanu caciobanu changed the title [2.0] [WIP] Add type hints [2.0] Add type hints Aug 7, 2018
@caciobanu
Copy link
Contributor Author

@alcaeus this is ready for review 👍

@alcaeus alcaeus self-requested a review August 7, 2018 17:58
Copy link
Member

@alcaeus alcaeus left a comment

Choose a reason for hiding this comment

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

Mighty impressive PR, thank you very much. I found some minor issues that need fixing. Also, as mentioned above, I believe it would be worth removing the exclusions in phpcs.xml I linked above to see if there are any other places where we might add some typehints.

{
$restrictSearchWithMatch = $this->restrictSearchWithMatch->getExpression() ?: (object) [];
Copy link
Member

Choose a reason for hiding this comment

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

👍

{
return $this->document;
}

public static function lockFailed($document)
public static function lockFailed($document): self
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this have an ?object type for $document?

* @return ObjectRepository|GridFSRepository
*/
protected function createRepository(DocumentManager $documentManager, $documentName)
protected function createRepository(DocumentManager $documentManager, string $documentName)
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 also have a common ObjectRepository return type.

*/
public function containsId($id, $rootClassName)
public function containsId(string $id, string $rootClassName): bool
Copy link
Member

Choose a reason for hiding this comment

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

This may have been documented wrongly in the docblock - most other methods don't specify a type for the ID.

*/
public function getClassNameForAssociation(array $mapping, $data)
public function getClassNameForAssociation(array $mapping, $data): string
Copy link
Member

Choose a reason for hiding this comment

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

?array $data

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems like ?array is not enough. object is needed also.

@@ -61,7 +61,7 @@ public function testRemoveEmbedMany()
$user->profileMany->removeElement($profile1);

$this->dm->flush();
$this->dm->clear($user);
$this->dm->clear('invalid');
Copy link
Member

Choose a reason for hiding this comment

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

I know this fixes the test, but if the clear didn't do anything so we might as well drop it instead of passing invalid stuff

@caciobanu
Copy link
Contributor Author

Updated after review & enabled the CS rules you mentioned above.

@alcaeus alcaeus requested a review from malarzm August 7, 2018 19:03
@malarzm
Copy link
Member

malarzm commented Aug 7, 2018

Yaaay, another few k diff PR :D Thanks a lot @caciobanu for your work on this, I'll review tomorrow while in the office :)

@caciobanu
Copy link
Contributor Author

caciobanu commented Aug 7, 2018

The activated CS rules run on lib and tests. The output is huge.

Should I run phpcbf and then go from there ?

@caciobanu
Copy link
Contributor Author

Unfortunately phpcs adds invalid parameter type hints on inherited methods from vendor packages, which lack type hints.

Copy link
Member

@alcaeus alcaeus left a comment

Choose a reason for hiding this comment

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

I was afraid that phpcs might find more than it should - we'll have to leave the sniffs excluded until we're completely done across the board, and even then might have to exclude some code pieces. Either way, I don't think it's feasible fixing those errors now - at least not at that scale.

My suggestion would be to bring this PR to a close, then focus on interfaces and abstract classes we're introducing here that don't have outside dependencies. Our first focus should be to get those cleaned up. In a second step, we should take care of other classes we're introducing, along with making them final unless we think they should be extended (which most likely, we don't). At the same time, we should also start the process of starting upstream updates in doctrine/persistence and so on, but that should wait until we got rid of doctrine/common.

*/
public function granularity($granularity)
public function granularity(string $granularity): \Doctrine\ODM\MongoDB\Aggregation\Stage\BucketAuto
Copy link
Member

Choose a reason for hiding this comment

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

This can be shortened as with the method above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Will do.

@caciobanu
Copy link
Contributor Author

Disabled the CS rules for type hinting.

Copy link
Member

@malarzm malarzm left a comment

Choose a reason for hiding this comment

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

Sorry for late and long review, but I really wanted to read this through :)

*/
public function limit($limit)
public function limit(int $limit)
Copy link
Member

Choose a reason for hiding this comment

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

Missing return type

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can't added it because of \Doctrine\ODM\MongoDB\Aggregation\Stage\GeoNear::limit which overrides it and has different returned type.

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 will add back in the comment @return Stage\Limit

* @return $this
*/
public function limit($limit)
public function limit(int $limit)
Copy link
Member

Choose a reason for hiding this comment

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

Missing return type

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See the comment above.

* @return $this
*/
public function maxDistance($maxDistance)
public function maxDistance(float $maxDistance)
Copy link
Member

Choose a reason for hiding this comment

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

Here as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I add to this method self I cannot add type hint to \Doctrine\ODM\MongoDB\Aggregation\Stage\Match::maxDistance. If I add to \Doctrine\ODM\MongoDB\Aggregation\Stage\Match::maxDistance then the method above has to return \Doctrine\ODM\MongoDB\Aggregation\Stage\Match

* @return $this
*
*/
public function minDistance($minDistance)
public function minDistance(float $minDistance)
Copy link
Member

Choose a reason for hiding this comment

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

and 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.

The same as above applies here as well.

Copy link
Member

Choose a reason for hiding this comment

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

I guess I'll need to say it at least once more, but sorry for the noise then :)

{
return $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
}

private function getReferencedFieldName($fieldName, array $mapping)
private function getReferencedFieldName(string $fieldName, array $mapping): ?string
Copy link
Member

Choose a reason for hiding this comment

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

Why is return type nullable? It seems like it's either string or an exception is thrown

* @param array|string $filter
* @return array
Copy link
Member

Choose a reason for hiding this comment

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

Same for @return

@@ -39,10 +37,7 @@ public function __construct(\ArrayIterator $metadata, $filter)
parent::__construct($metadata);
}
Copy link
Member

Choose a reason for hiding this comment

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

Can't comment on actual place, but can you set $this->_filter to be string[] and hint in ctor that $filter is string|string[]?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -273,11 +273,8 @@ public function __construct(DocumentManager $dm, EventManager $evm, HydratorFact

/**
* Factory for returning new PersistenceBuilder instances used for preparing data into
* queries for insert persistence.
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be removed ;)

@@ -2478,11 +2329,8 @@ public function getVisitedCollections($document)
/**
* INTERNAL:
* Gets PersistentCollections that are scheduled to update and related to $document
*
* @param object $document
* @return array
Copy link
Member

Choose a reason for hiding this comment

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

Could you keep this with PersistentCollectionInterface[]?

*/
public function registerManaged($document, $id, $data)
public function registerManaged(object $document, $id, $data): void
Copy link
Member

Choose a reason for hiding this comment

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

Could you add array for $data?

Copy link
Member

@malarzm malarzm left a comment

Choose a reason for hiding this comment

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

Actually even if something is still off a bit, let's get this merged once Travis is green!

@caciobanu
Copy link
Contributor Author

Before merging I can do a rebase to get rid of all those horrid commit messages.

@alcaeus
Copy link
Member

alcaeus commented Aug 9, 2018

Yeah, go ahead and give this a rebase - once the build is stable again I'd like to get this merged 🎉

@caciobanu
Copy link
Contributor Author

Done.

@alcaeus alcaeus merged commit 5e5e1fc into doctrine:master Aug 9, 2018
@alcaeus
Copy link
Member

alcaeus commented Aug 9, 2018

Thanks @caciobanu! 🎖

@caciobanu
Copy link
Contributor Author

Glad to help!

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.

None yet

3 participants