PluginProbe
Groups – Memberships and Access Control / 4.7.0
Groups – Memberships and Access Control v4.7.0
4.7.1 4.7.0 4.6.0 4.5.0 4.4.0 4.3.0 trunk 1.0.0-beta-1 1.0.0-beta-2 1.0.0-beta-3 1.0.0-beta-3b 1.0.0-beta-3c 1.0.0-beta-3d 1.1.4 1.1.5 1.10.0 1.10.1 1.10.2 1.10.3 1.11.0 1.11.1 1.11.2 1.11.3 1.12.0 1.13.0 All 131 releases
groups / lib / core / class-groups-lock.php

class-groups-lock.php in Groups – Memberships and Access Control 4.7.0, at lib/core/class-groups-lock.php

315 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class-groups-lock.php
4 *
5 * Copyright (c) "kento" Karim Rahimpur www.itthinx.com
6 *
7 * This code is released under the GNU General Public License.
8 * See COPYRIGHT.txt and LICENSE.txt.
9 *
10 * This code is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * This header and all notices must be kept intact.
16 *
17 * @author Karim Rahimpur
18 * @package groups
19 * @since groups 4.0.0
20 */
21
22 if ( !defined( 'ABSPATH' ) ) {
23 exit;
24 }
25
26 //phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fclose, WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.Security.EscapeOutput.ExceptionNotEscaped
27
28 /**
29 * Lock.
30 */
31 class Groups_Lock {
32
33 /**
34 * @var int
35 */
36 const TIMEOUT_DEFAULT = 1000000;
37
38 /**
39 * @var int
40 */
41 const SLEEP_DEFAULT = 10000;
42
43 /**
44 * @var int
45 */
46 const AUX_TIMEOUT = 2;
47
48 /**
49 * Lockfile path.
50 *
51 * @var string
52 */
53 private $path = null;
54
55 /**
56 * Lock file pointer.
57 *
58 * @var resource
59 */
60 private $h = null;
61
62 /**
63 * Timeout microseconds.
64 *
65 * @var int
66 */
67 private $timeout = self::TIMEOUT_DEFAULT;
68
69 /**
70 * Sleep microseconds.
71 *
72 * @var integer
73 */
74 private $sleep = self::SLEEP_DEFAULT;
75
76 /**
77 * Create an instance.
78 *
79 * @throws Groups_Lock_Exception
80 *
81 * @param string $name lock name, generic lock used by default
82 * @param int $timeout lock timeout in microseconds, default used when null, 0 is blocking
83 */
84 public function __construct( $name = null, $timeout = null ) {
85 if (
86 function_exists( 'fopen' ) &&
87 function_exists( 'flock' ) &&
88 function_exists( 'fclose' ) &&
89 function_exists( 'is_readable' )
90 ) {
91
92 if ( $name !== null ) {
93 $name = trim( sanitize_key( $name ) );
94 } else {
95 $name = 'groups-lock';
96 }
97 $blog_id = intval( get_current_blog_id() );
98 $path = sprintf(
99 '%s%s.%s-%d',
100 untrailingslashit( WP_CONTENT_DIR ),
101 DIRECTORY_SEPARATOR,
102 $name,
103 $blog_id
104 );
105 /**
106 * Modify the lock file path.
107 *
108 * @param string $path
109 * @param string $name
110 * @param int $blog_id
111 *
112 * @return string
113 */
114 $path = apply_filters( 'groups_lock_path', $path, $name, $blog_id );
115
116 if ( $timeout !== null && is_numeric( $timeout ) ) {
117 $this->timeout = max( 0, intval( $timeout ) );
118 }
119
120 /**
121 * Modify the lock timeout (microseconds).
122 *
123 * @param int $timeout
124 * @param string $name
125 * @param int $blog_id
126 *
127 * @return int
128 */
129 $this->timeout = apply_filters( 'groups_lock_timeout', $this->timeout, $name, $blog_id );
130 if ( is_numeric( $this->timeout ) ) {
131 $this->timeout = max( 0, intval( $this->timeout ) );
132 } else {
133 $this->timeout = 0;
134 }
135
136 /**
137 * Modify the lock sleep gap (microseconds).
138 *
139 * @param int $sleep
140 * @param string $name
141 * @param int $blog_id
142 *
143 * @return int
144 */
145 $this->sleep = apply_filters( 'groups_lock_sleep', $this->sleep, $name, $blog_id );
146 if ( is_numeric( $this->sleep ) ) {
147 $this->sleep = max( 1, intval( $this->sleep ) );
148 } else {
149 $this->sleep = self::SLEEP_DEFAULT;
150 }
151
152 if ( is_string( $path ) ) {
153 $h = @fopen( $path, 'c' );
154 if ( $h !== false ) {
155 $this->path = $path;
156 @fclose( $h );
157 } else {
158 $error = error_get_last();
159 $msg = $error !== null && !empty( $error['message'] ) ? $error['message'] : '';
160 if ( !file_exists( $path ) ) {
161 $error = sprintf( 'Failed to create lock %s [%s]', $path, $msg );
162 } else {
163 $error = sprintf( 'Failed to access lock %s [%s]', $path, $msg );
164 }
165 throw new Groups_Lock_Exception( $error );
166 }
167 }
168 register_shutdown_function( array( $this, 'release' ) );
169 } else {
170 throw new Groups_Lock_Exception( 'Lock requires fopen, flock, fclose, is_readable' );
171 }
172 }
173
174 /**
175 * Lock is usable.
176 *
177 * @return boolean
178 */
179 public function is_usable() {
180 return $this->path !== null && is_readable( $this->path );
181 }
182
183 /**
184 * Lock is locked.
185 *
186 * @since 4.3.0
187 *
188 * @return boolean
189 */
190 public function is_locked() {
191 return $this->h !== null;
192 }
193
194 /**
195 * Write lock.
196 *
197 * @throws Groups_Lock_Exception
198 *
199 * @return boolean
200 */
201 public function writer() {
202 if ( $this->path !== null ) {
203 if ( $this->h === null ) {
204 $h = @fopen( $this->path, 'r+' );
205 if ( $h !== false ) {
206 if ( $this->timeout === 0 ) {
207 // blocking
208 if ( @flock( $h, LOCK_EX ) ) {
209 $this->h = $h;
210 }
211 } else {
212 // non-blocking
213 if ( function_exists( 'microtime' ) && function_exists( 'usleep' ) ) {
214 $t = microtime( true ) * 1000000;
215 while ( $this->h === null && ( microtime( true ) * 1000000 - $t < $this->timeout ) ) {
216 if ( @flock( $h, LOCK_EX | LOCK_NB ) ) {
217 $this->h = $h;
218 break;
219 } else {
220 usleep( $this->sleep );
221 }
222 }
223 } else {
224 // fallback if required functions missing
225 $t = time();
226 while ( $this->h === null && ( time() - $t < self::AUX_TIMEOUT ) ) {
227 if ( @flock( $h, LOCK_EX | LOCK_NB ) ) {
228 $this->h = $h;
229 break;
230 } else {
231 sleep( 1 );
232 }
233 }
234 }
235 }
236 if ( $this->h === null ) {
237 throw new Groups_Lock_Exception( sprintf( 'Failed to obtain write lock %s', $this->path ) );
238 }
239 } else {
240 throw new Groups_Lock_Exception( sprintf( 'Failed to access write lock %s ', $this->path ) );
241 }
242 }
243 }
244 return $this->h !== null;
245 }
246
247 /**
248 * Read lock.
249 *
250 * @throws Groups_Lock_Exception
251 *
252 * @return boolean
253 */
254 public function reader() {
255 if ( $this->path !== null ) {
256 if ( $this->h === null ) {
257 $h = @fopen( $this->path, 'r' );
258 if ( $h !== false ) {
259 if ( $this->timeout === 0 ) {
260 // blocking
261 if ( @flock( $h, LOCK_SH ) ) {
262 $this->h = $h;
263 }
264 } else {
265 // non
266 if ( function_exists( 'microtime' ) && function_exists( 'usleep' ) ) {
267 $t = microtime( true ) * 1000000;
268 while ( $this->h === null && ( microtime( true ) * 1000000 - $t < $this->timeout ) ) {
269 if ( @flock( $h, LOCK_SH | LOCK_NB ) ) {
270 $this->h = $h;
271 break;
272 } else {
273 usleep( $this->sleep );
274 }
275 }
276 } else {
277 // fallback
278 $t = time();
279 while ( $this->h === null && ( time() - $t < self::AUX_TIMEOUT ) ) {
280 if ( @flock( $h, LOCK_SH | LOCK_NB ) ) {
281 $this->h = $h;
282 break;
283 } else {
284 sleep( 1 );
285 }
286 }
287 }
288 }
289 if ( $this->h === null ) {
290 throw new Groups_Lock_Exception( sprintf( 'Failed to obtain read lock %s', $this->path ) );
291 }
292 } else {
293 throw new Groups_Lock_Exception( sprintf( 'Failed to access read lock %s', $this->path ) );
294 }
295 }
296 }
297 return $this->h !== null;
298 }
299
300 /**
301 * Release lock.
302 *
303 * @return boolean
304 */
305 public function release() {
306 $released = false;
307 if ( $this->h !== null ) {
308 $released = @flock( $this->h, LOCK_UN );
309 @fclose( $this->h );
310 $this->h = null;
311 }
312 return $released;
313 }
314 }
315