Skip to content

Commit

Permalink
Merge 0fb7f62 into fd458d3
Browse files Browse the repository at this point in the history
  • Loading branch information
Ocramius committed May 1, 2014
2 parents fd458d3 + 0fb7f62 commit 2ffe88c
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,28 @@ class HydratorMethodsVisitor extends PHPParser_NodeVisitorAbstract
*/
public function __construct(ReflectionClass $reflectedClass)
{
$this->reflectedClass = $reflectedClass;
$this->accessibleProperties = $this->reflectedClass->getProperties(
(ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PUBLIC)
& ~ReflectionProperty::IS_STATIC
$this->reflectedClass = $reflectedClass;
$instanceProperties = array_filter(
$this->reflectedClass->getProperties(),
function (ReflectionProperty $property) {
return ! $property->isStatic();
}
);
$this->accessibleProperties = array_filter(
$instanceProperties,
function (ReflectionProperty $property) {
return $property->isProtected() || $property->isPublic();
}
);
/* @var $privateProperties \ReflectionProperty[] */
$privateProperties = array_filter(
$instanceProperties,
function (ReflectionProperty $property) {
return $property->isPrivate();
}
);

foreach ($reflectedClass->getProperties(ReflectionProperty::IS_PRIVATE) as $property) {
foreach ($privateProperties as $property) {
$this->propertyWriters[$property->getName()] = new PropertyAccessor($property, 'Writer');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,19 @@ class HydratorMethodsVisitorTest extends PHPUnit_Framework_TestCase
*
* @param string $className
* @param PHPParser_Node_Stmt_Class $classNode
* @param string[] $properties
*/
public function testBasicCodeGeneration($className, PHPParser_Node_Stmt_Class $classNode)
public function testBasicCodeGeneration($className, PHPParser_Node_Stmt_Class $classNode, array $properties)
{
$visitor = new HydratorMethodsVisitor(new ReflectionClass($className));

/* @var $modifiedAst PHPParser_Node_Stmt_Class */
$modifiedNode = $visitor->leaveNode($classNode);

$this->checkMethodExistence('hydrate', $modifiedNode);
$this->checkMethodExistence('extract', $modifiedNode);
$this->checkMethodExistence('__construct', $modifiedNode);
$this->assertMethodExistence('hydrate', $modifiedNode);
$this->assertMethodExistence('extract', $modifiedNode);
$this->assertMethodExistence('__construct', $modifiedNode);
$this->assertContainsPropertyAccessors($modifiedNode, $properties);
}

/**
Expand All @@ -61,9 +63,8 @@ public function testBasicCodeGeneration($className, PHPParser_Node_Stmt_Class $c
* @param string $methodName
* @param PHPParser_Node_Stmt_Class $class
*/
private function checkMethodExistence($methodName, PHPParser_Node_Stmt_Class $class)
private function assertMethodExistence($methodName, PHPParser_Node_Stmt_Class $class)
{

$members = $class->stmts;

$this->assertCount(
Expand All @@ -78,6 +79,62 @@ function (PHPParser_Node $node) use ($methodName) {
);
}

/**
* Verifies that the given properties and only the given properties are added to the hydrator logic
*
* @param PHPParser_Node_Stmt_Class $class
* @param array $properties
*/
private function assertContainsPropertyAccessors(PHPParser_Node_Stmt_Class $class, array $properties)
{
$lookupProperties = array_flip($properties);

foreach ($class->stmts as $method) {
if ($method instanceof \PHPParser_Node_Stmt_ClassMethod && $method->name === 'hydrate') {
foreach ($method->stmts as $assignment) {
if ($assignment instanceof \PHPParser_Node_Expr_Assign) {
$var = $assignment->var;

if ($var instanceof \PHPParser_Node_Expr_PropertyFetch && is_string($var->name)) {
if (! isset($lookupProperties[$var->name])) {
$this->fail(sprintf('Property "%s" should not be hydrated', $var->name));
}

unset($lookupProperties[$var->name]);
}
}
}
}

if ($method instanceof \PHPParser_Node_Stmt_ClassMethod && $method->name === '__construct') {
foreach ($method->stmts as $assignment) {
if ($assignment instanceof \PHPParser_Node_Expr_Assign) {
$var = $assignment->var;

if ($var instanceof \PHPParser_Node_Expr_PropertyFetch
&& preg_match('/(.*)Writer[a-zA-Z0-9]+/', $assignment->var->name, $matches)
) {
if (! isset($lookupProperties[$matches[1]])) {
$this->fail(sprintf('Property "%s" should not be hydrated', $matches[1]));
}

unset($lookupProperties[$matches[1]]);
}
}
}
}
}

if (empty($lookupProperties)) {
return;
}

$this->fail(sprintf(
'Could not match following properties in the generated code: %s',
var_export(array_flip($lookupProperties), true)
));
}

/**
* @return \PHPParser_Node[][]
*/
Expand All @@ -86,13 +143,21 @@ public function classAstProvider()
$parser = new PHPParser_Parser(new PHPParser_Lexer());

$className = UniqueIdentifierGenerator::getIdentifier('Foo');
$classCode = 'class ' . $className . ' { private $bar; private $baz; protected $tab;'
$classCode = 'class ' . $className . ' { private $bar; private $baz; protected $tab; '
. 'protected $tar; public $taw; public $tam; }';

eval($classCode);

return array(
array($className, $parser->parse('<?php ' . $classCode)[0]),
);
$staticClassName = UniqueIdentifierGenerator::getIdentifier('Foo');
$staticClassCode = 'class ' . $staticClassName . ' { private static $bar; '
. 'protected static $baz; public static $tab; private $taz; }';

eval($staticClassCode);


return [
[$className, $parser->parse('<?php ' . $classCode)[0], ['bar', 'baz', 'tab', 'tar', 'taw', 'tam']],
[$staticClassName, $parser->parse('<?php ' . $staticClassCode)[0], ['taz']],
];
}
}
10 changes: 10 additions & 0 deletions tests/GeneratedHydratorTest/Functional/HydratorFunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use GeneratedHydratorTestAsset\ClassWithPrivateProperties;
use GeneratedHydratorTestAsset\ClassWithProtectedProperties;
use GeneratedHydratorTestAsset\ClassWithPublicProperties;
use GeneratedHydratorTestAsset\ClassWithStaticProperties;
use GeneratedHydratorTestAsset\EmptyClass;
use GeneratedHydratorTestAsset\HydratedObject;
use PHPUnit_Framework_TestCase;
Expand Down Expand Up @@ -55,6 +56,10 @@ public function testHydrator($instance)
$newData = array();

foreach ($properties as $property) {
if ($property->isStatic()) {
continue;
}

$propertyName = $property->getName();

$property->setAccessible(true);
Expand All @@ -70,6 +75,10 @@ public function testHydrator($instance)
$inspectionData = array();

foreach ($properties as $property) {
if ($property->isStatic()) {
continue;
}

$propertyName = $property->getName();

$property->setAccessible(true);
Expand Down Expand Up @@ -104,6 +113,7 @@ public function getHydratorClasses()
array(new ClassWithProtectedProperties()),
array(new ClassWithPrivateProperties()),
array(new ClassWithMixedProperties()),
array(new ClassWithStaticProperties()),
);
}

Expand Down
70 changes: 70 additions & 0 deletions tests/GeneratedHydratorTestAsset/ClassWithStaticProperties.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?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 GeneratedHydratorTestAsset;

/**
* Base test class used to verify that generated hydrator ignores static properties
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class ClassWithStaticProperties
{
/**
* @var mixed
*/
private static $privateStatic;

/**
* @var mixed
*/
protected static $protectedStatic;

/**
* @var mixed
*/
public static $publicStatic;

/**
* @var mixed
*/
private $private;

/**
* @var mixed
*/
private $protected;

/**
* @var mixed
*/
private $public;

public function getStaticProperties()
{
return [
'privateStatic' => self::$privateStatic,
'protectedStatic' => self::$protectedStatic,
'publicStatic' => self::$publicStatic,
'private' => $this->private,
'protected' => $this->protected,
'public' => $this->public,
];
}
}

0 comments on commit 2ffe88c

Please sign in to comment.