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

Code-generation fatal error prevention #194

Merged
merged 49 commits into from
Nov 8, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c95975b
First attempt to write a test that verifies if ProxyManager will cras…
Ocramius Oct 23, 2014
6095e77
Minor docblock fixes/clarifications
Ocramius Oct 23, 2014
9785349
Minor docblock fixes/clarifications
Ocramius Oct 23, 2014
10973fe
Adding tests for lazy loading value holder instantiation with all def…
Ocramius Oct 23, 2014
1d4e914
Adding tests for null object instantiation with all defined classes
Ocramius Oct 23, 2014
52f7c51
Using the raw code generator in tests
Ocramius Oct 23, 2014
581f704
Mapping over existing classes and generators instead of manually defi…
Ocramius Oct 23, 2014
cae8c47
Removing limits over declared classes list - now testing all declared…
Ocramius Oct 23, 2014
75379c9
Skipping `final` classes when generating proxies: throwing an excepti…
Ocramius Oct 23, 2014
cddb91b
Skipping over non-test-asset classes that are found in the project, a…
Ocramius Oct 25, 2014
1f78150
Passing `-n` as parameter (should run without extensions)
Ocramius Oct 25, 2014
c0064af
Remove duplication of interfaces
Oct 27, 2014
3ed8eba
Turn interfaces unique on collection
Oct 27, 2014
c5a0a7f
Generate abstract methods to be compliance with method signatures
Oct 27, 2014
2051528
Fix abstract methods inheritance
Oct 27, 2014
bdfaba9
Fix interfaces implements
Oct 27, 2014
71bba0e
Remove debug line of code
Oct 27, 2014
f9744d7
Remove call to `setBody()` with a empty string
Oct 2, 2011
388c3b3
Update namespaces, remove alias for ClassGenerator
Oct 12, 2011
c7c3ca2
Modify name of "createCollection" to "buildConcreteMethodsFromOrigina…
Nov 6, 2014
700dd8b
Use array_map instead of foreach
Nov 6, 2014
a80bae9
Simplify `ClassGeneratorUtils` removing the method `getAbstractMethod…
Nov 6, 2014
9f65e13
Refactor call of method and arguments to create AbstractMethod Collec…
Nov 6, 2014
e429068
Simplify `setImplementedInterfaces`
Nov 6, 2014
86a69aa
Fix scope to create a class inside a Closure
Nov 6, 2014
d04340e
Hidden the E_STRICT message
Nov 6, 2014
341256a
Move errors settings to the constructor
Nov 6, 2014
293f4b6
Realocate ERRORS SETTINGS
Nov 6, 2014
3f8c80e
Fix CS
Nov 6, 2014
63e2891
Remove error_reporting
Nov 7, 2014
4cc4263
Fix docblock
Nov 7, 2014
62f7df9
Instantiate class by name directly and Fix Cd
Nov 7, 2014
1154d42
Skip test prevention
Nov 7, 2014
27f290e
#193 - reverting changes to `ClassGeneratorUtils`, as duplicate metho…
Ocramius Nov 8, 2014
bb83f3d
#193 - specification for a `CanProxyAssertion` implementation
Ocramius Nov 8, 2014
4b48b12
#193 - test assets required for the `CanProxyAssertion` tests
Ocramius Nov 8, 2014
eac0629
#193 - test coverage for `ProxyManager\Exception\InvalidProxiedClassE…
Ocramius Nov 8, 2014
096dcd9
#193 - specification for for `ProxyManager\Exception\InvalidProxiedCl…
Ocramius Nov 8, 2014
ecab662
#193 - implementing `ProxyManager\Exception\InvalidProxiedClassExcept…
Ocramius Nov 8, 2014
db34bbd
#193 - implementing `ProxyManager\Exception\CanProxyAssertion` logic
Ocramius Nov 8, 2014
ab0ca07
#193 - verifying that `ProxyManager\Exception\CanProxyAssertion` cann…
Ocramius Nov 8, 2014
7cc76d8
#193 - `ProxyManager\Exception\CanProxyAssertion` cannot be instantiated
Ocramius Nov 8, 2014
560a129
#193 - using `ProxyManager\Exception\CanProxyAssertion` in the genera…
Ocramius Nov 8, 2014
06171ed
#193 - removing `ProxyManager\ProxyGenerator\AccessInterceptorScopeLo…
Ocramius Nov 8, 2014
30bb59b
#193 - optimized imports
Ocramius Nov 8, 2014
680783f
#193 - skip heavy integration tests on HHVM (too slow for now)
Ocramius Nov 8, 2014
892c15a
#193 - minor CS fix as per PHP_CodeSniffer
Ocramius Nov 8, 2014
3147025
#193 - test skipping should not be defined in the data provider, but …
Ocramius Nov 8, 2014
a5626df
#193 - correct error message when skipping the fatal prevention funct…
Ocramius Nov 8, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/ProxyManager/Exception/InvalidProxiedClassException.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

