Skip to content

Commit 12eaeca

Browse files
authored
Merge pull request #122 from moufmouf/weakref-php7.4
Adding support for native WeakReferences
2 parents 9f9dac2 + 415d7da commit 12eaeca

8 files changed

+137
-11
lines changed

.travis.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ cache:
66
- $HOME/.composer/cache
77
matrix:
88
include:
9+
- php: 7.4
10+
env: PREFER_LOWEST="" DB=mysql RUN_PHPSTAN=1 RUN_CSCHECK=1 RUN_REQUIRECHECKER=1 NO_WEAKREF=1
11+
services:
12+
- mysql
913
- php: 7.2
10-
env: PREFER_LOWEST="" DB=mysql RUN_PHPSTAN=1 RUN_CSCHECK=1 RUN_REQUIRECHECKER=1
14+
env: PREFER_LOWEST="" DB=mysql
1115
services:
1216
- mysql
1317
- php: 7.3
@@ -57,7 +61,10 @@ matrix:
5761
postgresql: "9.6"
5862
services:
5963
- postgresql
64+
- php: 7.3
65+
env: PREFER_LOWEST="" DB=mysql NO_WEAKREF=1
6066
allow_failures:
67+
- php: 7.3 # 7.3 allowed to fail due to an issue with greenlion/PHP-SQL-Parser: https://github.com/greenlion/PHP-SQL-Parser/pull/304
6168
- php: 7.1
6269
env: PREFER_LOWEST="" DB=oracle PHPUNITFILE="-c phpunit.oracle.xml"
6370
- php: 7.1

composer-require-checker.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"null", "true", "false",
44
"static", "self", "parent",
55
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object",
6-
"WeakRef",
6+
"WeakRef", "WeakReference",
77
"TheCodingMachine\\TDBM\\Fixtures\\Interfaces\\TestUserDaoInterface",
88
"TheCodingMachine\\TDBM\\Fixtures\\Traits\\TestUserDaoTrait",
99
"TheCodingMachine\\TDBM\\Fixtures\\Interfaces\\TestUserInterface",

phpstan.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ parameters:
1111
- "#expects array<string>, array<int, int|string> given.#"
1212
- "/Parameter #. \\$types of method Doctrine\\\\DBAL\\\\Connection::.*() expects array<int|string>, array<int, Doctrine\\\\DBAL\\\\Types\\\\Type> given./"
1313
- "#Method TheCodingMachine\\\\TDBM\\\\Schema\\\\ForeignKey::.*() should return .* but returns array<string>|string.#"
14+
- '#Method TheCodingMachine\\TDBM\\NativeWeakrefObjectStorage::get\(\) should return TheCodingMachine\\TDBM\\DbRow\|null but returns object\|null.#'
15+
-
16+
message: '#WeakRef#'
17+
path: src/WeakrefObjectStorage.php
1418
-
1519
message: '#Result of && is always false.#'
1620
path: src/Test/Dao/Bean/Generated/ArticleBaseBean.php

