friendsofphp / proxy-manager-lts Goto Github PK
View Code? Open in Web Editor NEWAdding support for a wider range of PHP versions to ocramius/proxy-manager.
Home Page: https://github.com/Ocramius/ProxyManager
License: MIT License
Adding support for a wider range of PHP versions to ocramius/proxy-manager.
Home Page: https://github.com/Ocramius/ProxyManager
License: MIT License
Hello,
as doctrine/reflection say itself that it is abandoned:
⚠️ This package is abandoned, consider migrating to alternatives, such as roave/better-reflection. Features that are still useful to Doctrine have been moved to doctrine/persistence⚠️
Maybe you should either abandon this project or edit the readme to no longer act as a fork and thus not asking to "report issues and submit new features to the origin library."
:)
fixExport()
introduced in #17 does not work correctly on PHP 8.2, where var_export()
behaviour was changed:
1) ProxyManagerTest\Functional\MultipleProxyGenerationTest::testCanGenerateMultipleDifferentProxiesForSameClass with data set "php81defaults" (ProxyManagerTestAsset\ClassWithPhp81Defaults Object (...))
Error: Class "P\roxyManagerTestAsset\FooEnum" not found
proxy-manager-lts/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php:103
2) ProxyManagerTest\Functional\RemoteObjectFunctionalTest::testXmlRpcMethodCalls with data set "when using php8.1 defaults" ('ProxyManagerTestAsset\RemoteP...erface', 'php81Defaults', array(), array(ProxyManagerTestAsset\RemoteProxy\FooEnum Object (...), stdClass Object ()), 200)
Error: Class "P\roxyManagerTestAsset\RemoteProxy\FooEnum" not found
proxy-manager-lts/tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php:107
3) ProxyManagerTest\Functional\RemoteObjectFunctionalTest::testJsonRpcMethodCalls with data set "when using php8.1 defaults" ('ProxyManagerTestAsset\RemoteP...erface', 'php81Defaults', array(), array(ProxyManagerTestAsset\RemoteProxy\FooEnum Object (...), stdClass Object ()), 200)
Error: Class "P\roxyManagerTestAsset\RemoteProxy\FooEnum" not found
proxy-manager-lts/tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php:131
As you can see, it produces \P\roxyManagerTestAsset\FooEnum
instead of \ProxyManagerTestAsset\FooEnum
.
Ref: php/php-src#8232
Ref: php/php-src#8233
I have an issue when trying to read public readonly properties from the Proxy. I got an error:
Cannot initialize readonly property App\Entity\Customer::$firstName from scope App\Command\Command
Library version: 1.0.12
Preconditions:
We have a class only with public properties.
<?php
declare(strict_types=1);
namespace App\Entity;
class Customer
{
public function __construct(
public readonly string $firstName,
public readonly string $lastName,
) {
}
}
Steps to Reproduce:
$instance = (new LazyLoadingGhostFactory())->createProxy(
Customer::class,
static function (
GhostObjectInterface $ghostObject,
string $method,
array $parameters,
?Closure &$initializer,
array $properties,
) {
$initializer = null;
$properties['firstName'] = 'User';
$properties['lastName'] = 'Test';
return true;
},
);
Cannot initialize readonly property App\Entity\Customer::$firstName from scope App\Command\Command
$output->writeln('First Name: ' . $customer->firstName);
$output->writeln('Last Name: ' . $customer->lastName);
After investigation, I see the $class
and $scopeObject
variables equal to the App\Command\Command
.
} elseif (isset(self::$privateProperties98259[$name])) {
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
...
}
...
$targetObject = $realInstanceReflection->newInstanceWithoutConstructor();
$accessor = function & () use ($targetObject, $name) {
return $targetObject->$name;
};
$backtrace = debug_backtrace(true, 2);
$scopeObject = isset($backtrace[1]['object']) ? $backtrace[1]['object'] : new \ProxyManager\Stub\EmptyClassStub();
Note
But if we will add a getter for the property all works without errors.
class Customer
{
public function __construct(
public readonly string $firstName,
public readonly string $lastName,
) {
}
public function getFirstName(): string
{
return $this->firstName;
}
}
$output->writeln('First Name: ' . $customer->getFirstName());
$output->writeln('Last Name: ' . $customer->lastName);
In its current state, this library does not work fully with PHP 8.1. When using a promoted readonly only property in a class, this library will generate code that tries to change the readonly property and therefor breaking execution. Consider the following class (all have been stripped to only show the constructor):
class SystemNotificationService extends AbstractCacheableService
{
public function __construct(
private readonly SystemNotificationRepository $systemNotificationRepository,
private readonly SerializerInterface $serializer,
Features $features,
string $kernelEnvironment,
string $commitHash,
string $httpHost,
string $cacheDir)
{
parent::__construct($features, $kernelEnvironment, $commitHash, $httpHost, $cacheDir);
}
}
abstract class AbstractCacheableService
{
public function __construct(
protected readonly Features $features,
string $kernelEnvironment,
string $commitHash,
string $httpHost,
private readonly string $cacheDir)
{
}
}
will generate the following proxy:
class SystemNotificationService_6ddbdeb extends \App\Service\SystemNotificationService implements \ProxyManager\Proxy\VirtualProxyInterface
{
public function __construct(private \JMS\Serializer\SerializerInterface $serializer, \App\Features $features)
{
static $reflection;
if (! $this->valueHolder06ae0) {
$reflection = $reflection ?? new \ReflectionClass('App\\Service\\SystemNotificationService');
$this->valueHolder06ae0 = $reflection->newInstanceWithoutConstructor();
unset($this->features);
\Closure::bind(function (\App\Service\SystemNotificationService $instance) {
unset($instance->systemNotificationRepository, $instance->serializer);
}, $this, 'App\\Service\\SystemNotificationService')->__invoke($this);
\Closure::bind(function (\App\Service\AbstractCacheableService $instance) {
unset($instance->cache, $instance->cacheHash, $instance->clearedTags, $instance->clearedKeys, $instance->cacheDir);
}, $this, 'App\\Service\\AbstractCacheableService')->__invoke($this);
}
$this->valueHolder06ae0->__construct($systemNotificationRepository, $serializer, $features, $kernelEnvironment, $commitHash, $httpHost, $cacheDir);
}
}
and thus throw Cannot unset readonly property App\Service\AbstractCacheableService::$features from scope ContainerXH6sJpc\SystemNotificationService_6ddbdeb
, caused by the unset($instance->features)
.
Note that the same will also happen with the systemNotificationRepository
and serializer
properties (unset($instance->systemNotificationRepository, $instance->serializer);
).
Also posted at Ocramius/ProxyManager#737.
Its not bug, but maybe someone will need this tip:
Affected in Sylius:
Update from ocramius/proxy-manager
to friendsofphp/proxy-manager-lts
causes error with return type on getProxyInitializer() method in src/ProxyManager/Proxy/LazyLoadingInterface.php - tip: clear:cache
is not enough but rm -fr var/cache
fix this problem.
Right now the composer.json has the following requirement:
"laminas/laminas-code": "~3.4.1|^4.0"
This constraint won't allow version 3.5.x
. This has the downside that it is not possible to support 7.3, 7.4. and 8.0 at once. For reference:
laminas-code | require |
---|---|
3.4.* |
php: ^7.1 |
3.5.* |
php: ^7.3 || ~8.0.0 |
4.0.* |
php: ^7.4 || ~8.0.0 |
As far as I can tell, it doesn't make sense to take this upstream as the version support is explicitly why this package exists. Would it make sense to change the requirement in the composer.json to support 3.5 as well?
Seeing the following error on 1.0.17 that I wasn't seeing on 1.0.16.
/vendor/laminas/laminas-code/src/Generator/TypeGenerator/AtomicType.php:217 Type "mixed" cannot be nullable
Hi,
I think the url should be written with https;// instead of http://.
proxy-manager-lts/composer.json
Lines 14 to 19 in b6c4815
There is still code in the library that triggers class_exists(\Zend\CodeGenerator\ClassGenerator)
for an unclear reason, maybe to make the laminas/laminas-zendframework-bridge
happy that is transisently loaded via laminas/laminas-eventmanager
even though it already uses Laminas
use statements everywhere.
This laminas-zendframework-bridge
introduces a heavy autoloader as documented here laminas/laminas-zendframework-bridge#83 and this library should make sure to avoid this trap by lobbying this dependency be removed from laminas/laminas-eventmanager
laminas/laminas-eventmanager#21 and remove the class_exists
calls.
Hi, we tried to enable the latest proxy-manager-lts on php 8.1 but we get this error
https://github.com/doctrine/migrations/runs/3643678725?check_suite_focus=true
Hello everyone,
First of all thank you for the great work on this repository. Long term support for old PHP versions is very precious and happens thanks to your hard work.
At prestashop we are trying provide a wide range of PHP versions for our next version, which would be php7.2 to php8.0 .
Although php versions prior to 7.4 are not maintained anymore, in the CMS ecosystem php versions can be slow to upgrade.
Moreover if someone is running a PrestaShop version on php7.2 today and wants to upgrade to the latest version tomorrow, it's very useful for her/him to be able to 1) upgrade the software ; and it runs on 7.2 ; 2) verify everything seems all right 3) upgrade php version. If we drop php7.2 compatibility, this cannot happen and make the upgrade path harder.
proxy-manager-lts requires laminas-code which provide
So we cannot have a single set of dependencies for proxy-manager-lts that would support both php7.2 and php8.0.
Would it be possible to consider
-tls
package, just like this one, for laminas-code, to aim for the same goal ? that means a new package friendsofphp/laminas-code-lts, we will be happy to contribute to it to achieve this goalHave a nice day :) I'm open to all ideas and feedback
I ran into a problem that took a lot of time, and perhaps it may be solved at the proxy creation level.
My problem was about EntityManager and EntityRepository behavior in Symfony app:
final class ProductRepository implements ProductRepositoryInterface
{
/** @var EntityRepository<Product> $repository */
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Product::class);
}
public function update(string $productId): void
{
$product = $this->repository->find($productId);
$product->update(); // <- change state
$this->entityManager->flush(); // <- do nothing
}
}
Of course, it turned out that there were different UnitOfWork instances in the injected entity manager and stored repository:
$unitOfWork1 = $this->repository->getEntityManager()->getUnitOfWork();
$unitOfWork2 = $this->entityManager->getUnitOfWork();
dump($unitOfWork1 === $unitOfWork2); // <- FALSE
After investigation, I found implicit circular dependency in my container (and resolved it with making one of my services lazy
).
Anyway, I think that it's interesting how can two (proxied) EntityManager instances (with different UnitOfWork instances) exist in the container.
What I found:
$entityManager->getRepository
call$entityManager->getRepository
again\ContainerDjfkKub\EntityManager_9a5be93::getRepository
calls in the stack; Thanks to coalesce $this->services['doctrine.dbal.default_connection'] ?? $this->getDoctrine_Dbal_DefaultConnectionService()
or something else we do not go there third time$wrappedInstance
by ref with newly created EntityManager. Twice. While references to first are already saved in container.My in-place dirty fix for it looks like callable "setter" param which ensures to not assign twice:
// EntityManager_9a5be93
public function getRepository($entityName)
{
$this->initializer8b6cd && ($this->initializer8b6cd->__invoke(
$valueHolder3da10,
$this,
'getRepository',
array('entityName' => $entityName),
$this->initializer8b6cd,
setter: fn($v) => $this->valueHolder3da10 ??= $v, // <- instead of passing $valueHolder3da10 by ref, this closure assure to assign once
) || 1);
return $this->valueHolder3da10->getRepository($entityName);
}
// App_KernelDevDebugContainer
return $this->services['doctrine.orm.default_entity_manager'] = $this->createProxy('EntityManager_9a5be93', function () {
return \EntityManager_9a5be93::staticProxyConstructor(function (
&$wrappedInstance,
\ProxyManager\Proxy\LazyLoadingInterface $proxy,
string $method,
array $args,
callable $self,
?callable $setter = null
) {
$entityManager = $this->getDoctrine_Orm_DefaultEntityManagerService(false);
if ($setter) {
$setter($entityManager);
} else {
$wrappedInstance = $entityManager;
}
$proxy->setProxyInitializer(null);
return true;
});
});
I don't think a fix like that should be used. Instead, some checks to protect against proxy double initialization may be added
Version: "friendsofphp/proxy-manager-lts": "v1.0.12" (via "symfony/proxy-manager-bridge": "v6.1.0")
Maybe the whole issue should be redirected to original ocramius/proxymanager, but I can't find easy way to reproduce Symfony behavior using proxymanager directly
Requirements have a dependency with an especific version of symfony/filesystem >= 4.4.17
, this avoids installation if using earlier versions of Symfony.
I see this library is used just to dump code to disk, something that was present on earlier versions of symfony/filesystem
, I have forked the project and tried symfony/filesystem:^3.4
to be exact 3.4.47 and it works fine, there is some reason to require '>= 4.4.17`?
description here:
symfony/symfony#53843
Hello,
This is not a bug, but a question, why laminas/laminas-code dependency is limited to that?
"laminas/laminas-code": "~3.4.1|^4.0",
There is a incompatibility with 3.5.x? We are having 3.5.1 in a project.
Thank you very much.
Hi,
After upgrading this package to 1.0.15, I'm getting the error Type "Psr\Log\NullLogger" is unknown or cannot be used as property default value.
in my app.
I have a feeling this is not the fault of this package, but somewhere in my project and I have a hard time tracking this down.
Can anyone point me in direction of solving this? Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.