Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Commit

Permalink
Merge branch 'hotfix/zendframework/zendframework#6869-stdlib-hydrator…
Browse files Browse the repository at this point in the history
…-should-only-hydrate-public-properties' into develop

Close zendframework/zendframework#6869
Forward port zendframework/zendframework#6869
  • Loading branch information
Ocramius committed Nov 29, 2014
3 parents 77e1418 + b67c240 + e5f9a91 commit c858ecd
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 10 deletions.
53 changes: 43 additions & 10 deletions src/Hydrator/ObjectProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@
namespace Zend\Stdlib\Hydrator;

use Zend\Stdlib\Exception;
use ReflectionClass;
use ReflectionProperty;

class ObjectProperty extends AbstractHydrator
{
/**
* Extract values from an object
* @var array[] indexed by class name and then property name
*/
private static $skippedPropertiesCache = array();

/**
* {@inheritDoc}
*
* Extracts the accessible non-static properties of the given $object.
*
* @param object $object
* @return array
* @throws Exception\BadMethodCallException for a non-object $object
*/
public function extract($object)
Expand All @@ -30,48 +35,76 @@ public function extract($object)
));
}

$data = get_object_vars($object);

$data = get_object_vars($object);
$filter = $this->getFilter();

foreach ($data as $name => $value) {
// Filter keys, removing any we don't want
if (!$filter->filter($name)) {
if (! $filter->filter($name)) {
unset($data[$name]);
continue;
}

// Replace name if extracted differ
$extracted = $this->extractName($name, $object);

if ($extracted !== $name) {
unset($data[$name]);
$name = $extracted;
}

$data[$name] = $this->extractValue($name, $value, $object);
}

return $data;
}

/**
* {@inheritDoc}
*
* Hydrate an object by populating public properties
*
* Hydrates an object by setting public properties of the object.
*
* @param array $data
* @param object $object
* @return object
* @throws Exception\BadMethodCallException for a non-object $object
*/
public function hydrate(array $data, $object)
{
if (!is_object($object)) {
if (! is_object($object)) {
throw new Exception\BadMethodCallException(sprintf(
'%s expects the provided $object to be a PHP object)', __METHOD__
));
}

$properties = & self::$skippedPropertiesCache[get_class($object)];

if (! isset($properties)) {
$reflection = new ReflectionClass($object);
$properties = array_fill_keys(
array_map(
function (ReflectionProperty $property) {
return $property->getName();
},
$reflection->getProperties(
ReflectionProperty::IS_PRIVATE
+ ReflectionProperty::IS_PROTECTED
+ ReflectionProperty::IS_STATIC
)
),
true
);
}

foreach ($data as $name => $value) {
$property = $this->hydrateName($name, $data);

if (isset($properties[$property])) {
continue;
}

$object->$property = $this->hydrateValue($property, $value, $data);
}

return $object;
}
}
166 changes: 166 additions & 0 deletions test/Hydrator/ObjectPropertyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\Stdlib\Hydrator;

use Zend\Stdlib\Hydrator\ObjectProperty;
use Zend\Stdlib\Exception\BadMethodCallException;
use ZendTest\Stdlib\TestAsset\ClassWithPublicStaticProperties;
use ZendTest\Stdlib\TestAsset\ObjectProperty as ObjectPropertyTestAsset;

