Skip to content

Commit

Permalink
Add support for services created with factories (#21)
Browse files Browse the repository at this point in the history
* Add support for services with factories

* Added tests

* cs

* Fixed tests

* PHP 7.2 fix
  • Loading branch information
Nyholm authored Aug 20, 2021
1 parent 2b41327 commit 8da1c44
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,17 @@ public function process(ContainerBuilder $container)
};

$proxy = $factory->createProxy($definition->getClass(), $initializer);
$definition->setClass(get_class($proxy));
$definition->setClass($proxyClass = get_class($proxy));
$definition->setPublic(true);
$definition->setLazy(true);

if (null !== $definition->getFactory()) {
$factoryMethod = $definition->getFactory();
$arguments = $definition->getArguments();
array_unshift($arguments, $factoryMethod);
$definition->setFactory([$proxyClass, '__construct_with_factory']);
$definition->setArguments($arguments);
}
}
}
}
1 change: 1 addition & 0 deletions src/Generator/LazyLoadingValueHolderGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ static function (MethodGenerator $generatedMethod) use ($originalClass, $classGe
[
new StaticProxyConstructor($initializer, Properties::fromReflectionClass($originalClass)),
Constructor::generateMethod($originalClass, $valueHolder), // Not a standard constructor
StaticConstructor::generateMethod($valueHolder), // Not a standard constructor
new MagicGet($originalClass, $initializer, $valueHolder, $publicProperties),
new MagicSet($originalClass, $initializer, $valueHolder, $publicProperties),
new MagicIsset($originalClass, $initializer, $valueHolder, $publicProperties),
Expand Down
43 changes: 43 additions & 0 deletions src/Generator/StaticConstructor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Happyr\ServiceMocking\Generator;

use Laminas\Code\Generator\Exception\InvalidArgumentException;
use Laminas\Code\Generator\ParameterGenerator;
use Laminas\Code\Generator\PropertyGenerator;
use ProxyManager\Generator\MethodGenerator;

/**
* The `__construct_with_factory` implementation for lazy loading proxies. This
* is used for services created with service factories.
*
* @interal
*/
class StaticConstructor extends MethodGenerator
{
/**
* @throws InvalidArgumentException
*/
public static function generateMethod(PropertyGenerator $valueHolder): self
{
$constructor = new self('__construct_with_factory');
$constructor->setStatic(true);
$constructor->setParameter(new ParameterGenerator('factory', 'callable'));
$parameter = new ParameterGenerator('arguments');
$parameter->setVariadic(true);
$constructor->setParameter($parameter);

$constructor->setBody(
'static $reflection;'."\n\n"
.'$reflection = $reflection ?? new \ReflectionClass(self::class);'."\n"
.'$model = $reflection->newInstanceWithoutConstructor();'."\n"
.'$model->'.$valueHolder->getName().' = \Closure::fromCallable($factory)->__invoke(...$arguments);'."\n"
.'\Happyr\ServiceMocking\ServiceMock::initializeProxy($model);'."\n\n"
.'return $model;'
);

return $constructor;
}
}
16 changes: 16 additions & 0 deletions tests/Functional/BundleInitializationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Happyr\ServiceMocking\HappyrServiceMockingBundle;
use Happyr\ServiceMocking\ServiceMock;
use Happyr\ServiceMocking\Tests\Resource\ExampleService;
use Happyr\ServiceMocking\Tests\Resource\ServiceWithFactory;
use Happyr\ServiceMocking\Tests\Resource\StatefulService;
use Nyholm\BundleTest\BaseBundleTestCase;
use ProxyManager\Proxy\VirtualProxyInterface;
Expand Down Expand Up @@ -54,6 +55,21 @@ public function testInitBundle()
ServiceMock::swap($service, $mock);

$this->assertSame(2, $service->getNumber());

$serviceWithFactory = $container->get(ServiceWithFactory::class);
$this->assertSame(3, $serviceWithFactory->getSecretNumber());

$called = false;
ServiceMock::next($serviceWithFactory, 'getNumber', function ($dir) use (&$called) {
$called = true;
$this->assertSame(11, $dir);

return 17;
});

$this->assertSame(17, $serviceWithFactory->getNumber(11));
$this->assertTrue($called);
$this->assertSame(14, $serviceWithFactory->getNumber(11));
}

public function testRebootBundle()
Expand Down
5 changes: 5 additions & 0 deletions tests/Functional/config.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
happyr_service_mocking:
services:
- 'Happyr\ServiceMocking\Tests\Resource\ExampleService'
- 'Happyr\ServiceMocking\Tests\Resource\ServiceWithFactory'

services:

Expand All @@ -10,3 +11,7 @@ services:
Happyr\ServiceMocking\Tests\Resource\StatefulService:
tags:
- { name: happyr_service_mock }

Happyr\ServiceMocking\Tests\Resource\ServiceWithFactory:
factory: [Happyr\ServiceMocking\Tests\Resource\ServiceWithFactory, create]
arguments: [6,3]
34 changes: 34 additions & 0 deletions tests/Resource/ServiceWithFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Happyr\ServiceMocking\Tests\Resource;

class ServiceWithFactory
{
private $number;
private $secretNumber;

public function __construct(int $number)
{
$this->number = $number;
}

public static function create(int $number, int $secret)
{
$self = new self($number);
$self->secretNumber = $secret;

return $self;
}

public function getNumber(int $input = 0): int
{
return $this->number + $input - $this->secretNumber;
}

public function getSecretNumber(): int
{
return $this->secretNumber;
}
}
Empty file removed tests/Unit/.gitignore
Empty file.

0 comments on commit 8da1c44

Please sign in to comment.