From 869a9375c213fb8ab231368a08fb5dd24eef51b8 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 30 Jun 2015 14:43:35 +0200 Subject: [PATCH 1/4] Deprecated supportsAttribute and supportsClass methods --- src/Symfony/Component/Security/CHANGELOG.md | 3 +++ .../Security/Core/Authorization/AccessDecisionManager.php | 4 ++++ .../Core/Authorization/AccessDecisionManagerInterface.php | 4 ++++ .../Security/Core/Authorization/Voter/VoterInterface.php | 4 ++++ .../Core/Tests/Authorization/AccessDecisionManagerTest.php | 6 ++++++ 5 files changed, 21 insertions(+) diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 0728da9bdb70a..a5e8170b66cde 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -12,6 +12,9 @@ CHANGELOG `Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface` instead * deprecated `Symfony\Component\Security\Core\Util\ClassUtils`, use `Symfony\Component\Security\Acl\Util\ClassUtils` instead + * deprecated `supportsAttribute()` and `supportsClass()` attributes of + `Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface` and + `Symfony\Component\Security\Core\Authorization\Voter\VoterInterface`. 2.7.0 ----- diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php index e021cc73547c8..ef942b852691f 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php @@ -77,6 +77,8 @@ public function decide(TokenInterface $token, array $attributes, $object = null) */ public function supportsAttribute($attribute) { + @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.'); + foreach ($this->voters as $voter) { if ($voter->supportsAttribute($attribute)) { return true; @@ -91,6 +93,8 @@ public function supportsAttribute($attribute) */ public function supportsClass($class) { + @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.'); + foreach ($this->voters as $voter) { if ($voter->supportsClass($class)) { return true; diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php index 16209ba4080f9..d18b5e3466873 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManagerInterface.php @@ -37,6 +37,8 @@ public function decide(TokenInterface $token, array $attributes, $object = null) * @param string $attribute An attribute * * @return bool true if this decision manager supports the attribute, false otherwise + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function supportsAttribute($attribute); @@ -46,6 +48,8 @@ public function supportsAttribute($attribute); * @param string $class A class name * * @return true if this decision manager can process the class + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function supportsClass($class); } diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php index d00ff1cfaefae..7e243f9fbd9d0 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php @@ -30,6 +30,8 @@ interface VoterInterface * @param string $attribute An attribute * * @return bool true if this Voter supports the attribute, false otherwise + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function supportsAttribute($attribute); @@ -39,6 +41,8 @@ public function supportsAttribute($attribute); * @param string $class A class name * * @return bool true if this Voter can process the class + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function supportsClass($class); diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php index bd876c729f1d8..08bbc5824518a 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php @@ -16,6 +16,9 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase { + /** + * @group legacy + */ public function testSupportsClass() { $manager = new AccessDecisionManager(array( @@ -31,6 +34,9 @@ public function testSupportsClass() $this->assertFalse($manager->supportsClass('FooClass')); } + /** + * @group legacy + */ public function testSupportsAttribute() { $manager = new AccessDecisionManager(array( From 64fd6bd982073cb424cc81d05d1cfc8607f1d154 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 9 Aug 2015 14:36:22 +0200 Subject: [PATCH 2/4] Rewrote logic of AbstractVoter --- UPGRADE-3.0.md | 37 ++++++++++ src/Symfony/Component/Security/CHANGELOG.md | 4 +- .../Authorization/Voter/AbstractVoter.php | 71 +++++++++++++++++-- .../Authorization/Voter/AbstractVoterTest.php | 56 +++++++++++++++ .../Voter/LegacyAbstractVoterTest.php | 33 +++++++++ 5 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php create mode 100644 src/Symfony/Component/Security/Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index de9a230a55468..9d842155f08ac 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -680,6 +680,43 @@ UPGRADE FROM 2.x to 3.0 )); ``` + * The `AbstractVoter::getSupportedAttributes()` and `AbstractVoter::getSupportedClasses()` + methods have been removed in favor of `AbstractVoter::supports()`. + + Before: + + ```php + class MyVoter extends AbstractVoter + { + protected function getSupportedAttributes() + { + return array('CREATE', 'EDIT'); + } + + protected function getSupportedClasses() + { + return array('AppBundle\Entity\Post'); + } + + // ... + } + ``` + + After: + + ```php + class MyVoter extends AbstractVoter + { + protected function supports($attribute, $class) + { + return $this->isClassInstanceOf($class, 'AppBundle\Entity\Post') + && in_array($attribute, array('CREATE', 'EDIT')); + } + + // ... + } + ``` + ### Translator * The `Translator::setFallbackLocale()` method has been removed in favor of diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index a5e8170b66cde..5e89a92aea65c 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -12,9 +12,11 @@ CHANGELOG `Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface` instead * deprecated `Symfony\Component\Security\Core\Util\ClassUtils`, use `Symfony\Component\Security\Acl\Util\ClassUtils` instead - * deprecated `supportsAttribute()` and `supportsClass()` attributes of + * deprecated `supportsAttribute()` and `supportsClass()` methods of `Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface` and `Symfony\Component\Security\Core\Authorization\Voter\VoterInterface`. + * deprecated `getSupportedAttributes()` and `getSupportedClasses()` methods of + `Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter`, use `supports()` instead. 2.7.0 ----- diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php index 6bbea361fd098..9c0595b1d3c9b 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php @@ -26,6 +26,8 @@ abstract class AbstractVoter implements VoterInterface */ public function supportsAttribute($attribute) { + @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.'); + return in_array($attribute, $this->getSupportedAttributes()); } @@ -34,6 +36,8 @@ public function supportsAttribute($attribute) */ public function supportsClass($class) { + @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.'); + foreach ($this->getSupportedClasses() as $supportedClass) { if ($supportedClass === $class || is_subclass_of($class, $supportedClass)) { return true; @@ -58,12 +62,13 @@ public function supportsClass($class) */ public function vote(TokenInterface $token, $object, array $attributes) { - if (!$object || !$this->supportsClass(get_class($object))) { + if (!$object) { return self::ACCESS_ABSTAIN; } // abstain vote by default in case none of the attributes are supported $vote = self::ACCESS_ABSTAIN; + $class = get_class($object); $reflector = new \ReflectionMethod($this, 'voteOnAttribute'); $isNewOverwritten = $reflector->getDeclaringClass()->getName() !== 'Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter'; @@ -72,7 +77,7 @@ public function vote(TokenInterface $token, $object, array $attributes) } foreach ($attributes as $attribute) { - if (!$this->supportsAttribute($attribute)) { + if (!$this->supports($attribute, $class)) { continue; } @@ -95,19 +100,77 @@ public function vote(TokenInterface $token, $object, array $attributes) return $vote; } + /** + * Determines if the attribute and class are supported by this voter. + * + * To determine if the passed class is instance of the supported class, the + * isClassInstanceOf() method can be used. + * + * @param string $attribute An attribute + * @param string $class The fully qualified class name of the passed object + * + * @return bool True if the attribute and class is supported, false otherwise + */ + protected function supports($attribute, $class) + { + @trigger_error('The getSupportedClasses and getSupportedAttributes methods are deprecated since version 2.8 and will be removed in version 3.0. Overwrite supports instead.'); + + $classIsSupported = false; + foreach ($this->getSupportedClasses() as $supportedClass) { + if ($this->isClassInstanceOf($class, $supportedClass)) { + $classIsSupported = true; + break; + } + } + + if (!$classIsSupported) { + return false; + } + + if (!in_array($attribute, $this->getSupportedAttributes())) { + return false; + } + + return true; + } + + /** + * A helper method to test if the actual class is instanceof or equal + * to the expected class. + * + * @param string $actualClass The actual class name + * @param string $expectedClass The expected class name + * + * @return bool + */ + protected function isClassInstanceOf($actualClass, $expectedClass) + { + return $expectedClass === $actualClass || is_subclass_of($actualClass, $expectedClass); + } + /** * Return an array of supported classes. This will be called by supportsClass. * * @return array an array of supported classes, i.e. array('Acme\DemoBundle\Model\Product') + * + * @deprecated since version 2.8, to be removed in 3.0. Use supports() instead. */ - abstract protected function getSupportedClasses(); + protected function getSupportedClasses() + { + @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.'); + } /** * Return an array of supported attributes. This will be called by supportsAttribute. * * @return array an array of supported attributes, i.e. array('CREATE', 'READ') + * + * @deprecated since version 2.8, to be removed in 3.0. Use supports() instead. */ - abstract protected function getSupportedAttributes(); + protected function getSupportedAttributes() + { + @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.'); + } /** * Perform a single access check operation on a given attribute, object and (optionally) user diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php new file mode 100644 index 0000000000000..3b11595481fdd --- /dev/null +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php @@ -0,0 +1,56 @@ +isClassInstanceOf($class, 'AbstractVoterTest_Object') + && in_array($attribute, array('EDIT', 'CREATE')); + } +} + +class AbstractVoterTest extends \PHPUnit_Framework_TestCase +{ + protected $voter; + protected $object; + protected $token; + + protected function setUp() + { + $this->voter = new AbstractVoterTest_Voter(); + $this->object = $this->getMock('AbstractVoterTest_Object'); + $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + } + + public function testAttributeAndClassSupported() + { + $this->assertEquals(VoterInterface::ACCESS_GRANTED, $this->voter->vote($this->token, $this->object, array('EDIT'))); + $this->assertEquals(VoterInterface::ACCESS_DENIED, $this->voter->vote($this->token, $this->object, array('CREATE'))); + } + + public function testAttributeNotSupported() + { + $this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $this->voter->vote($this->token, $this->object, array('DELETE'))); + } + + public function testOneAttributeSupported() + { + $this->assertEquals(VoterInterface::ACCESS_GRANTED, $this->voter->vote($this->token, $this->object, array('DELETE', 'EDIT'))); + $this->assertEquals(VoterInterface::ACCESS_DENIED, $this->voter->vote($this->token, $this->object, array('DELETE', 'CREATE'))); + } + + public function testClassNotSupported() + { + $this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $this->voter->vote($this->token, $this->getMock('AbstractVoterTest_Object1'), array('EDIT'))); + } +} diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php new file mode 100644 index 0000000000000..3a0cf1eb3993f --- /dev/null +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php @@ -0,0 +1,33 @@ +voter = new LegacyAbstractVoterTest_Voter(); + } +} From 3d4743dfcbe68af25219650f2f5243a68f15b484 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Fri, 25 Sep 2015 09:32:23 +0200 Subject: [PATCH 3/4] Cleanup AbstractVoter tests --- .../Authorization/Voter/AbstractVoterTest.php | 34 +++-- .../Voter/AbstractVoterTest.php | 122 ------------------ 2 files changed, 25 insertions(+), 131 deletions(-) delete mode 100644 src/Symfony/Component/Security/Tests/Core/Authentication/Voter/AbstractVoterTest.php diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php index 3b11595481fdd..23ac6db71aa00 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php @@ -2,12 +2,13 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; class AbstractVoterTest_Voter extends AbstractVoter { - protected function isGranted($attribute, $object, $user = null) + protected function voteOnAttribute($attribute, $object, TokenInterface $token) { return 'EDIT' === $attribute; } @@ -34,23 +35,38 @@ protected function setUp() public function testAttributeAndClassSupported() { - $this->assertEquals(VoterInterface::ACCESS_GRANTED, $this->voter->vote($this->token, $this->object, array('EDIT'))); - $this->assertEquals(VoterInterface::ACCESS_DENIED, $this->voter->vote($this->token, $this->object, array('CREATE'))); + $this->assertEquals(VoterInterface::ACCESS_GRANTED, $this->voter->vote($this->token, $this->object, array('EDIT')), 'ACCESS_GRANTED if attribute grants access'); + $this->assertEquals(VoterInterface::ACCESS_DENIED, $this->voter->vote($this->token, $this->object, array('CREATE')), 'ACESS_DENIED if attribute denies access'); } - public function testAttributeNotSupported() + public function testOneAttributeSupported() { - $this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $this->voter->vote($this->token, $this->object, array('DELETE'))); + $this->assertEquals(VoterInterface::ACCESS_GRANTED, $this->voter->vote($this->token, $this->object, array('DELETE', 'EDIT')), 'ACCESS_GRANTED if supported attribute grants access'); + $this->assertEquals(VoterInterface::ACCESS_DENIED, $this->voter->vote($this->token, $this->object, array('DELETE', 'CREATE')), 'ACCESS_DENIED if supported attribute denies access'); } - public function testOneAttributeSupported() + public function testOneAttributeGrantsAccess() + { + $this->assertEquals(VoterInterface::ACCESS_GRANTED, $this->voter->vote($this->token, $this->object, array('CREATE', 'EDIT')), 'ACCESS_GRANTED'); + } + + public function testNoAttributeSupported() { - $this->assertEquals(VoterInterface::ACCESS_GRANTED, $this->voter->vote($this->token, $this->object, array('DELETE', 'EDIT'))); - $this->assertEquals(VoterInterface::ACCESS_DENIED, $this->voter->vote($this->token, $this->object, array('DELETE', 'CREATE'))); + $this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $this->voter->vote($this->token, $this->object, array('DELETE')), 'ACCESS_ABSTAIN'); } public function testClassNotSupported() { - $this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $this->voter->vote($this->token, $this->getMock('AbstractVoterTest_Object1'), array('EDIT'))); + $this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $this->voter->vote($this->token, $this->getMock('AbstractVoterTest_Object1'), array('EDIT')), 'ACCESS_ABSTAIN'); + } + + public function testNullObject() + { + $this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $this->voter->vote($this->token, null, array('EDIT')), 'ACCESS_ABSTAIN'); + } + + public function testNoAttributes() + { + $this->assertEquals(VoterInterface::ACCESS_ABSTAIN, $this->voter->vote($this->token, $this->object, array()), 'ACCESS_ABSTAIN'); } } diff --git a/src/Symfony/Component/Security/Tests/Core/Authentication/Voter/AbstractVoterTest.php b/src/Symfony/Component/Security/Tests/Core/Authentication/Voter/AbstractVoterTest.php deleted file mode 100644 index ecf82fbfe49f1..0000000000000 --- a/src/Symfony/Component/Security/Tests/Core/Authentication/Voter/AbstractVoterTest.php +++ /dev/null @@ -1,122 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Tests\Core\Authentication\Voter; - -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; - -/** - * @author Roman Marintšenko - */ -class AbstractVoterTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var AbstractVoter - */ - private $voter; - - private $token; - - protected function setUp() - { - $this->voter = new VoterFixture(); - - $tokenMock = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); - $tokenMock - ->expects($this->any()) - ->method('getUser') - ->will($this->returnValue('user')); - - $this->token = $tokenMock; - } - - /** - * @dataProvider getData - */ - public function testVote($expectedVote, $object, $attributes, $message) - { - $this->assertEquals($expectedVote, $this->voter->vote($this->token, $object, $attributes), $message); - } - - /** - * @dataProvider getData - * @group legacy - */ - public function testVoteUsingDeprecatedIsGranted($expectedVote, $object, $attributes, $message) - { - $voter = new DeprecatedVoterFixture(); - - $this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message); - } - - public function getData() - { - return array( - array(AbstractVoter::ACCESS_ABSTAIN, null, array(), 'ACCESS_ABSTAIN for null objects'), - array(AbstractVoter::ACCESS_ABSTAIN, new UnsupportedObjectFixture(), array(), 'ACCESS_ABSTAIN for objects with unsupported class'), - array(AbstractVoter::ACCESS_ABSTAIN, new ObjectFixture(), array(), 'ACCESS_ABSTAIN for no attributes'), - array(AbstractVoter::ACCESS_ABSTAIN, new ObjectFixture(), array('foobar'), 'ACCESS_ABSTAIN for unsupported attributes'), - array(AbstractVoter::ACCESS_GRANTED, new ObjectFixture(), array('foo'), 'ACCESS_GRANTED if attribute grants access'), - array(AbstractVoter::ACCESS_GRANTED, new ObjectFixture(), array('bar', 'foo'), 'ACCESS_GRANTED if *at least one* attribute grants access'), - array(AbstractVoter::ACCESS_GRANTED, new ObjectFixture(), array('foobar', 'foo'), 'ACCESS_GRANTED if *at least one* attribute grants access'), - array(AbstractVoter::ACCESS_DENIED, new ObjectFixture(), array('bar', 'baz'), 'ACCESS_DENIED for if no attribute grants access'), - ); - } -} - -class VoterFixture extends AbstractVoter -{ - protected function getSupportedClasses() - { - return array( - 'Symfony\Component\Security\Tests\Core\Authentication\Voter\ObjectFixture', - ); - } - - protected function getSupportedAttributes() - { - return array('foo', 'bar', 'baz'); - } - - protected function voteOnAttribute($attribute, $object, TokenInterface $token) - { - return $attribute === 'foo'; - } -} - -class DeprecatedVoterFixture extends AbstractVoter -{ - protected function getSupportedClasses() - { - return array( - 'Symfony\Component\Security\Tests\Core\Authentication\Voter\ObjectFixture', - ); - } - - protected function getSupportedAttributes() - { - return array('foo', 'bar', 'baz'); - } - - protected function isGranted($attribute, $object, $user = null) - { - return $attribute === 'foo'; - } -} - -class ObjectFixture -{ -} - -class UnsupportedObjectFixture -{ -} From 8567966394bfee307401954f861a1511b091f57a Mon Sep 17 00:00:00 2001 From: WouterJ Date: Fri, 25 Sep 2015 09:33:06 +0200 Subject: [PATCH 4/4] Indicate method will become abstract in 3.0 --- .../Security/Core/Authorization/Voter/AbstractVoter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php index 9c0595b1d3c9b..2cafc5f952bd7 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php @@ -106,6 +106,8 @@ public function vote(TokenInterface $token, $object, array $attributes) * To determine if the passed class is instance of the supported class, the * isClassInstanceOf() method can be used. * + * This method will become abstract in 3.0. + * * @param string $attribute An attribute * @param string $class The fully qualified class name of the passed object *