Deprecate lock.store aliases
authorJérémy Derussé <jeremy@derusse.com>
Thu, 15 Oct 2020 19:27:13 +0000 (21:27 +0200)
committerJérémy Derussé <jeremy@derusse.com>
Thu, 15 Oct 2020 20:19:33 +0000 (22:19 +0200)
UPGRADE-5.2.md
UPGRADE-6.0.md
src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
src/Symfony/Component/Lock/CHANGELOG.md
src/Symfony/Component/Lock/LockFactory.php
src/Symfony/Component/Lock/Tests/LockFactoryTest.php
src/Symfony/Component/Semaphore/SemaphoreFactory.php
src/Symfony/Component/Semaphore/Tests/SemaphoreFactoryTest.php

index ff29d10..36859ed 100644 (file)
@@ -15,7 +15,7 @@ FrameworkBundle
    keep cache namespaces separated by environment of the app. The `%kernel.container_class%` (which includes the environment)
    used to be added by default to the seed, which is not the case anymore. This allows sharing caches between
    apps or different environments.
- * Deprecated the `lock.RESOURCE_NAME` service and the `lock` and `LockInterface` aliases, use `lock.RESOURCE_NAME.factory`, `lock.factory` or `LockFactory` instead.
+ * Deprecated the `lock.RESOURCE_NAME` and `lock.RESOURCE_NAME.store` services and the `lock`, `LockInterface`, `lock.store` and `PersistingStoreInterface` aliases, use `lock.RESOURCE_NAME.factory`, `lock.factory` or `LockFactory` instead.
 
 Form
 ----
index 1bedeaa..2ee58d9 100644 (file)
@@ -58,7 +58,7 @@ FrameworkBundle
  * Removed `session.attribute_bag` service and `session.flash_bag` service.
  * The `form.factory`, `form.type.file`, `translator`, `security.csrf.token_manager`, `serializer`,
    `cache_clearer`, `filesystem` and `validator` services are now private.
- * Removed the `lock.RESOURCE_NAME` service and the `lock` and `LockInterface` aliases, use `lock.RESOURCE_NAME.factory`, `lock.factory` or `LockFactory` instead.
+ * Removed the `lock.RESOURCE_NAME` and `lock.RESOURCE_NAME.store` services and the `lock`, `LockInterface`, `lock.store` and `PersistingStoreInterface` aliases, use `lock.RESOURCE_NAME.factory`, `lock.factory` or `LockFactory` instead.
 
 HttpFoundation
 --------------
index 0d12982..dde9bad 100644 (file)
@@ -14,7 +14,7 @@ CHANGELOG
  * added `assertCheckboxChecked()` and `assertCheckboxNotChecked()` in `WebTestCase`
  * added `assertFormValue()` and `assertNoFormValue()` in `WebTestCase`
  * Added "--as-tree=3" option to `translation:update` command to dump messages as a tree-like structure. The given value defines the level where to switch to inline YAML
- * Deprecated the `lock.RESOURCE_NAME` service and the `lock` and `LockInterface` aliases, use `lock.RESOURCE_NAME.factory`, `lock.factory` or `LockFactory` instead.
+ * Deprecated the `lock.RESOURCE_NAME` and `lock.RESOURCE_NAME.store` services and the `lock`, `LockInterface`, `lock.store` and `PersistingStoreInterface` aliases, use `lock.RESOURCE_NAME.factory`, `lock.factory` or `LockFactory` instead.
 
 5.1.0
 -----
index 3a7ab77..c9f9d40 100644 (file)
@@ -1674,14 +1674,15 @@ class FrameworkExtension extends Extension
             if (\count($storeDefinitions) > 1) {
                 $combinedDefinition = new ChildDefinition('lock.store.combined.abstract');
                 $combinedDefinition->replaceArgument(0, $storeDefinitions);
-                $container->setDefinition('lock.'.$resourceName.'.store', $combinedDefinition);
+                $container->setDefinition('lock.'.$resourceName.'.store', $combinedDefinition)->setDeprecated('symfony/framework-bundle', '5.2', 'The "%service_id%" service is deprecated, use "lock.'.$resourceName.'.factory" instead.');
+                $container->setDefinition($storeDefinitionId = '.lock.'.$resourceName.'.store.'.$container->hash($resourceStores), $combinedDefinition);
             } else {
-                $container->setAlias('lock.'.$resourceName.'.store', new Alias((string) $storeDefinitions[0], false));
+                $container->setAlias('lock.'.$resourceName.'.store', (new Alias($storeDefinitionId, false))->setDeprecated('symfony/framework-bundle', '5.2', 'The "%alias_id%" alias is deprecated, use "lock.'.$resourceName.'.factory" instead.'));
             }
 
             // Generate factories for each resource
             $factoryDefinition = new ChildDefinition('lock.factory.abstract');
