2016-05-08 15:37:49 +02:00
|
|
|
<?php
|
|
|
|
|
2024-02-16 14:47:20 +01:00
|
|
|
class Locks {
|
|
|
|
private static function filesystem(string $key): Lock|false {
|
|
|
|
$key = str_replace('/', '::', $key);
|
|
|
|
$key = str_replace("\0", '', $key);
|
|
|
|
|
|
|
|
$fd = fopen("tmp/locks/$key", "w");
|
|
|
|
if ($fd === false) {
|
|
|
|
return false;
|
2024-02-16 14:18:55 +01:00
|
|
|
}
|
2024-02-16 14:47:20 +01:00
|
|
|
|
|
|
|
return new class($fd) implements Lock {
|
|
|
|
// Resources have no type in php.
|
|
|
|
private mixed $f;
|
|
|
|
|
|
|
|
|
|
|
|
function __construct($fd) {
|
|
|
|
$this->f = $fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function get(bool $nonblock = false): Lock|false {
|
|
|
|
$wouldblock = false;
|
|
|
|
flock($this->f, LOCK_SH | ($nonblock ? LOCK_NB : 0), $wouldblock);
|
|
|
|
if ($nonblock && $wouldblock) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function get_ex(bool $nonblock = false): Lock|false {
|
|
|
|
$wouldblock = false;
|
|
|
|
flock($this->f, LOCK_EX | ($nonblock ? LOCK_NB : 0), $wouldblock);
|
|
|
|
if ($nonblock && $wouldblock) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function free(): Lock {
|
|
|
|
flock($this->f, LOCK_UN);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
};
|
2024-02-16 14:18:55 +01:00
|
|
|
}
|
2016-05-08 15:37:49 +02:00
|
|
|
|
2024-02-16 14:47:20 +01:00
|
|
|
/**
|
|
|
|
* No-op. Can be used for mocking.
|
|
|
|
*/
|
|
|
|
public static function none(): Lock|false {
|
|
|
|
return new class() implements Lock {
|
|
|
|
public function get(bool $nonblock = false): Lock|false {
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function get_ex(bool $nonblock = false): Lock|false {
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function free(): Lock {
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
};
|
2024-02-16 14:18:55 +01:00
|
|
|
}
|
2016-05-08 15:37:49 +02:00
|
|
|
|
2024-02-16 14:47:20 +01:00
|
|
|
public static function get_lock(array $config, string $key): Lock|false {
|
2024-02-16 14:18:55 +01:00
|
|
|
if ($config['lock']['enabled'] == 'fs') {
|
2024-02-16 14:47:20 +01:00
|
|
|
return self::filesystem($key);
|
|
|
|
} else {
|
|
|
|
return self::none();
|
2024-02-16 14:18:55 +01:00
|
|
|
}
|
|
|
|
}
|
2024-02-16 14:47:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
interface Lock {
|
|
|
|
// Get a shared lock
|
|
|
|
public function get(bool $nonblock = false): Lock|false;
|
|
|
|
|
|
|
|
// Get an exclusive lock
|
|
|
|
public function get_ex(bool $nonblock = false): Lock|false;
|
2016-05-08 15:37:49 +02:00
|
|
|
|
2024-02-16 14:18:55 +01:00
|
|
|
// Free a lock
|
2024-02-16 14:47:20 +01:00
|
|
|
public function free(): Lock;
|
2016-05-08 15:37:49 +02:00
|
|
|
}
|