use InvalidArgumentException;
use ReflectionClass;
use ReflectionMethod;

/**
* Exception for invalid proxied classes
Expand All @@ -38,4 +39,41 @@ public static function interfaceNotSupported(ReflectionClass $reflection)
{
return new self(sprintf('Provided interface "%s" cannot be proxied', $reflection->getName()));
}

/**
* @param ReflectionClass $reflection
*
* @return self
*/
public static function finalClassNotSupported(ReflectionClass $reflection)
{
return new self(sprintf('Provided class "%s" is final and cannot be proxied', $reflection->getName()));
}

/**
* @param ReflectionClass $reflection
*
* @return self
*/
public static function abstractProtectedMethodsNotSupported(ReflectionClass $reflection)
{
return new self(sprintf(
'Provided class "%s" has following protected abstract methods, and therefore cannot be proxied:' . "\n%s",
$reflection->getName(),
implode(
"\n",
array_map(
function (ReflectionMethod $reflectionMethod) {
return $reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName();
},
array_filter(
$reflection->getMethods(),
function (ReflectionMethod $method) {
return $method->isAbstract() && $method->isProtected();
}
)
)
)
));
}
}
7 changes: 4 additions & 3 deletions src/ProxyManager/Generator/Util/ClassGeneratorUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
namespace ProxyManager\Generator\Util;

use ReflectionClass;
use ReflectionMethod;
use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\MethodGenerator;
use Zend\Code\Generator\ClassGenerator as GeneratorClass;

/**
* Util class to help to generate code
Expand All @@ -32,14 +33,14 @@ final class ClassGeneratorUtils
{
/**
* @param ReflectionClass $originalClass
* @param GeneratorClass $classGenerator
* @param ClassGenerator $classGenerator
* @param MethodGenerator $generatedMethod
*
* @return void|false
*/
public static function addMethodIfNotFinal(
ReflectionClass $originalClass,
GeneratorClass $classGenerator,
ClassGenerator $classGenerator,
MethodGenerator $generatedMethod
) {
$methodName = $generatedMethod->getName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@

namespace ProxyManager\ProxyGenerator;

use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodPrefixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodSuffixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodPrefixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\AbstractMethod;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\InterceptedMethod;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicClone;
Expand All @@ -31,6 +31,7 @@
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicSet;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicSleep;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicUnset;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ReflectionClass;
use ReflectionMethod;
Expand All @@ -54,9 +55,7 @@ class AccessInterceptorScopeLocalizerGenerator implements ProxyGeneratorInterfac
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
if ($originalClass->isInterface()) {
throw InvalidProxiedClassException::interfaceNotSupported($originalClass);
}
CanProxyAssertion::assertClassCanBeProxied($originalClass, false);

$classGenerator->setExtendedClass($originalClass->getName());
$classGenerator->setImplementedInterfaces(array('ProxyManager\\Proxy\\AccessInterceptorInterface'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodSuffixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodPrefixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodSuffixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\AbstractMethod;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\InterceptedMethod;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicClone;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicGet;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicIsset;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicSet;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicUnset;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\PropertyGenerator\ValueHolderProperty;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
Expand Down Expand Up @@ -58,6 +60,8 @@ class AccessInterceptorValueHolderGenerator implements ProxyGeneratorInterface
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);

$publicProperties = new PublicPropertiesMap($originalClass);
$interfaces = array(
'ProxyManager\\Proxy\\AccessInterceptorInterface',
Expand Down
102 changes: 102 additions & 0 deletions src/ProxyManager/ProxyGenerator/Assertion/CanProxyAssertion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ProxyManager\ProxyGenerator\Assertion;

use BadMethodCallException;
use ProxyManager\Exception\InvalidProxiedClassException;
use ReflectionClass;
use ReflectionMethod;

/**
* Assertion that verifies that a class can be proxied
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
final class CanProxyAssertion
{
/**
* Disabled constructor: not meant to be instantiated
*
* @throws BadMethodCallException
*/
public function __construct()
{
throw new BadMethodCallException('Unsupported constructor.');
}

/**
* @param ReflectionClass $originalClass
* @param bool $allowInterfaces
*
* @throws InvalidProxiedClassException
*/
public static function assertClassCanBeProxied(ReflectionClass $originalClass, $allowInterfaces = true)
{
self::isNotFinal($originalClass);
self::hasNoAbstractProtectedMethods($originalClass);

if (! $allowInterfaces) {
self::isNotInterface($originalClass);
}
}

/**
* @param ReflectionClass $originalClass
*
* @throws InvalidProxiedClassException
*/
private static function isNotFinal(ReflectionClass $originalClass)
{
if ($originalClass->isFinal()) {
throw InvalidProxiedClassException::finalClassNotSupported($originalClass);
}
}

/**
* @param ReflectionClass $originalClass
*
* @throws InvalidProxiedClassException
*/
private static function hasNoAbstractProtectedMethods(ReflectionClass $originalClass)
{
$protectedAbstract = array_filter(
$originalClass->getMethods(),
function (ReflectionMethod $method) {
return $method->isAbstract() && $method->isProtected();
}
);

if ($protectedAbstract) {
throw InvalidProxiedClassException::abstractProtectedMethodsNotSupported($originalClass);
}
}

/**
* @param ReflectionClass $originalClass
*
* @throws InvalidProxiedClassException
*/
private static function isNotInterface(ReflectionClass $originalClass)
{
if ($originalClass->isInterface()) {
throw InvalidProxiedClassException::interfaceNotSupported($originalClass);
}
}
}
4 changes: 4 additions & 0 deletions src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
namespace ProxyManager\ProxyGenerator;

use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\AbstractMethod;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\CallInitializer;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\GetProxyInitializer;
Expand Down Expand Up @@ -58,6 +60,8 @@ class LazyLoadingGhostGenerator implements ProxyGeneratorInterface
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);

$interfaces = array('ProxyManager\\Proxy\\GhostObjectInterface');
$publicProperties = new PublicPropertiesMap($originalClass);
$publicPropsDefaults = new PublicPropertiesDefaults($originalClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

namespace ProxyManager\ProxyGenerator;

use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\MagicWakeup;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\AbstractMethod;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator\GetProxyInitializer;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator\InitializeProxy;
Expand All @@ -36,7 +39,6 @@
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\GetWrappedValueHolderValue;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ReflectionClass;
use ReflectionMethod;
use Zend\Code\Generator\ClassGenerator;
Expand All @@ -58,6 +60,8 @@ class LazyLoadingValueHolderGenerator implements ProxyGeneratorInterface
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);

$interfaces = array('ProxyManager\\Proxy\\VirtualProxyInterface');
$publicProperties = new PublicPropertiesMap($originalClass);

Expand Down
8 changes: 4 additions & 4 deletions src/ProxyManager/ProxyGenerator/NullObjectGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
namespace ProxyManager\ProxyGenerator;

use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\AbstractMethod;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\NullObject\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\NullObject\MethodGenerator\NullObjectMethodInterceptor;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
Expand All @@ -41,14 +43,12 @@ class NullObjectGenerator implements ProxyGeneratorInterface
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);

