<?php
namespace Doctrine\ORM {
class EntityManager {
/**
* @template T
* @template-typeof T $class_name
* @return EntityRepository<T>
*/
public function getRepository(string $class_name) {}
}
/**
* @template T as object
*/
class EntityRepository {
/**
* Finds an entity by its primary key / identifier.
*
* @param mixed $id The identifier.
* @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants
* or NULL if no specific lock mode should be used
* during the search.
* @param int|null $lockVersion The lock version.
*
* @return T|null The entity instance or NULL if the entity can not be found.
*/
public function find($id, $lockMode = null, $lockVersion = null)
{
return $this->_em->find($this->_entityName, $id, $lockMode, $lockVersion);
}
/**
* Finds all entities in the repository.
*
* @return T[] The entities.
*/
public function findAll()
{
return $this->findBy([]);
}
/**
* Finds entities by a set of criteria.
*
* @param array $criteria
* @param array|null $orderBy
* @param int|null $limit
* @param int|null $offset
*
* @return T[] The objects.
*/
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
{
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
return $persister->loadAll($criteria, $orderBy, $limit, $offset);
}
/**
* Finds a single entity by a set of criteria.
*
* @param array $criteria
* @param array|null $orderBy
*
* @return T|null The entity instance or NULL if the entity can not be found.
*/
public function findOneBy(array $criteria, array $orderBy = null)
{
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
return $persister->load($criteria, null, null, [], null, 1, $orderBy);
}
}
}
namespace Doctrine\Common\Collections {
/**
* @template T
*/
interface Collection extends Countable, IteratorAggregate, ArrayAccess
{
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @param T $element The element to search for.
*
* @return bool TRUE if the collection contains the element, FALSE otherwise.
*/
public function contains($element);
/**
* Removes the element at the specified index from the collection.
*
* @param string|int $key The kex/index of the element to remove.
*
* @return T|null The removed element or NULL, if the collection did not contain the element.
*/
public function remove($key);
/**
* Removes the specified element from the collection, if it is found.
*
* @param T $element The element to remove.
*
* @return bool TRUE if this collection contained the specified element, FALSE otherwise.
*/
public function removeElement($element);
/**
* Gets the element at the specified key/index.
*
* @param string|int $key The key/index of the element to retrieve.
*
* @return T
*/
public function get($key);
/**
* Gets all values of the collection.
*
* @return T[] The values of all elements in the collection, in the order they
* appear in the collection.
*/
public function getValues();
/**
* Sets an element in the collection at the specified key/index.
*
* @param string|int $key The key/index of the element to set.
* @param T $value The element to set.
*
* @return void
*/
public function set($key, $value);
/**
* Gets a native PHP array representation of the collection.
*
* @return T[]
*/
public function toArray();
/**
* Sets the internal iterator to the first element in the collection and returns this element.
*
* @return T
*/
public function first();
/**
* Sets the internal iterator to the last element in the collection and returns this element.
*
* @return T
*/
public function last();
/**
* Gets the element of the collection at the current iterator position.
*
* @return T
*/
public function current();
/**
* Moves the internal iterator position to the next element and returns this element.
*
* @return T
*/
public function next();
/**
* Partitions this collection in two collections according to a predicate.
* Keys are preserved in the resulting collections.
*
* @param Closure $p The predicate on which to partition.
*
* @return Collection<T>[] An array with two elements. The first element contains the collection
* of elements where the predicate returned TRUE, the second element
* contains the collection of elements where the predicate returned FALSE.
*/
public function partition(Closure $p);
/**
* Gets the index/key of a given element. The comparison of two elements is strict,
* that means not only the value but also the type must match.
* For objects this means reference equality.
*
* @param T $element The element to search for.
*
* @return int|string|bool The key/index of the element or FALSE if the element was not found.
*/
public function indexOf($element);
/**
* Extracts a slice of $length elements starting at position $offset from the Collection.
*
* If $length is null it returns all elements from $offset to the end of the Collection.
* Keys have to be preserved by this method. Calling this method will only return the
* selected slice and NOT change the elements contained in the collection slice is called on.
*
* @param int $offset The offset to start from.
* @param int|null $length The maximum number of elements to return, or null for no limit.
*
* @return T[]
*/
public function slice($offset, $length = null);
}
}
Partial stubs like this should be importable into a given project by including the package via composer.
Also findBy() and findOneBy(): https://github.com/doctrine/common/blob/2.8/lib/Doctrine/Common/Persistence/ObjectRepository.php
That would be immensely useful, especially being able to partially stub things. We have some stubs for Doctrine Collections and a couple of other libraries, it would be nice to have a way to distribute them.
I've updated the stubs above to one that I've got working. Unfortunately I don't have a codebase that uses Doctrine ORM to test against - any suggestions?
Also @weirdan if you could post those stubs to a new repo, you could require that repo via composer and include a ref to the vendor/.../stubs.php file in your psalm config.
I think the best thing is to require each extension repo have a stubs.php file in the root directory (which itself can require other files)
For me, relying on the file paths is a problematic part (due to the particular setup we use, but also in general), so I'd like to relegate as much of the file structure heavy-lifting to composer. Which means I'd like to see plugins addressed by namespace/classname, not by file path.
If you're open to this idea I can draft up a kind of proposal the following week.
@weirdan absolutely. The only thing I was thinking is that it I'd want to provide an easy way to disable extensions/plugins, which is cumbersome if you have to do it via Composer.
I was thinking something like
<extensions>
<directory name="vendor/muglug/psalm-doctrine-ext" />
</extensions>
You may also composer require a project that itself composer requires a given Psalm extension in composer, but you don't want Psalm to use it when analysing your _own_ code.
The only thing I was thinking is that it I'd want to provide an easy way to disable extensions/plugins,
Sure, it's on my todo.
I was thinking something like
<directory name="vendor/...."/>
That's file path, and that's what I'd like to avoid. The approach I'm working on wouldn't require any file paths (but PoC would be a bit tied to composer infrastructure, with an option to unbind it later).
You may also composer require a project that itself composer requires a given Psalm extension in composer, but you don't want Psalm to use it when analysing your own code.
It's likely to be dev-required (rather than required), and composer does not install dev-dependencies of your dependencies. Still, it's a valid point, and would be addressed.
Still, it's a valid point, and would be addressed.
Brilliant! If you can address that, then I'm happy relying on composer. I was thinking that each package would add a registration script into autoload_files, but that'd have to be a short easy snippet.
@weirdan is this done in your own plugin?
Mostly. I don't have EntityManager there, but considered adding that (and renaming to psalm-doctrine). I plan to get back to it the week after next, when I'm back in the office.
Thanks!
Most helpful comment
Mostly. I don't have EntityManager there, but considered adding that (and renaming to psalm-doctrine). I plan to get back to it the week after next, when I'm back in the office.