bug #16772 Refactoring EntityUserProvider::__construct() to not do work, cause cache warm error (weaverryan)

This PR was submitted for the 2.8 branch but it was merged into the 2.3 branch instead (closes #16772).

Discussion
----------

Refactoring EntityUserProvider::__construct() to not do work, cause cache warm error

| Q             | A
| ------------- | ---
| Bug fix?      | "yes"
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | none
| License       | MIT
| Doc PR        | n/a

This fixes a "Database not Found" error when using `doctrine/orm` 2.5 while warming up your cache under certain situations. Basically, if you use the `EntityUserProvider`, then during cache warmup, Twig requires the `security.authorization_checker` which eventually requires this `EntityUserProvider`, which previously caused Doctrine to calculate the metadata for your User class. If no database exists (and you haven't specified the platform), you'll get the error.

More broadly, this simply tries to do less work in the constructor. It's a "bug fix", only kind of, but as it's completely an internal refactoring, it should be quite safe.

Thanks!

Commits
-------

44a2861 Refactoring EntityUserProvider::__construct() to not do work, cause cache warm error
This commit is contained in:
Christophe Coevoet 2015-12-02 16:35:50 +01:00
commit fb791fdbcb
2 changed files with 51 additions and 23 deletions

View File

@ -27,22 +27,17 @@ use Symfony\Component\Security\Core\User\UserInterface;
*/
class EntityUserProvider implements UserProviderInterface
{
private $registry;
private $managerName;
private $classOrAlias;
private $class;
private $repository;
private $property;
private $metadata;
public function __construct(ManagerRegistry $registry, $class, $property = null, $managerName = null)
public function __construct(ManagerRegistry $registry, $classOrAlias, $property = null, $managerName = null)
{
$em = $registry->getManager($managerName);
$this->class = $class;
$this->metadata = $em->getClassMetadata($class);
if (false !== strpos($this->class, ':')) {
$this->class = $this->metadata->getName();
}
$this->repository = $em->getRepository($class);
$this->registry = $registry;
$this->managerName = $managerName;
$this->classOrAlias = $classOrAlias;
$this->property = $property;
}
@ -51,14 +46,15 @@ class EntityUserProvider implements UserProviderInterface
*/
public function loadUserByUsername($username)
{
$repository = $this->getRepository();
if (null !== $this->property) {
$user = $this->repository->findOneBy(array($this->property => $username));
$user = $repository->findOneBy(array($this->property => $username));
} else {
if (!$this->repository instanceof UserProviderInterface) {
throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement UserProviderInterface.', get_class($this->repository)));
if (!$repository instanceof UserProviderInterface) {
throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement UserProviderInterface.', get_class($repository)));
}
$user = $this->repository->loadUserByUsername($username);
$user = $repository->loadUserByUsername($username);
}
if (null === $user) {
@ -73,18 +69,20 @@ class EntityUserProvider implements UserProviderInterface
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof $this->class) {
$class = $this->getClass();
if (!$user instanceof $class) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
if ($this->repository instanceof UserProviderInterface) {
$refreshedUser = $this->repository->refreshUser($user);
$repository = $this->getRepository();
if ($repository instanceof UserProviderInterface) {
$refreshedUser = $repository->refreshUser($user);
} else {
// The user must be reloaded via the primary key as all other data
// might have changed without proper persistence in the database.
// That's the case when the user has been changed by a form with
// validation errors.
if (!$id = $this->metadata->getIdentifierValues($user)) {
if (!$id = $this->getClassMetadata()->getIdentifierValues($user)) {
throw new \InvalidArgumentException('You cannot refresh a user '.
'from the EntityUserProvider that does not contain an identifier. '.
'The user object has to be serialized with its own identifier '.
@ -92,7 +90,7 @@ class EntityUserProvider implements UserProviderInterface
);
}
$refreshedUser = $this->repository->find($id);
$refreshedUser = $repository->find($id);
if (null === $refreshedUser) {
throw new UsernameNotFoundException(sprintf('User with id %s not found', json_encode($id)));
}
@ -106,6 +104,36 @@ class EntityUserProvider implements UserProviderInterface
*/
public function supportsClass($class)
{
return $class === $this->class || is_subclass_of($class, $this->class);
return $class === $this->getClass() || is_subclass_of($class, $this->getClass());
}
private function getObjectManager()
{
return $this->registry->getManager($this->managerName);
}
private function getRepository()
{
return $this->getObjectManager()->getRepository($this->classOrAlias);
}
private function getClass()
{
if (null === $this->class) {
$class = $this->classOrAlias;
if (false !== strpos($class, ':')) {
$class = $this->getClassMetadata()->getName();
}
$this->class = $class;
}
return $this->class;
}
private function getClassMetadata()
{
return $this->getObjectManager()->getClassMetadata($this->classOrAlias);
}
}

View File

@ -92,7 +92,7 @@ class EntityUserProviderTest extends \PHPUnit_Framework_TestCase
private function getManager($em, $name = null)
{
$manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
$manager->expects($this->once())
$manager->expects($this->any())
->method('getManager')
->with($this->equalTo($name))
->will($this->returnValue($em));