Skip to content

Commit 77fc472

Browse files
authored
Merge pull request #16 from contentful/feat/object-hydrator
Extracted object hydration into a standalone class
2 parents e431301 + 07a3549 commit 77fc472

File tree

5 files changed

+214
-63
lines changed

5 files changed

+214
-63
lines changed

.travis.yml

Lines changed: 45 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,85 +2,68 @@ language: php
22

33
env:
44
global:
5-
- GH_REPO="github.com/${TRAVIS_REPO_SLUG}.git"
6-
- BUILD_LEAD=0
7-
- SYNTAX_TEST=0
5+
- GH_REPO="github.com/${TRAVIS_REPO_SLUG}.git"
6+
- BUILD_LEAD=0
7+
- QUALITY_CHECK=0
88

99
matrix:
1010
fast_finish: true
1111
include:
12-
- php: 7.1
13-
env: SYNTAX_TEST=1
14-
- php: 5.6
15-
- php: 7.0
16-
- php: 7.1
17-
env: BUILD_LEAD=1
18-
- php: 7.2
12+
- php: 7.2
13+
env: QUALITY_CHECK=1
14+
- php: 5.6
15+
- php: 7.0
16+
- php: 7.1
17+
- php: 7.2
18+
env: BUILD_LEAD=1
1919

2020
branches:
2121
only:
22-
- master
22+
- master
2323

2424
before_install:
25-
- |
26-
# Disable XDebug
27-
#
28-
# XDebug makes PHPUnit and PHP-CS-Fixer slow.
29-
# We need it only for generating the code coverage report,
30-
# so we disable it everywhere but for the build lead.
31-
if [ $BUILD_LEAD == 0 ]
32-
then
33-
phpenv config-rm xdebug.ini
34-
fi
25+
# Disable XDebug
26+
- |
27+
if [ $BUILD_LEAD == 0 ]; then
28+
phpenv config-rm xdebug.ini
29+
fi
3530
3631
install:
37-
- |
38-
# Install dependencies
39-
# We don't need to install dependencies when doing the syntax check.
40-
if [ $SYNTAX_TEST == 0 ]
41-
then
42-
composer install
43-
fi
32+
- composer install
4433