$interfaces = array('ProxyManager\\Proxy\\NullObjectInterface');

if ($originalClass->isInterface()) {
$interfaces[] = $originalClass->getName();
} else {
foreach ($originalClass->getInterfaceNames() as $name) {
$interfaces[] = $name;
}
}

$classGenerator->setImplementedInterfaces($interfaces);
Expand Down
4 changes: 4 additions & 0 deletions src/ProxyManager/ProxyGenerator/RemoteObjectGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
namespace ProxyManager\ProxyGenerator;

use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\AbstractMethod;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\MagicGet;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\MagicIsset;
Expand Down Expand Up @@ -48,6 +50,8 @@ class RemoteObjectGenerator implements ProxyGeneratorInterface
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);

$interfaces = array('ProxyManager\\Proxy\\RemoteObjectInterface');

if ($originalClass->isInterface()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@
*/
class InvalidProxiedClassExceptionTest extends PHPUnit_Framework_TestCase
{
/**
* @covers \ProxyManager\Exception\InvalidProxiedClassException::interfaceNotSupported
*/
public function testInterfaceNotSupported()
{
$this->assertSame(
Expand All @@ -45,4 +42,26 @@ public function testInterfaceNotSupported()
)->getMessage()
);
}

public function testFinalClassNotSupported()
{
$this->assertSame(
'Provided class "ProxyManagerTestAsset\FinalClass" is final and cannot be proxied',
InvalidProxiedClassException::finalClassNotSupported(
new ReflectionClass('ProxyManagerTestAsset\FinalClass')
)->getMessage()
);
}

public function testAbstractProtectedMethodsNotSupported()
{
$this->assertSame(
'Provided class "ProxyManagerTestAsset\ClassWithAbstractProtectedMethod" has following protected abstract'
. ' methods, and therefore cannot be proxied:' . "\n"
. 'ProxyManagerTestAsset\ClassWithAbstractProtectedMethod::protectedAbstractMethod',
InvalidProxiedClassException::abstractProtectedMethodsNotSupported(
new ReflectionClass('ProxyManagerTestAsset\ClassWithAbstractProtectedMethod')
)->getMessage()
);
}
}
Loading