Skip to content

Commit

Permalink
fix(coroutines): added custom proxy type instead of lazy loading valu…
Browse files Browse the repository at this point in the history
…e holder to get rid of shared lazy tmp object access
  • Loading branch information
pf-releaser-bot authored and Martin Fris committed Feb 25, 2023
1 parent 1632b47 commit df3da24
Show file tree
Hide file tree
Showing 23 changed files with 662 additions and 52 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ mutagen.yml.lock
tests/Fixtures/Symfony/app/data/*
docker-compose.override.yml
Dockerfile.override
ext
!ext/.gitignore
1 change: 1 addition & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
'tests/Unit/Server/Php8',
'vendor',
'hack',
'ext',
]);

$config = new PhpCsFixer\Config();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public function __construct(private ServicePoolContainer $container)
{
}

/**
* @param BaseServicePool<object> $servicePool
*/
public function configure(BaseServicePool $servicePool): void
{
$this->container->addPool($servicePool);
Expand Down
5 changes: 2 additions & 3 deletions src/Bridge/Symfony/Bundle/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ services:

K911\Swoole\Bridge\Symfony\Container\Proxy\Instantiator:
arguments:
$proxyFactory: '@swoole_bundle.service_proxy_factory'
$proxyGenerator: '@K911\Swoole\Bridge\Symfony\Container\Proxy\Generator'

K911\Swoole\Bridge\Symfony\Container\Proxy\UnmanagedFactoryInstantiator:
arguments:
Expand All @@ -244,8 +244,7 @@ services:
swoole_bundle.filesystem:
class: 'Symfony\Component\Filesystem\Filesystem'

swoole_bundle.service_proxy_factory:
class: 'ProxyManager\Factory\LazyLoadingValueHolderFactory'
K911\Swoole\Bridge\Symfony\Container\Proxy\Generator:
arguments:
$configuration: '@swoole_bundle.service_proxy_configuration'

Expand Down
25 changes: 25 additions & 0 deletions src/Bridge/Symfony/Container/Proxy/ContextualProxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Container\Proxy;

use K911\Swoole\Bridge\Symfony\Container\ServicePool\ServicePool;

/**
* @template RealObjectType of object
*/
interface ContextualProxy
{
/**
* @return ServicePool<RealObjectType>
*/
public function getServicePool(): ServicePool;

/**
* @param ServicePool<RealObjectType> $servicePool
*
* @return ContextualProxy<RealObjectType>&RealObjectType
*/
public static function staticProxyConstructor(ServicePool $servicePool): object;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Container\Proxy\Generation;

use K911\Swoole\Bridge\Symfony\Container\Proxy\ContextualProxy;
use K911\Swoole\Bridge\Symfony\Container\ServicePool\ServicePool;
use ProxyManager\Configuration;
use ProxyManager\Factory\AbstractBaseFactory;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
use ProxyManager\Signature\Exception\MissingSignatureException;

/**
* Factory responsible of producing proxy objects.
*/
class ContextualAccessForwarderFactory extends AbstractBaseFactory
{
private $generator;

public function __construct(?Configuration $configuration = null)
{
parent::__construct($configuration);

$this->generator = new ContextualAccessForwarderGenerator(new MethodForwarderBuilder());
}

/**
* @template RealObjectType of object
*
* @param ServicePool<RealObjectType> $servicePool
* @param class-string<RealObjectType> $serviceClass
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws \OutOfBoundsException
*
* @return ContextualProxy<RealObjectType>&RealObjectType
*/
public function createProxy(ServicePool $servicePool, string $serviceClass)
{
/** @var class-string<ContextualProxy<RealObjectType>&RealObjectType> $proxyClassName */
$proxyClassName = $this->generateProxy($serviceClass);

return $proxyClassName::staticProxyConstructor($servicePool);
}

protected function getGenerator(): ProxyGeneratorInterface
{
return $this->generator;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Container\Proxy\Generation;

use K911\Swoole\Bridge\Symfony\Container\Proxy\ContextualProxy;
use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator\GetWrappedServicePoolValue;
use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator\MagicGet;
use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator\MagicSet;
use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator\StaticProxyConstructor;
use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\PropertyGenerator\ServicePoolProperty;
use Laminas\Code\Generator\ClassGenerator;
use Laminas\Code\Generator\MethodGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;

/**
* Generator for proxies with service pool.
*/
class ContextualAccessForwarderGenerator implements ProxyGeneratorInterface
{
public function __construct(private MethodForwarderBuilder $forwarderBuilder)
{
}

/**
* @template T of object
*
* @param \ReflectionClass<T> $originalClass
*
* @throws \InvalidArgumentException
* @throws InvalidProxiedClassException
*/
public function generate(\ReflectionClass $originalClass, ClassGenerator $classGenerator): void
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);

$interfaces = [
ContextualProxy::class,
];

if ($originalClass->isInterface()) {
$interfaces[] = $originalClass->getName();
}

if (!$originalClass->isInterface()) {
$classGenerator->setExtendedClass($originalClass->getName());
}

$publicProperties = new PublicPropertiesMap(Properties::fromReflectionClass($originalClass));
$classGenerator->setImplementedInterfaces($interfaces);
$classGenerator->addPropertyFromGenerator($servicePoolProperty = new ServicePoolProperty());
$classGenerator->addPropertyFromGenerator($publicProperties);
$closure = static function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator): void {
ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod);
};

array_map(
$closure,
array_merge(
array_map(
$this->forwarderBuilder->buildMethodInterceptor($servicePoolProperty),
ProxiedMethodsFilter::getProxiedMethods($originalClass)
),
[
new StaticProxyConstructor($servicePoolProperty, Properties::fromReflectionClass($originalClass)),
new GetWrappedServicePoolValue($servicePoolProperty),
new MagicGet($originalClass, $servicePoolProperty, $publicProperties),
new MagicSet($originalClass, $servicePoolProperty, $publicProperties),
]
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Container\Proxy\Generation;

use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator\ForwardedMethod;
use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\PropertyGenerator\ServicePoolProperty;
use Laminas\Code\Reflection\MethodReflection;

final class MethodForwarderBuilder
{
public function buildMethodInterceptor(ServicePoolProperty $servicePoolHolderProperty): callable
{
return static function (\ReflectionMethod $method) use ($servicePoolHolderProperty): ForwardedMethod {
return ForwardedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$servicePoolHolderProperty
);
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator;

use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator\Util\MethodForwarderGenerator;
use Laminas\Code\Generator\Exception\InvalidArgumentException;
use Laminas\Code\Generator\PropertyGenerator;
use Laminas\Code\Reflection\MethodReflection;
use ProxyManager\Generator\MethodGenerator;

/**
* Method with additional pre- and post- interceptor logic in the body.
*/
class ForwardedMethod extends MethodGenerator
{
/**
* @throws InvalidArgumentException
*/
public static function generateMethod(
MethodReflection $originalMethod,
PropertyGenerator $servicePoolHolderProperty
): self {
$method = static::fromReflectionWithoutBodyAndDocBlock($originalMethod);
$forwardedParams = [];

foreach ($originalMethod->getParameters() as $parameter) {
$forwardedParams[] = ($parameter->isVariadic() ? '...' : '').'$'.$parameter->getName();
}

$method->setBody(MethodForwarderGenerator::createForwardedMethodBody(
$originalMethod->getName().'('.implode(', ', $forwardedParams).')',
$servicePoolHolderProperty,
$originalMethod
));

return $method;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator;

use K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\PropertyGenerator\ServicePoolProperty;
use K911\Swoole\Bridge\Symfony\Container\ServicePool\ServicePool;
use Laminas\Code\Generator\Exception\InvalidArgumentException;
use ProxyManager\Generator\MethodGenerator;

/**
* Implementation for {@see \ProxyManager\Proxy\ValueHolderInterface::getWrappedValueHolderValue}
* for lazy loading value holder objects.
*/
class GetWrappedServicePoolValue extends MethodGenerator
{
/**
* Constructor.
*
* @throws InvalidArgumentException
*/
public function __construct(ServicePoolProperty $servicePoolHolderProperty)
{
parent::__construct('getServicePool');
$this->setBody('return $this->'.$servicePoolHolderProperty->getName().';');
$this->setReturnType(ServicePool::class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Container\Proxy\Generation\MethodGenerator;

use Laminas\Code\Generator\ParameterGenerator;
use Laminas\Code\Generator\PropertyGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;

/**
* Magic `__get` for lazy loading value holder objects.
*/
class MagicGet extends MagicMethodGenerator
{
/**
* Constructor.
*
* @template T of object
*
* @param \ReflectionClass<T> $originalClass
*
* @throws \InvalidArgumentException
*/
public function __construct(
\ReflectionClass $originalClass,
PropertyGenerator $servicePoolProperty,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);

$hasParent = $originalClass->hasMethod('__get');

$servicePool = $servicePoolProperty->getName();
$callParent = 'if (isset(self::$'.$publicProperties->getName()."[\$name])) {\n"
.' return $this->'.$servicePool.'->get()->$name;'
."\n}\n\n";

if ($hasParent) {
$this->setBody(
$callParent.'return $this->'.$servicePool.'->get()->__get($name);'
);

return;
}

$this->setBody(
$callParent.PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name',
null,
new PropertyGenerator($servicePool.'->get()'),
null,
$originalClass
)
);
}
}
Loading

0 comments on commit df3da24

Please sign in to comment.