4534
script:
46-
- |
47-
# PHPUnit tests
48-
if [ $SYNTAX_TEST == 0 ]
49-
then
50-
php vendor/bin/phpunit --stop-on-error --stop-on-failure -v
51-
fi
35+
- |
36+
# PHPUnit tests
37+
if [ $QUALITY_CHECK == 0 ]; then
38+
php vendor/bin/phpunit --stop-on-error --stop-on-failure -v
39+
fi
5240
53-
- |
54-
# Syntax tests
55-
#
56-
# We use PHP-CS-Fixer to check for syntax and style errors,
57-
# but we run a separate build for that to take advantage of build parallelism.
58-
if [ $SYNTAX_TEST == 1 ]
59-
then
60-
wget http://cs.sensiolabs.org/download/php-cs-fixer-v2.phar -q -O php-cs-fixer.phar
61-
php php-cs-fixer.phar fix --dry-run --stop-on-violation -v
62-
fi
41+
- |
42+
# Static analysis
43+
if [ $QUALITY_CHECK == 1 ]; then
44+
wget https://github.com/phpstan/phpstan/releases/download/0.9.2/phpstan.phar -q -O phpstan.phar
45+
php phpstan.phar analyse --level=max src/
46+
fi
6347
48+
- |
49+
# Syntax check
50+
if [ $QUALITY_CHECK == 1 ]; then
51+
wget http://cs.sensiolabs.org/download/php-cs-fixer-v2.phar -q -O php-cs-fixer.phar
52+
php php-cs-fixer.phar fix --dry-run --stop-on-violation -v
53+
fi
6454
after_success:
65-
- |
66-
# Upload code coverage
67-
#
68-
# The code coverage report is only generated when BUILD_LEAD is 1.
69-
if [ $BUILD_LEAD == 1 ]
70-
then
71-
travis_retry bash <(curl -s https://codecov.io/bash) -f build/logs/clover.xml
72-
fi
55+
# Upload code coverage
56+
- |
57+
if [ $BUILD_LEAD == 1 ]; then
58+
travis_retry bash <(curl -s https://codecov.io/bash) -f build/logs/clover.xml
59+
fi
7360
74-
- |
75-
# Generate API docs
76-
#
77-
# We generate API docs only for the build lead (to avoid wasting time on all builds),
78-
# and when on merges to master, which is the result of this condition and the setting
79-
# branches: only: master above.
80-
if [[ ($TRAVIS_PULL_REQUEST == false || $TRAVIS_TAG != '') && $BUILD_LEAD == 1 ]]
81-
then
82-
sh ./scripts/prepare-docs.sh
83-
fi
61+
# Generate API docs
62+
- |
63+
if [[ ($TRAVIS_PULL_REQUEST == false || $TRAVIS_TAG != '') && $BUILD_LEAD == 1 ]]
64+
then
65+
sh ./scripts/prepare-docs.sh
66+
fi
8467
8568
deploy:
8669
provider: pages

src/Api/BaseClient.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ protected function request($method, $path, array $options = [])
155155
: \null;
156156

157157
return $body
158-
? guzzle_json_decode((string) $body, \true)
158+
? guzzle_json_decode($body, \true)
159159
: [];
160160
}
161161

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the contentful/contentful-core package.
5+
*
6+
* @copyright 2015-2018 Contentful GmbH
7+
* @license MIT
8+
*/
9+
10+
namespace Contentful\Core\ResourceBuilder;
11+
12+
/**
13+
* Class ObjectHydrator.
14+
*
15+
* Utility class for handling updating private or protected properties of an object.
16+
*/
17+
class ObjectHydrator
18+
{
19+
/**
20+
* @var \Closure[]
21+
*/
22+
private $hydrators = [];
23+
24+
/**
25+
* If given a class name as target, the hydrator will create an instance of that class,
26+
* but skipping the constructor. The hydrator will then update the internal properties,
27+
* according to the keys defined in the $data parameter.
28+
*
29+
* @param string|object $target
30+
* @param array $data
31+
*
32+
* @return object
33+
*/
34+
public function hydrate($target, array $data)
35+
{
36+
$class = \is_object($target) ? \get_class($target) : $target;
37+
if (\is_string($target)) {
38+
$target = (new \ReflectionClass($class))
39+
->newInstanceWithoutConstructor()
40+
;
41+
}
42+
43+
$hydrator = $this->getHydrator($class);
44+
$hydrator($target, $data);
45+
46+
return $target;
47+
}
48+
49+
/**
50+
* @param string $class
51+
*
52+
* @return \Closure
53+
*/
54+
private function getHydrator($class)
55+
{
56+
if (isset($this->hydrators[$class])) {
57+
return $this->hydrators[$class];
58+
}
59+
60+
return $this->hydrators[$class] = \Closure::bind(function ($object, $properties) {
61+
foreach ($properties as $property => $value) {
62+
$object->$property = $value;
63+
}
64+
}, \null, $class);
65+
}
66+
}

tests/Unit/Api/BaseClientTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public function testClient()
5656

5757
$this->assertSame('DELIVERY', $client->getApi());
5858
$this->assertSame('https://cdn.contentful.com', $client->getHost());
59+
$this->assertSame($logger, $client->getLogger());
5960

6061
$jsonResponse = $client->request('GET', '/spaces/cfexampleapi');
6162

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the contentful/contentful-core package.
5+
*
6+
* @copyright 2015-2018 Contentful GmbH
7+
* @license MIT
8+
*/
9+
10+
namespace Contentful\Tests\Core\Unit\ResourceBuilder;
11+
12+
use Contentful\Core\ResourceBuilder\ObjectHydrator;
13+
use Contentful\Tests\Core\TestCase;
14+
15+
class ObjectHydratorTest extends TestCase
16+
{
17+
public function testHydration()
18+
{
19+
$hydrator = new ObjectHydrator();
20+
21+
/** @var TestPerson $person */
22+
$person = $hydrator->hydrate(TestPerson::class, [
23+
'name' => 'Kanji Tatsumi',
24+
'age' => 15,
25+
]);
26+
27+
$this->assertSame('Kanji Tatsumi', $person->getName());
28+
$this->assertSame(15, $person->getAge());
29+
30+
/** @var TestPerson $person */
31+
$person = $hydrator->hydrate(TestPerson::class, [
32+
'name' => 'Makoto Niijima',
33+
'age' => 17,
34+
]);
35+
36+
$this->assertSame('Makoto Niijima', $person->getName());
37+
$this->assertSame(17, $person->getAge());
38+
39+
$videogame = new TestVideogame();
40+
$this->assertNull($videogame->getTitle());
41+
$this->assertNull($videogame->getConsole());
42+
43+
$hydrator->hydrate($videogame, [
44+
'title' => 'Persona 5',
45+
'console' => 'PS4',
46+
]);
47+
48+
$this->assertSame('Persona 5', $videogame->getTitle());
49+
$this->assertSame('PS4', $videogame->getConsole());
50+
51+
$reflectionObject = new \ReflectionObject($hydrator);
52+
$property = $reflectionObject->getProperty('hydrators');
53+
$property->setAccessible(\true);
54+
$hydrators = $property->getValue($hydrator);
55+
56+
$this->assertCount(2, $hydrators);
57+
$this->assertArrayHasKey(TestPerson::class, $hydrators);
58+
$this->assertArrayHasKey(TestVideogame::class, $hydrators);
59+
60+
$this->assertInstanceOf(\Closure::class, $hydrators[TestPerson::class]);
61+
$this->assertInstanceOf(\Closure::class, $hydrators[TestVideogame::class]);
62+
}
63+
}
64+
65+
class TestPerson
66+
{
67+
private $name;
68+
69+
private $age;
70+
71+
private function __construct()
72+
{
73+
}
74+
75+
public function getName()
76+
{
77+
return $this->name;
78+
}
79+
80+
public function getAge()
81+
{
82+
return $this->age;
83+
}
84+
}
85+
86+
class TestVideogame
87+
{
88+
private $title;
89+
90+
private $console;
91+
92+
public function getTitle()
93+
{
94+
return $this->title;
95+
}
96+
97+
public function getConsole()
98+
{
99+
return $this->console;
100+
}
101+
}

0 commit comments

Comments
 (0)