Grégoire Pineau
Contributed by Grégoire Pineau in #10475

File locking is a mechanism that restricts access to a computer file by allowing only one user or process access at any specific time. This mechanism was introduced back in 1963 for the mainframes and it will make its debut in Symfony starting from version 2.6.

The new LockHandler class provides a simple abstraction to lock anything by means of a file lock. Its most common use case is to avoid race conditions by locking commands, so the same command cannot be executed concurrently by different processes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\LockHandler;

class UpdateContentsCommand extends Command
{
    protected function configure()
    {
        // ...
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // create the lock
        $lock = new LockHandler('update:contents');
        if (!$lock->lock()) {
            $output->writeln('The command is already running in another process.');

            return 0;
        }

        // ... do some task

        // (optional) release the lock (otherwise, PHP will do it
        // for you automatically)
        $lock->release();
    }
}

The LockHandler constructor takes as its first argument the lock identifier, which will be used as part of the name of the file used to create the lock. By default, locks are created in the temporary directory of the system. If you want to use a specific directory, pass it as the second optional argument of the constructor.

The lock() method returns true if the lock was acquired and false otherwise. In addition, you can optionally pass a boolean argument to indicate if you want to wait until the requested lock is released. This is useful to execute a command just after the other locking command is finished:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class UpdateContentsCommand extends Command
{
    // ...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // create the lock
        $lock = new LockHandler('update:contents');

        // wait for the lock release as long as necessary
        if (!$lock->lock(true)) {
            $output->writeln('The command is already running in another process.');

            return 0;
        }

        // ...
    }
}

The lock handler has been limited on purpose to work only with file-based locks, because it's extremely complex to make it work on network or databases. This means that it only works when using one and only one host. If you have several hosts, you must not use this helper.

Published in #Living on the edge