FileLock::__destruct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace TH\Lock;
4
5
use Psr\Log\LoggerInterface;
6
use Psr\Log\NullLogger;
7
8
class FileLock implements Lock
9
{
10
    const EXCLUSIVE = true;
11
    const SHARED = false;
12
13
    const BLOCKING = true;
14
    const NON_BLOCKING = false;
15
16
    private $lock_file;
17
    private $exclusive;
18
    private $blocking;
19
    private $fh;
20
    private $remove_on_release;
21
22
    private $logger;
23
24
    /**
25
     * @param string          $lock_file         path to file
26
     * @param boolean         $exclusive         true for an exclusive lock, false for shared one
27
     * @param boolean         $blocking          true to wait for lock to be available,
28
     *                                           false to throw exception instead of waiting
29
     * @param boolean         $remove_on_release remove file on release if no other lock remains
30
     * @param LoggerInterface $logger
31
     */
32
    public function __construct(
33
        $lock_file,
34
        $exclusive = FileLock::EXCLUSIVE,
35
        $blocking = FileLock::NON_BLOCKING,
36
        $remove_on_release = false,
37
        LoggerInterface $logger = null
38
    ) {
39
        $this->lock_file         = $lock_file;
40
        $this->exclusive         = $exclusive;
41
        $this->blocking          = $blocking;
42
        $this->remove_on_release = $remove_on_release;
43
44
        $this->logger = $logger ?: new NullLogger;
45
    }
46
47
    /**
48
     * @inherit
49
     */
50
    public function acquire()
51
    {
52
        if ($this->exclusive === FileLock::EXCLUSIVE) {
53
            $lock_type = "exclusive";
54
            $operation = LOCK_EX;
55
        } else {
56
            $lock_type = "shared";
57
            $operation = LOCK_SH;
58
        }
59
60
        if ($this->blocking === FileLock::NON_BLOCKING) {
61
            $operation |= LOCK_NB;
62
        }
63
64
        $this->tryAcquire($operation, $lock_type);
65
    }
66
67
    /**
68
     * try to acquire lock on file, throw in case of faillure
69
     * @param  int    $operation
70
     * @param  string $lock_type lock type description
71
     * @return void
72
     * @see https://php.net/flock
73
     */
74
    private function tryAcquire($operation, $lock_type)
75
    {
76
        $log_data = [
77
            "lock_file" => $this->lock_file,
78
            "lock_type" => $lock_type
79
        ];
80
81
        if (!$this->flock($operation)) {
82
            $this->logger->debug("could not acquire {lock_type} lock on {lock_file}", $log_data);
83
84
            throw new RuntimeException(
85
                "Could not acquire $lock_type lock on {$this->lock_file}"
86
            );
87
88
        }
89
90
        $this->logger->debug("{lock_type} lock acquired on {lock_file}", $log_data);
91
    }
92
93
    public function release()
94
    {
95
        if ($this->fh === null) {
96
            return;
97
        }
98
99
        if ($this->remove_on_release && $this->flock(LOCK_EX | LOCK_NB)) {
100
            if (is_file($this->lock_file)) {
101
                unlink($this->lock_file);
102
            }
103
        }
104
105
        $this->flock(LOCK_UN);
106
        fclose($this->fh);
107
        $this->fh = null;
108
109
        $this->logger->debug("{lock_type} lock released on {lock_file}", ["lock_file" => $this->lock_file]);
110
    }
111
112
    public function __destruct()
113
    {
114
        $this->release();
115
    }
116
117
    /**
118
     * @return boolean
119
     */
120
    private function flock($operation)
121
    {
122
        if ($this->fh === null) {
123
            $this->fh = fopen($this->lock_file, "c");
124
        }
125
126
        if (!is_resource($this->fh)) {
127
            throw new RuntimeException("Could not open lock file {$this->lock_file}");
128
        }
129
130
        return flock($this->fh, $operation);
131
    }
132
}
133