RoleInterface выдает ошибку "вызвать не-объект"

Я работаю над Symfony 2.0.16

В моем UserProvider есть метод getRoles

public function getRoles()
{
    /**
     * @var \Doctrine\Common\Collections\ArrayCollection $rol
     */
    return $this->rol->toArray();
}

и мой объект Rol имеет интерфейс ролей

class Rol implements \Symfony\Component\Security\Core\Role\RoleInterface
//...
public function getRole()
{
    return $this->getName();
}

но когда я пытаюсь войти в систему, я получаю следующую ошибку:

Неустранимая ошибка: вызов функции-члена getRole() для не-объекта в C:\Users\julian\Code\parqueadero\vendor\symfony\src\Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector.php on line 57

Чтение класса SecurityDataCollector, ошибка вызывается закрытием

array_map(function ($role){ return $role->getRole();}, $token->getRoles()

Теперь я изменяю это на

array_map(function ($role){ var_dump($role); return $role->getRole();}, $token->getRoles()

К моему удивлению, $role - это объект Rol, но Я не могу понять, почему я получаю ошибку.

Ответ 1

Я нашел решение, проблема - ошибка в PHP 5.4 (используемая php) метод сериализации пользователь github yoannch предложил это решение, перезаписывает методы serialize/unserialize, используя методы json_encode/json_decode

class User implements \Serializable

//...

/**
 * Serializes the content of the current User object
 * @return string
 */
public function serialize()
{
    return \json_encode(
            array($this->username, $this->password, $this->salt,
                    $this->rol, $this->id));
}

/**
 * Unserializes the given string in the current User object
 * @param serialized
 */
public function unserialize($serialized)
{
    list($this->username, $this->password, $this->salt,
                    $this->rol, $this->id) = \json_decode(
            $serialized);
}

требуется изменить правильные свойства имени

Ответ 2

У меня была та же проблема (Windows, PHP 5.4.5), обновленная до 5.4.7, и она по-прежнему не работает. Тем не менее, я придумал обходное решение, которое требует меньшего обслуживания (при перезаписывании функций сериализации, как описано в упомянутой статье, вам нужно будет обновлять их при добавлении/удалении полей). Пока это работает для меня, я надеюсь, что нет никаких других проблем, связанных с обходным решением, которое я, возможно, забыл. Просто измените функцию User getRoles() следующим образом:

/**
 * @inheritDoc
 */
public function getRoles()
{
    $roles = array();
    foreach ($this->userRoles as $role) {
        $roles[] = $role->getRole();
    }

    return $roles;
}

Обратите внимание, что $role->getRole() возвращает имя роли в виде строки (например, ROLE_ADMIN).

Ответ 3

решение этой задачи очень просто. Все проблемы, связанные с циклическими ссылками на ваши объекты User и Role. Таким образом, вы не должны сериализовать поля User::$roles и Role::$users.

Посмотрите Symfony\Component\Security\Core\Authentication\Token\AbstractToken::__construct() и Symfony\Component\Security\Core\Authentication\Token\AbstractToken::serialize().

Как вы можете видеть, Symfony выполняет роли пользователя, вызывая UserInterface::getRoles() перед сериализацией. И сериализуйте User и Roles отдельно.

Вам нужно реализовать интерфейс \Serializable в объектах User и Role.

Пример:

/**
 * Acme\Bundle\UserBundle\Entity\User
 * 
 * @ORM\Table(name="`user`")
 * @ORM\Entity(repositoryClass="Acme\Bundle\UserBundle\Entity\UserRepository")
 */
class User implements AdvancedUserInterface, EquatableInterface, \Serializable
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $username
     * 
     * @ORM\Column(type="string", length=30, unique=true)
     */
    private $username;

    /**
     * @var string $email
     * 
     * @ORM\Column(type="string", length=100, unique=true)
     */
    private $email;

    /**
     * @var string $salt
     *
     * @ORM\Column(type="string", length=40)
     */
    private $salt;

    /**
     * @var string $password
     *
     * @ORM\Column(type="string", length=128)
     */
    private $password;

    /**
     * @var boolean $isActive
     *
     * @ORM\Column(type="boolean")
     */
    private $isActive;

    /**
     * User roles. (Owning Side)
     * 
     * @var ArrayCollection
     * 
     * @ORM\ManyToMany(targetEntity="Role", inversedBy="users")
     */
    private $roles;

    // .....

    /**
     * @see \Serializable::serialize()
     */
    public function serialize()
    {
        /*
         * ! Don't serialize $roles field !
         */
        return \serialize(array(
            $this->id,
            $this->username,
            $this->email,
            $this->salt,
            $this->password,
            $this->isActive
        ));
    }

    /**
     * @see \Serializable::unserialize()
     */
    public function unserialize($serialized)
    {
        list (
            $this->id,
            $this->username,
            $this->email,
            $this->salt,
            $this->password,
            $this->isActive
        ) = \unserialize($serialized);
    }

}
/**
 * Acme\Bundle\UserBundle\Entity\Role
 *
 * @ORM\Table(name="role")
 * @ORM\Entity
 * 
 */
class Role implements RoleInterface, \Serializable
{
    /**
     * @var integer $id
     *
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $role
     *
     * @ORM\Column(name="role", type="string", length=20, unique=true)
     */
    private $role;

    /**
     * Users in group (Inverse Side)
     * 
     * @var ArrayCollection
     * 
     * @ORM\ManyToMany(targetEntity="User", mappedBy="roles")
     */
    private $users;

    // .....    

    /**
     * @see \Serializable::serialize()
     */
    public function serialize()
    {
        /*
         * ! Don't serialize $users field !
         */
        return \serialize(array(
            $this->id,
            $this->role
        ));
    }

    /**
     * @see \Serializable::unserialize()
     */
    public function unserialize($serialized)
    {
        list(
            $this->id,
            $this->role
        ) = \unserialize($serialized);
    }
}

И все будет правильно сериализовано/несертифицировано.

Смотрите discus на https://github.com/symfony/symfony/issues/3691

См. также: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/entities-in-session.html#serializing-entity-into-the-session