src/NativeWeakrefObjectStorage.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/*
5+
Copyright (C) 2006-2014 David Négrier - THE CODING MACHINE
6+
7+
This program is free software; you can redistribute it and/or modify
8+
it under the terms of the GNU General Public License as published by
9+
the Free Software Foundation; either version 2 of the License, or
10+
(at your option) any later version.
11+
12+
This program is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
GNU General Public License for more details.
16+
17+
You should have received a copy of the GNU General Public License
18+
along with this program; if not, write to the Free Software
19+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20+
*/
21+
22+
namespace TheCodingMachine\TDBM;
23+
24+
use WeakReference;
25+
26+
/**
27+
* The NativeWeakrefObjectStorage class is used to reference all beans that have been fetched from the database.
28+
* If a bean is requested twice from TDBM, the NativeWeakrefObjectStorage is used to "cache" the bean.
29+
* Unlike the StandardObjectStorage, the NativeWeakrefObjectStorage manages memory in a clever way, using WeakReference.
30+
* WeakReference have been added in PHP 7.4.
31+
*
32+
* @author David Negrier
33+
*/
34+
class NativeWeakrefObjectStorage implements ObjectStorageInterface
35+
{
36+
/**
37+
* An array of fetched object, accessible via table name and primary key.
38+
* If the primary key is split on several columns, access is done by an array of columns, serialized.
39+
*
40+
* @var WeakReference[][]
41+
*/
42+
private $objects = array();
43+
44+
/**
45+
* Every 10000 set in the dataset, we perform a cleanup to ensure the WeakRef instances
46+
* are removed if they are no more valid.
47+
* This is to avoid having memory used by dangling WeakRef instances.
48+
*
49+
* @var int
50+
*/
51+
private $garbageCollectorCount = 0;
52+
53+
/**
54+
* Sets an object in the storage.
55+
*
56+
* @param string $tableName
57+
* @param string|int $id
58+
* @param DbRow $dbRow
59+
*/
60+
public function set(string $tableName, $id, DbRow $dbRow): void
61+
{
62+
$this->objects[$tableName][$id] = WeakReference::create($dbRow);
63+
++$this->garbageCollectorCount;
64+
if ($this->garbageCollectorCount === 10000) {
65+
$this->garbageCollectorCount = 0;
66+
$this->cleanupDanglingWeakRefs();
67+
}
68+
}
69+
70+
/**
71+
* Returns an object from the storage (or null if no object is set).
72+
*
73+
* @param string $tableName
74+
* @param string|int $id
75+
*
76+
* @return DbRow|null
77+
*/
78+
public function get(string $tableName, $id) : ?DbRow
79+
{
80+
if (isset($this->objects[$tableName][$id])) {
81+
return $this->objects[$tableName][$id]->get();
82+
}
83+
return null;
84+
}
85+
86+
/**
87+
* Removes an object from the storage.
88+
*
89+
* @param string $tableName
90+
* @param string|int $id
91+
*/
92+
public function remove(string $tableName, $id): void
93+
{
94+
unset($this->objects[$tableName][$id]);
95+
}
96+
97+
private function cleanupDanglingWeakRefs(): void
98+
{
99+
foreach ($this->objects as $tableName => $table) {
100+
foreach ($table as $id => $obj) {
101+
if ($obj->get() === null) {
102+
unset($this->objects[$tableName][$id]);
103+
}
104+
}
105+
}
106+
}
107+
}

src/TDBMService.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
namespace TheCodingMachine\TDBM;
2323

24+
use function class_exists;
2425
use Doctrine\Common\Cache\Cache;
2526
use Doctrine\Common\Cache\ClearableCache;
2627
use Doctrine\Common\Cache\VoidCache;
@@ -46,7 +47,7 @@
4647
use Psr\Log\LoggerInterface;
4748
use Psr\Log\LogLevel;
4849
use Psr\Log\NullLogger;
49-
use function var_export;
50+
use WeakReference;
5051

5152
/**
5253
* The TDBMService class is the main TDBM class. It provides methods to retrieve TDBMObject instances
@@ -101,7 +102,7 @@ class TDBMService
101102
* Access is done by table name and then by primary key.
102103
* If the primary key is split on several columns, access is done by an array of columns, serialized.
103104
*
104-
* @var StandardObjectStorage|WeakrefObjectStorage
105+
* @var ObjectStorageInterface
105106
*/
106107
private $objectStorage;
107108