-            $factoryDefinition->replaceArgument(0, new Reference('lock.'.$resourceName.'.store'));
+            $factoryDefinition->replaceArgument(0, new Reference($storeDefinitionId));
             $container->setDefinition('lock.'.$resourceName.'.factory', $factoryDefinition);
 
             // Generate services for lock instances
@@ -1693,14 +1694,14 @@ class FrameworkExtension extends Extension
 
             // provide alias for default resource
             if ('default' === $resourceName) {
-                $container->setAlias('lock.store', new Alias('lock.'.$resourceName.'.store', false));
+                $container->setAlias('lock.store', (new Alias($storeDefinitionId, false))->setDeprecated('symfony/framework-bundle', '5.2', 'The "%alias_id%" alias is deprecated, use "lock.factory" instead.'));
                 $container->setAlias('lock.factory', new Alias('lock.'.$resourceName.'.factory', false));
                 $container->setAlias('lock', (new Alias('lock.'.$resourceName, false))->setDeprecated('symfony/framework-bundle', '5.2', 'The "%alias_id%" alias is deprecated, use "lock.factory" instead.'));
-                $container->setAlias(PersistingStoreInterface::class, new Alias('lock.store', false));
+                $container->setAlias(PersistingStoreInterface::class, (new Alias($storeDefinitionId, false))->setDeprecated('symfony/framework-bundle', '5.2', 'The "%alias_id%" alias is deprecated, use "'.LockFactory::class.'" instead.'));
                 $container->setAlias(LockFactory::class, new Alias('lock.factory', false));
                 $container->setAlias(LockInterface::class, (new Alias('lock.'.$resourceName, false))->setDeprecated('symfony/framework-bundle', '5.2', 'The "%alias_id%" alias is deprecated, use "'.LockFactory::class.'" instead.'));
             } else {
-                $container->registerAliasForArgument('lock.'.$resourceName.'.store', PersistingStoreInterface::class, $resourceName.'.lock.store');
+                $container->registerAliasForArgument($storeDefinitionId, PersistingStoreInterface::class, $resourceName.'.lock.store')->setDeprecated('symfony/framework-bundle', '5.2', 'The "%alias_id%" alias is deprecated, use "'.LockFactory::class.' '.$resourceName.'LockFactory" instead.');
                 $container->registerAliasForArgument('lock.'.$resourceName.'.factory', LockFactory::class, $resourceName.'.lock.factory');
                 $container->registerAliasForArgument('lock.'.$resourceName, LockInterface::class, $resourceName.'.lock')->setDeprecated('symfony/framework-bundle', '5.2', 'The "%alias_id%" alias is deprecated, use "'.LockFactory::class.' $'.$resourceName.'LockFactory" instead.');
             }
index 360c1eb..4c85f76 100644 (file)
@@ -11,6 +11,7 @@ CHANGELOG
  * deprecated `RetryTillSaveStore`, logic has been moved in `Lock` and is not needed anymore.
  * added `InMemoryStore`
  * added `PostgreSqlStore`
+ * added the `LockFactory::CreateLockFromKey()` method.
 
 5.1.0
 -----
