FileLock::has()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 9
ccs 0
cts 9
cp 0
crap 12
rs 10
1
<?php
2
3
namespace kalanis\kw_locks\Methods;
4
5
6
use kalanis\kw_locks\Interfaces\IKLTranslations;
7
use kalanis\kw_locks\Interfaces\ILock;
8
use kalanis\kw_locks\LockException;
9
use kalanis\kw_locks\Traits\TLang;
10
use kalanis\kw_paths\Stuff;
11
12
13
/**
14
 * Class FileLock
15
 * @package kalanis\kw_locks\Methods
16
 * Lock some target
17
 * Uses low-level volume operations
18
 * @codeCoverageIgnore accessing local volume
19
 */
20
class FileLock implements ILock
21
{
22
    use TLang;
23
24
    protected string $lockFilename = '';
25
    /** @var resource|null */
26
    protected $handle = null;
27
28
    /**
29
     * @param string $lockFilename
30
     * @param IKLTranslations|null $lang
31
     * @throws LockException
32
     */
33
    public function __construct(string $lockFilename, ?IKLTranslations $lang = null)
34
    {
35
        $this->setKlLang($lang);
36
        $path = Stuff::directory($lockFilename);
37
        $this->accessDir($path);
38
        if ((!is_file($lockFilename) && !is_writable($path)) || (is_file($lockFilename) && !is_writable($lockFilename))) {
39
            throw new LockException($this->getKlLang()->iklCannotUseFile($lockFilename));
40
        }
41
        $this->lockFilename = $lockFilename;
42
    }
43
44
    public function __destruct()
45
    {
46
        try {
47
            $this->delete();
48
        } catch (LockException $ex) {
49
            // do nothing instead of
50
            // register_shutdown_function([$this, 'delete']);
51
        }
52
    }
53
54
    /**
55
     * @param string $path
56
     * @throws LockException
57
     */
58
    protected function accessDir(string $path): void
59
    {
60
        if (!is_dir($path)) {
61
            if (mkdir($path, 0777, true)) {
62
                chmod($path, 0777);
63
            } else {
64
                throw new LockException($this->getKlLang()->iklCannotUsePath($path));
65
            }
66
        }
67
    }
68
69
    public function has(): bool
70
    {
71
        if (file_exists($this->lockFilename)) {
72
            if (!empty($this->handle)) {
73
                return true;
74
            }
75
            throw new LockException($this->getKlLang()->iklLockedByOther());
76
        }
77
        return false;
78
    }
79
80
    public function create(bool $force = false): bool
81
    {
82
        if ($this->has()) {
83
            return false;
84
        }
85
86
        $handle = @fopen($this->lockFilename, 'c');
87
        if (false === $handle) {
88
            throw new LockException($this->getKlLang()->iklCannotOpenFile($this->lockFilename));
89
        }
90
        $this->handle = $handle;
91
        $result = flock($this->handle, LOCK_EX | LOCK_NB);
92
        if (false === $result) {
93
            fclose($this->handle);
94
        }
95
96
        if (true === $result) {
97
            $writeStatus = @fwrite($this->handle, strval(getmypid()));
98
            if (false === $writeStatus) {
99
                fclose($this->handle);
100
                return false;
101
            }
102
        }
103
104
        return $result;
105
    }
106
107
    public function delete(bool $force = false): bool
108
    {
109
        if (!$this->has()) {
110
            return true;
111
        }
112
        if (is_resource($this->handle)) {
113
            $resultLock = flock($this->handle, LOCK_UN);
114
            $resultHandle = fclose($this->handle);
115
            $resultRemove = @unlink($this->lockFilename);
116
            return ($resultLock && $resultHandle && $resultRemove);
117
        } else {
118
            return false;
119
        }
120
    }
121
}
122