Skip to content

Commit 0e61151

Browse files
author
Willem Stuursma-Ruwen
authored
Merge pull request #26 from TheLevti/develop
#25: Drop support for php 5.6 and 7.0
2 parents 3c900cf + b01c781 commit 0e61151

40 files changed

+721
-652
lines changed

.gitattributes

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
/.github export-ignore
12
/tests export-ignore
23
/.gitattributes export-ignore
34
/.gitignore export-ignore
45
/.travis.yml export-ignore
5-
/phpunit.xml export-ignore
6+
/phpunit.xml.dist export-ignore

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
/.idea/
1+
/.idea
2+
/vendor
23
/composer.lock
3-
/vendor/
4+
/phpunit.xml

.travis.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ cache:
77
- $HOME/.composer/cache
88

99
php:
10-
- 5.6
11-
- 7.0
1210
- 7.1
1311
- 7.2
1412
- nightly
@@ -47,6 +45,6 @@ before_script:
4745
- psql -c 'create database test;' -U postgres
4846

4947
script:
50-
- vendor/bin/phpunit --debug
48+
- vendor/bin/phpunit
5149
- vendor/bin/phpcs --standard=PSR2 classes/ tests/
5250

README.md

Lines changed: 129 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,149 @@
1+
**[Requirements](#requirements)** |
2+
**[Installation](#installation)** |
3+
**[Usage](#usage)** |
4+
**[License and authors](#license-and-authors)** |
5+
**[Donations](#donations)**
6+
7+
# php-lock/lock
8+
9+
[![Latest Stable Version](https://poser.pugx.org/malkusch/lock/version)](https://packagist.org/packages/malkusch/lock)
10+
[![Total Downloads](https://poser.pugx.org/malkusch/lock/downloads)](https://packagist.org/packages/malkusch/lock)
11+
[![Latest Unstable Version](https://poser.pugx.org/malkusch/lock/v/unstable)](//packagist.org/packages/malkusch/lock)
112
[![Build Status](https://travis-ci.org/php-lock/lock.svg?branch=master)](https://travis-ci.org/php-lock/lock)
13+
[![License](https://poser.pugx.org/malkusch/lock/license)](https://packagist.org/packages/malkusch/lock)
214

315
This library helps executing critical code in concurrent situations.
416

5-
# Installation
17+
php-lock/lock follows semantic versioning. Read more on [semver.org][1].
18+
19+
----
20+
21+
## Requirements
22+
23+
- PHP 7.1 or above
24+
- Optionally [nrk/predis][2] to use the predis locks.
25+
- Optionally the [php-pcntl][3] extension to enable locking with flock without
26+
busy waiting in CLI scripts.
627

7-
Use [Composer](https://getcomposer.org/):
28+
----
29+
30+
## Installation
31+
32+
### Composer
33+
34+
To use this library through [composer][4], run the following terminal command
35+
inside your repository's root folder.
836

937
```sh
10-
composer require malkusch/lock
38+
composer require "malkusch/lock"
1139
```
1240

13-
# Usage
41+
As an alternative, you can directly add this library to your `composer.json`.
1442

15-
The package is in the namespace
16-
[`malkusch\lock`](http://malkusch.github.io/lock/api/namespace-malkusch.lock.html).
43+
``` json
44+
{
45+
"require": {
46+
"malkusch/lock": "^1.4"
47+
}
48+
}
49+
```
1750

18-
## Mutex
51+
To use the newest (maybe unstable) version, use `dev-master` as the version in
52+
your `composer.json`.
1953

20-
The
21-
[`Mutex`](http://malkusch.github.io/lock/api/class-malkusch.lock.mutex.Mutex.html)
22-
provides the API for this library.
54+
``` json
55+
{
56+
"require": {
57+
"malkusch/lock": "dev-master"
58+
}
59+
}
60+
```
61+
62+
## Usage
63+
64+
This library uses the namespace `malkusch\lock`.
65+
66+
### Mutex
2367

24-
### Mutex::synchronized()
68+
The [`malkusch\lock\mutex\Mutex`][5] class is an abstract class and provides the
69+
base API for this library.
2570

26-
[`Mutex::synchronized()`](http://malkusch.github.io/lock/api/class-malkusch.lock.mutex.Mutex.html#_synchronized)
27-
executes code exclusively. This method guarantees that the code is only executed
28-
by one process at once. Other processes have to wait until the mutex is available.
29-
The critical code may throw an exception, which would release the lock as well.
71+
#### Mutex::synchronized()
72+
73+
[`malkusch\lock\mutex\Mutex::synchronized()`][6] executes code exclusively. This
74+
method guarantees that the code is only executed by one process at once. Other
75+
processes have to wait until the mutex is available. The critical code may throw
76+
an exception, which would release the lock as well.
77+
78+
This method returns what ever is returned to the given callable. The return
79+
value is not checked, thus it is up to the user to decide if for example the
80+
return value `false` or `null` should be seen as a failed action.
3081

3182
Example:
83+
3284
```php
33-
$mutex->synchronized(function () use ($bankAccount, $amount) {
85+
$newBalance = $mutex->synchronized(function () use ($bankAccount, $amount) {
3486
$balance = $bankAccount->getBalance();
3587
$balance -= $amount;
3688
if ($balance < 0) {
37-
throw new \DomainException("You have no credit.");
38-
89+
throw new \DomainException('You have no credit.');
3990
}
4091
$bankAccount->setBalance($balance);
92+
93+
return $balance;
4194
});
4295
```
4396

44-
### Mutex::check()
97+
#### Mutex::check()
98+
99+
[`malkusch\lock\mutex\Mutex::check()`][7] sets a callable, which will be
100+
executed when [`malkusch\lock\util\DoubleCheckedLocking::then()`][8] is called,
101+
and performs a double-checked locking pattern, where it's return value decides
102+
if the lock needs to be acquired and the synchronized code to be executed.
103+
104+
See [https://en.wikipedia.org/wiki/Double-checked_locking][9] for a more
105+
detailed explanation of that feature.
45106

46-
[`Mutex::check()`](http://malkusch.github.io/lock/api/class-malkusch.lock.mutex.Mutex.html#_check)
47-
performs a double-checked locking pattern. I.e. if the check fails, no lock
48-
will be acquired. Else if the check was true, a lock will be acquired and the
49-
check will be perfomed as well together with the critical code.
107+
If the check's callable returns `false`, no lock will be acquired and the
108+
synchronized code will not be executed. In this case the
109+
[`malkusch\lock\util\DoubleCheckedLocking::then()`][8] method, will also return
110+
`false` to indicate that the check did not pass either before or after acquiring
111+
the lock.
112+
113+
In the case where the check's callable returns a value other than `false`, the
114+
[`malkusch\lock\util\DoubleCheckedLocking::then()`][8] method, will
115+
try to acquire the lock and on success will perform the check again. Only when
116+
the check returns something other than `false` a second time, the synchronized
117+
code callable, which has been passed to `then()` will be executed. In this case
118+
the return value of `then()` will be what ever the given callable returns and
119+
thus up to the user to return `false` or `null` to indicate a failed action as
120+
this return value will not be checked by the library.
50121

51122
Example:
123+
52124
```php
53-
$mutex->check(function () use ($bankAccount, $amount) {
125+
$newBalance = $mutex->check(function () use ($bankAccount, $amount) {
54126
return $bankAccount->getBalance() >= $amount;
55-
56127
})->then(function () use ($bankAccount, $amount) {
57128
$balance = $bankAccount->getBalance();
58129
$balance -= $amount;
59130
$bankAccount->setBalance($balance);
131+
132+
return $balance;
60133
});
134+
135+
if (false === $newBalance) {
136+
if ($balance < 0) {
137+
throw new \DomainException('You have no credit.');
138+
}
139+
}
61140
```
62141

63142
### Implementations
64143

65-
The `Mutex` is an abstract class. You will have to choose an implementation:
144+
Because the [`malkusch\lock\mutex\Mutex`](#mutex) class is an abstract class,
145+
you can choose from one of the provided implementations or create/extend your
146+
own implementation.
66147

67148
- [`CASMutex`](#casmutex)
68149
- [`FlockMutex`](#flockmutex)
@@ -74,19 +155,18 @@ The `Mutex` is an abstract class. You will have to choose an implementation:
74155
- [`MySQLMutex`](#mysqlmutex)
75156
- [`PgAdvisoryLockMutex`](#pgadvisorylockmutex)
76157

77-
78158
#### CASMutex
79159

80-
The **CASMutex**
81-
has to be used with a [Compare-and-swap](https://en.wikipedia.org/wiki/Compare-and-swap) operation.
82-
This mutex is lock free. It will repeat executing the code until the CAS operation was
83-
successful. The code should therefore notify the mutex by calling
84-
[`CASMutex::notify()`](http://malkusch.github.io/lock/api/class-malkusch.lock.mutex.CASMutex.html#_notify).
160+
The **CASMutex** has to be used with a [Compare-and-swap][10] operation. This
161+
mutex is lock free. It will repeat executing the code until the CAS operation
162+
was successful. The code should therefore notify the mutex by calling
163+
[`malkusch\lock\mutex\CASMutex::notify()`][11].
85164

86-
As the mutex keeps executing the critical code, it must not have any side effects
87-
as long as the CAS operation was not successful.
165+
As the mutex keeps executing the critical code, it must not have any side
166+
effects as long as the CAS operation was not successful.
88167

89168
Example:
169+
90170
```php
91171
$mutex = new CASMutex();
92172
$mutex->synchronized(function () use ($memcached, $mutex, $amount) {
@@ -118,8 +198,8 @@ $mutex->synchronized(function () use ($bankAccount, $amount) {
118198
});
119199
```
120200

121-
Timeouts are supported as an optional second argument. This uses the pcntl extension if
122-
possible or busy waiting if not.
201+
Timeouts are supported as an optional second argument. This uses the pcntl
202+
extension if possible or busy waiting if not.
123203

124204
#### MemcachedMutex
125205

@@ -240,7 +320,6 @@ $mutex->synchronized(function () use ($pdo, $accountId, $amount) {
240320
});
241321
```
242322

243-
244323
#### MySQLMutex
245324

246325
The **MySQLMutex** uses MySQL's
@@ -267,6 +346,7 @@ $mutex->synchronized(function () use ($bankAccount, $amount) {
267346
$bankAccount->setBalance($balance);
268347
});
269348
```
349+
270350
#### PgAdvisoryLockMutex
271351

272352
The **PgAdvisoryLockMutex** uses PostgreSQL's
@@ -293,9 +373,7 @@ $mutex->synchronized(function () use ($bankAccount, $amount) {
293373
});
294374
```
295375

296-
297-
298-
# License and authors
376+
## License and authors
299377

300378
This project is free and under the WTFPL.
301379
Responsible for this project is Willem Stuursma-Ruwen <[email protected]>.
@@ -305,4 +383,14 @@ Responsible for this project is Willem Stuursma-Ruwen <[email protected]>.
305383
If you like this project and feel generous donate a few Bitcoins here:
306384
[1P5FAZ4QhXCuwYPnLZdk3PJsqePbu1UDDA](bitcoin:1P5FAZ4QhXCuwYPnLZdk3PJsqePbu1UDDA)
307385

308-
386+
[1]: http://semver.org
387+
[2]: https://github.com/nrk/predis
388+
[3]: http://php.net/manual/en/book.pcntl.php
389+
[4]: https://getcomposer.org
390+
[5]: https://github.com/php-lock/lock/blob/master/classes/mutex/Mutex.php
391+
[6]: https://github.com/php-lock/lock/blob/master/classes/mutex/Mutex.php#L38
392+
[7]: https://github.com/php-lock/lock/blob/master/classes/mutex/Mutex.php#L57
393+
[8]: https://github.com/php-lock/lock/blob/master/classes/util/DoubleCheckedLocking.php#L72
394+
[9]: https://en.wikipedia.org/wiki/Double-checked_locking
395+
[10]: https://en.wikipedia.org/wiki/Compare-and-swap
396+
[11]: https://github.com/php-lock/lock/blob/master/classes/mutex/CASMutex.php#L44

classes/mutex/CASMutex.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ class CASMutex extends Mutex
3333
* @param int $timeout The timeout in seconds.
3434
* @throws \LengthException The timeout must be greater than 0.
3535
*/
36-
public function __construct($timeout = 3)
36+
public function __construct(int $timeout = 3)
3737
{
3838
$this->loop = new Loop($timeout);
3939
}
4040

4141
/**
4242
* Notifies the Mutex about a successful CAS operation.
4343
*/
44-
public function notify()
44+
public function notify(): void
4545
{
4646
$this->loop->end();
4747
}

classes/mutex/FlockMutex.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*/
2121
class FlockMutex extends LockMutex
2222
{
23-
const INFINITE_TIMEOUT = -1;
23+
public const INFINITE_TIMEOUT = -1;
2424

2525
/**
2626
* @internal
@@ -58,7 +58,7 @@ class FlockMutex extends LockMutex
5858
* @param resource $fileHandle The file handle.
5959
* @param int $timeout
6060
*/
61-
public function __construct($fileHandle, $timeout = self::INFINITE_TIMEOUT)
61+
public function __construct($fileHandle, int $timeout = self::INFINITE_TIMEOUT)
6262
{
6363
if (!is_resource($fileHandle)) {
6464
throw new \InvalidArgumentException("The file handle is not a valid resource.");
@@ -85,7 +85,7 @@ private function determineLockingStrategy()
8585
/**
8686
* @throws LockAcquireException
8787
*/
88-
private function lockBlocking()
88+
private function lockBlocking(): void
8989
{
9090
if (!flock($this->fileHandle, LOCK_EX)) {
9191
throw new LockAcquireException("Failed to lock the file.");
@@ -96,13 +96,13 @@ private function lockBlocking()
9696
* @throws LockAcquireException
9797
* @throws TimeoutException
9898
*/
99-
private function lockPcntl()
99+
private function lockPcntl(): void
100100
{
101101
$timebox = new PcntlTimeout($this->timeout);
102102

103103
try {
104104
$timebox->timeBoxed(
105-
function () {
105+
function (): void {
106106
$this->lockBlocking();
107107
}
108108
);
@@ -118,7 +118,7 @@ function () {
118118
private function lockBusy()
119119
{
120120
$loop = new Loop($this->timeout);
121-
$loop->execute(function () use ($loop) {
121+
$loop->execute(function () use ($loop): void {
122122
if ($this->acquireNonBlockingLock()) {
123123
$loop->end();
124124
}
@@ -129,7 +129,7 @@ private function lockBusy()
129129
* @return bool
130130
* @throws LockAcquireException
131131
*/
132-
private function acquireNonBlockingLock()
132+
private function acquireNonBlockingLock(): bool
133133
{
134134
if (!flock($this->fileHandle, LOCK_EX | LOCK_NB, $wouldBlock)) {
135135
if ($wouldBlock) {
@@ -147,7 +147,7 @@ private function acquireNonBlockingLock()
147147
* @throws LockAcquireException
148148
* @throws TimeoutException
149149
*/
150-
protected function lock()
150+
protected function lock(): void
151151
{
152152
switch ($this->strategy) {
153153
case self::STRATEGY_BLOCK:
@@ -167,7 +167,7 @@ protected function lock()
167167
/**
168168
* @throws LockReleaseException
169169
*/
170-
protected function unlock()
170+
protected function unlock(): void
171171
{
172172
if (!flock($this->fileHandle, LOCK_UN)) {
173173
throw new LockReleaseException("Failed to unlock the file.");

0 commit comments

Comments
 (0)