Skip to content

Commit

Permalink
feature #54135 [Console] Add a way to use custom lock factory in lock…
Browse files Browse the repository at this point in the history
…ableTrait (VincentLanglet)

This PR was squashed before being merged into the 7.1 branch.

Discussion
----------

[Console] Add a way to use custom lock factory in lockableTrait

| Q             | A
| ------------- | ---
| Branch?       | 7.1
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Issues        | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exists, explain below instead -->
| License       | MIT

The LockableTrait only use SemaphoreStore or FlockStore, but currently we cannot use MemcachedStore or RedisStore.

When using a custom LockFactory everywhere in the codebase, it would be useful to chose if the commande need a server-related store or not.

I'm not sure if I have a way to  provide autowire to the `setLockFactory` method without introducing a BC break for people who rely on the fact that the LockableTrait use a different LockFactory than the one configured the `framework.lock` dsn.

Commits
-------

28c73f8 [Console] Add a way to use custom lock factory in lockableTrait
  • Loading branch information
nicolas-grekas committed Mar 18, 2024
2 parents 099daf5 + 28c73f8 commit a2fc092
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 5 deletions.
16 changes: 11 additions & 5 deletions src/Symfony/Component/Console/Command/LockableTrait.php
Expand Up @@ -26,6 +26,8 @@ trait LockableTrait
{
private ?LockInterface $lock = null;

private ?LockFactory $lockFactory = null;

/**
* Locks a command.
*/
Expand All @@ -39,13 +41,17 @@ private function lock(?string $name = null, bool $blocking = false): bool
throw new LogicException('A lock is already in place.');
}

if (SemaphoreStore::isSupported()) {
$store = new SemaphoreStore();
} else {
$store = new FlockStore();
if (null === $this->lockFactory) {
if (SemaphoreStore::isSupported()) {
$store = new SemaphoreStore();
} else {
$store = new FlockStore();
}

$this->lockFactory = (new LockFactory($store));
}

$this->lock = (new LockFactory($store))->createLock($name ?: $this->getName());
$this->lock = $this->lockFactory->createLock($name ?: $this->getName());
if (!$this->lock->acquire($blocking)) {
$this->lock = null;

Expand Down
16 changes: 16 additions & 0 deletions src/Symfony/Component/Console/Tests/Command/LockableTraitTest.php
Expand Up @@ -14,6 +14,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\SharedLockInterface;
use Symfony\Component\Lock\Store\FlockStore;
use Symfony\Component\Lock\Store\SemaphoreStore;

Expand All @@ -26,6 +27,7 @@ public static function setUpBeforeClass(): void
self::$fixturesPath = __DIR__.'/../Fixtures/';
require_once self::$fixturesPath.'/FooLockCommand.php';
require_once self::$fixturesPath.'/FooLock2Command.php';
require_once self::$fixturesPath.'/FooLock3Command.php';
}

public function testLockIsReleased()
Expand Down Expand Up @@ -64,4 +66,18 @@ public function testMultipleLockCallsThrowLogicException()
$tester = new CommandTester($command);
$this->assertSame(1, $tester->execute([]));
}

public function testCustomLockFactoryIsUsed()
{
$lockFactory = $this->createMock(LockFactory::class);
$command = new \FooLock3Command($lockFactory);

$tester = new CommandTester($command);

$lock = $this->createMock(SharedLockInterface::class);
$lock->method('acquire')->willReturn(false);

$lockFactory->expects(static::once())->method('createLock')->willReturn($lock);
$this->assertSame(1, $tester->execute([]));
}
}
35 changes: 35 additions & 0 deletions src/Symfony/Component/Console/Tests/Fixtures/FooLock3Command.php
@@ -0,0 +1,35 @@
<?php

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LockableTrait;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Lock\LockFactory;

class FooLock3Command extends Command
{
use LockableTrait;

public function __construct(LockFactory $lockFactory)
{
parent::__construct();

$this->lockFactory = $lockFactory;
}

protected function configure(): void
{
$this->setName('foo:lock3');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!$this->lock()) {
return 1;
}

$this->release();

return 2;
}
}

0 comments on commit a2fc092

Please sign in to comment.