index 82bcd01..11e3bd2 100644 (file)
@@ -43,7 +43,19 @@ class LockFactory implements LoggerAwareInterface
      */
     public function createLock(string $resource, ?float $ttl = 300.0, bool $autoRelease = true): LockInterface
     {
-        $lock = new Lock(new Key($resource), $this->store, $ttl, $autoRelease);
+        return $this->createLockFromKey(new Key($resource), $ttl, $autoRelease);
+    }
+
+    /**
+     * Creates a lock from the given key.
+     *
+     * @param Key        $key         The key containing the lock's state
+     * @param float|null $ttl         Maximum expected lock duration in seconds
+     * @param bool       $autoRelease Whether to automatically release the lock or not when the lock instance is destroyed
+     */
+    public function createLockFromKey(Key $key, ?float $ttl = 300.0, bool $autoRelease = true): LockInterface
+    {
+        $lock = new Lock($key, $this->store, $ttl, $autoRelease);
         $lock->setLogger($this->logger);
 
         return $lock;
index 376bd69..7026d02 100644 (file)
@@ -13,8 +13,8 @@ namespace Symfony\Component\Lock\Tests;
 
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use Symfony\Component\Lock\Key;
 use Symfony\Component\Lock\LockFactory;
-use Symfony\Component\Lock\LockInterface;
 use Symfony\Component\Lock\PersistingStoreInterface;
 
 /**
@@ -25,12 +25,61 @@ class LockFactoryTest extends TestCase
     public function testCreateLock()
     {
         $store = $this->getMockBuilder(PersistingStoreInterface::class)->getMock();
+        $store->expects($this->any())->method('exists')->willReturn(false);
+
+        $keys = [];
+        $store
+            ->expects($this->exactly(2))
+            ->method('save')
+            ->with($this->callback(function ($key) use (&$keys) {
+                $keys[] = $key;
+
+                return true;
+            }))
+            ->willReturn(true);
+
         $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
         $factory = new LockFactory($store);
         $factory->setLogger($logger);
 
-        $lock = $factory->createLock('foo');
+        $lock1 = $factory->createLock('foo');
+        $lock2 = $factory->createLock('foo');
+
+        // assert lock1 and lock2 don't share the same state
+        $lock1->acquire();
+        $lock2->acquire();
+
+        $this->assertNotSame($keys[0], $keys[1]);
+    }
+
+    public function testCreateLockFromKey()
+    {
+        $store = $this->getMockBuilder(PersistingStoreInterface::class)->getMock();
+        $store->expects($this->any())->method('exists')->willReturn(false);
+
+        $keys = [];
+        $store
+            ->expects($this->exactly(2))
+            ->method('save')
+            ->with($this->callback(function ($key) use (&$keys) {
+                $keys[] = $key;
+
+                return true;
+            }))
+            ->willReturn(true);
+
+        $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
+        $factory = new LockFactory($store);
+        $factory->setLogger($logger);
+
+        $key = new Key('foo');
+        $lock1 = $factory->createLockFromKey($key);
+        $lock2 = $factory->createLockFromKey($key);
+
+        // assert lock1 and lock2 share the same state
+        $lock1->acquire();
+        $lock2->acquire();
 
-        $this->assertInstanceOf(LockInterface::class, $lock);
+        $this->assertSame($keys[0], $keys[1]);
     }
 }
index bf4743e..9194817 100644 (file)
@@ -42,7 +42,16 @@ class SemaphoreFactory implements LoggerAwareInterface
      */
     public function createSemaphore(string $resource, int $limit, int $weight = 1, ?float $ttlInSecond = 300.0, bool $autoRelease = true): SemaphoreInterface
     {
-        $semaphore = new Semaphore(new Key($resource, $limit, $weight), $this->store, $ttlInSecond, $autoRelease);
+        return $this->createSemaphoreFromKey(new Key($resource, $limit, $weight), $ttlInSecond, $autoRelease);
+    }
+
+    /**
+     * @param float|null $ttlInSecond Maximum expected semaphore duration in seconds
+     * @param bool       $autoRelease Whether to automatically release the semaphore or not when the semaphore instance is destroyed
+     */
+    public function createSemaphoreFromKey(Key $key, ?float $ttlInSecond = 300.0, bool $autoRelease = true): SemaphoreInterface
+    {
+        $semaphore = new Semaphore($key, $this->store, $ttlInSecond, $autoRelease);
         $semaphore->setLogger($this->logger);
 
         return $semaphore;
index cf79154..5156f64 100644 (file)
@@ -13,9 +13,9 @@ namespace Symfony\Component\Semaphore\Tests;
 
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use Symfony\Component\Semaphore\Key;
 use Symfony\Component\Semaphore\PersistingStoreInterface;
 use Symfony\Component\Semaphore\SemaphoreFactory;
-use Symfony\Component\Semaphore\SemaphoreInterface;
 
 /**
  * @author Jérémy Derussé <jeremy@derusse.com>
@@ -26,12 +26,59 @@ class SemaphoreFactoryTest extends TestCase
     public function testCreateSemaphore()
     {
         $store = $this->getMockBuilder(PersistingStoreInterface::class)->getMock();
+
+        $keys = [];
+        $store
+            ->expects($this->exactly(2))
+            ->method('save')
+            ->with($this->callback(function ($key) use (&$keys) {
+                $keys[] = $key;
+
+                return true;
+            }))
+            ->willReturn(true);
+
         $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
         $factory = new SemaphoreFactory($store);
         $factory->setLogger($logger);
 
-        $semaphore = $factory->createSemaphore('foo', 4);
+        $semaphore1 = $factory->createSemaphore('foo', 4);
+        $semaphore2 = $factory->createSemaphore('foo', 4);
+
+        // assert lock1 and lock2 don't share the same state
+        $semaphore1->acquire();
+        $semaphore2->acquire();
+
+        $this->assertNotSame($keys[0], $keys[1]);
+    }
+
+    public function testCreateSemaphoreFromKey()
+    {
+        $store = $this->getMockBuilder(PersistingStoreInterface::class)->getMock();
+
+        $keys = [];
+        $store
+            ->expects($this->exactly(2))
+            ->method('save')
+            ->with($this->callback(function ($key) use (&$keys) {
+                $keys[] = $key;
+
+                return true;
+            }))
+            ->willReturn(true);
+
+        $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
+        $factory = new SemaphoreFactory($store);
+        $factory->setLogger($logger);
+
+        $key = new Key('foo', 4);
+        $semaphore1 = $factory->createSemaphoreFromKey($key);
+        $semaphore2 = $factory->createSemaphoreFromKey($key);
+
+        // assert lock1 and lock2 share the same state
+        $semaphore1->acquire();
+        $semaphore2->acquire();
 
-        $this->assertInstanceOf(SemaphoreInterface::class, $semaphore);
+        $this->assertSame($keys[0], $keys[1]);
     }
 }