/**
* Unit tests for {@see \Zend\Stdlib\Hydrator\ObjectProperty}
*
* @covers \Zend\Stdlib\Hydrator\ObjectProperty
* @group Zend_Stdlib
*/
class ObjectPropertyTest extends \PHPUnit_Framework_TestCase
{
/**
* @var ObjectProperty
*/
private $hydrator;

/**
* {@inheritDoc}
*/
protected function setUp()
{
$this->hydrator = new ObjectProperty();
}

/**
* Verify that we get an exception when trying to extract on a non-object
*/
public function testHydratorExtractThrowsExceptionOnNonObjectParameter()
{
$this->setExpectedException('BadMethodCallException');
$this->hydrator->extract('thisIsNotAnObject');
}

/**
* Verify that we get an exception when trying to hydrate a non-object
*/
public function testHydratorHydrateThrowsExceptionOnNonObjectParameter()
{
$this->setExpectedException('BadMethodCallException');
$this->hydrator->hydrate(array('some' => 'data'), 'thisIsNotAnObject');
}

/**
* Verifies that the hydrator can extract from property of stdClass objects
*/
public function testCanExtractFromStdClass()
{
$object = new \stdClass();
$object->foo = 'bar';

$this->assertSame(array('foo' => 'bar'), $this->hydrator->extract($object));
}

/**
* Verifies that the extraction process works on classes that aren't stdClass
*/
public function testCanExtractFromGenericClass()
{
$this->assertSame(
array(
'foo' => 'bar',
'bar' => 'foo',
'blubb' => 'baz',
'quo' => 'blubb'
),
$this->hydrator->extract(new ObjectPropertyTestAsset())
);
}

/**
* Verify hydration of {@see \stdClass}
*/
public function testCanHydrateStdClass()
{
$object = new \stdClass();
$object->foo = 'bar';

$object = $this->hydrator->hydrate(array('foo' => 'baz'), $object);

$this->assertEquals('baz', $object->foo);
}

/**
* Verify that new properties are created if the object is stdClass
*/
public function testCanHydrateAdditionalPropertiesToStdClass()
{
$object = new \stdClass();
$object->foo = 'bar';

$object = $this->hydrator->hydrate(array('foo' => 'baz', 'bar' => 'baz'), $object);

$this->assertEquals('baz', $object->foo);
$this->assertObjectHasAttribute('bar', $object);
$this->assertAttributeSame('baz', 'bar', $object);
}

/**
* Verify that it can hydrate our class public properties
*/
public function testCanHydrateGenericClassPublicProperties()
{
$object = $this->hydrator->hydrate(
array(
'foo' => 'foo',
'bar' => 'bar',
'blubb' => 'blubb',
'quo' => 'quo',
'quin' => 'quin'
),
new ObjectPropertyTestAsset()
);

$this->assertAttributeSame('foo', 'foo', $object);
$this->assertAttributeSame('bar', 'bar', $object);
$this->assertAttributeSame('blubb', 'blubb', $object);
$this->assertAttributeSame('quo', 'quo', $object);
$this->assertAttributeNotSame('quin', 'quin', $object);
}

/**
* Verify that it can hydrate new properties on generic classes
*/
public function testCanHydrateGenericClassNonExistingProperties()
{
$object = $this->hydrator->hydrate(array('newProperty' => 'newPropertyValue'), new ObjectPropertyTestAsset());

$this->assertAttributeSame('newPropertyValue', 'newProperty', $object);
}

/**
* Verify that hydration is skipped for class properties (it is an object hydrator after all)
*/
public function testSkipsPublicStaticClassPropertiesHydration()
{
$this->hydrator->hydrate(
array('foo' => '1', 'bar' => '2', 'baz' => '3'),
new ClassWithPublicStaticProperties()
);

$this->assertSame('foo', ClassWithPublicStaticProperties::$foo);
$this->assertSame('bar', ClassWithPublicStaticProperties::$bar);
$this->assertSame('baz', ClassWithPublicStaticProperties::$baz);
}

/**
* Verify that extraction is skipped for class properties (it is an object hydrator after all)
*/
public function testSkipsPublicStaticClassPropertiesExtraction()
{
$this->assertEmpty($this->hydrator->extract(new ClassWithPublicStaticProperties()));
}
}
28 changes: 28 additions & 0 deletions test/TestAsset/ClassWithPublicStaticProperties.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\Stdlib\TestAsset;

class ClassWithPublicStaticProperties
{
/**
* @var string
*/
public static $foo = 'foo';

/**
* @var string
*/
public static $bar = 'bar';

/**
* @var string
*/
public static $baz = 'baz';
}
2 changes: 2 additions & 0 deletions test/TestAsset/ObjectProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ class ObjectProperty
public $bar = null;
public $blubb = null;
public $quo = null;
protected $quin = null;

public function __construct()
{
$this->foo = "bar";
$this->bar = "foo";
$this->blubb = "baz";
$this->quo = "blubb";
$this->quin = 'five';
}

}

0 comments on commit c858ecd

Please sign in to comment.