@@ -179,7 +180,9 @@ class TDBMService
179180
*/
180181
public function __construct(ConfigurationInterface $configuration)
181182
{
182-
if (extension_loaded('weakref')) {
183+
if (class_exists(WeakReference::class)) {
184+
$this->objectStorage = new NativeWeakrefObjectStorage();
185+
} elseif (extension_loaded('weakref')) {
183186
$this->objectStorage = new WeakrefObjectStorage();
184187
} else {
185188
$this->objectStorage = new StandardObjectStorage();

src/WeakrefObjectStorage.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
* If a bean is requested twice from TDBM, the WeakrefObjectStorage is used to "cache" the bean.
2727
* Unlike the StandardObjectStorage, the WeakrefObjectStorage manages memory in a clever way, using the weakref
2828
* PHP extension. It is used if the "weakref" extension is available.
29-
* Otherwise, the StandardObjectStorage is used instead.
29+
* Otherwise, the StandardObjectStorage or NativeWeakrefObjectStorage (in PHP 7.4+) is used instead.
30+
*
31+
* Note: the weakref extension is available until PHP 7.2, but not available in PHP 7.3+
3032
*
3133
* @author David Negrier
3234
*/

tests/Commands/GenerateCommandTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function testCall(): void
2626

2727
$result = $this->callCommand(new GenerateCommand($this->getConfiguration()), $input);
2828

29-
$this->assertStringContainsString('Finished regenerating DAOs and beans', $result);
29+
$this->assertContains('Finished regenerating DAOs and beans', $result);
3030
}
3131

3232
/**

tests/TDBMDaoGeneratorTest.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use Ramsey\Uuid\Uuid;
3131
use ReflectionClass;
3232
use ReflectionMethod;
33+
use ReflectionNamedType;
3334
use TheCodingMachine\TDBM\Dao\TestAlbumDao;
3435
use TheCodingMachine\TDBM\Dao\TestArticleDao;
3536
use TheCodingMachine\TDBM\Dao\TestCountryDao;
@@ -88,6 +89,7 @@
8889
use TheCodingMachine\TDBM\Utils\PathFinder\PathFinder;
8990
use TheCodingMachine\TDBM\Utils\TDBMDaoGenerator;
9091
use Symfony\Component\Process\Process;
92+
use function gettype;
9193

9294
class TDBMDaoGeneratorTest extends TDBMAbstractServiceTest
9395
{
@@ -1611,9 +1613,10 @@ public function testUuidv4(): void
16111613
public function testTypeHintedConstructors(): void
16121614
{
16131615
$userBaseBeanReflectionConstructor = new \ReflectionMethod(UserBaseBean::class, '__construct');
1616+
/** @var ReflectionNamedType $nameParam */
16141617
$nameParam = $userBaseBeanReflectionConstructor->getParameters()[0];
16151618

1616-
$this->assertSame('string', (string)$nameParam->getType());
1619+
$this->assertSame('string', $nameParam->getType()->getName());
16171620
}
16181621

16191622
/**
@@ -1725,7 +1728,7 @@ public function testBlob(): void
17251728
$resource = $loadedFile->getFile();
17261729
$result = fseek($resource, 0);
17271730
$this->assertSame(0, $result);
1728-
$this->assertIsResource($resource);
1731+
$this->assertSame('resource', gettype($resource));
17291732
$firstLine = fgets($resource);
17301733
$this->assertSame("<?php\n", $firstLine);
17311734
}
@@ -1739,7 +1742,7 @@ public function testReadBlob(): void
17391742
$loadedFile = $fileDao->getById(1);
17401743

17411744
$resource = $loadedFile->getFile();
1742-
$this->assertIsResource($resource);
1745+
$this->assertSame('resource', gettype($resource));
17431746
$firstLine = fgets($resource);
17441747
$this->assertSame("<?php\n", $firstLine);
17451748

@@ -1826,7 +1829,7 @@ public function testFilterBag(): void
18261829
public function testDecimalIsMappedToString(): void
18271830
{
18281831
$reflectionClass = new \ReflectionClass(BoatBaseBean::class);
1829-
$this->assertSame('string', (string) $reflectionClass->getMethod('getLength')->getReturnType());
1832+
$this->assertSame('string', $reflectionClass->getMethod('getLength')->getReturnType()->getName());
18301833
}
18311834

18321835
/**

0 commit comments

Comments
 (0)