14c9c01c451d4e7525df30d678d236ebcf7b1d66
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Locking / FileLockStrategy.php
1 <?php
2 namespace TYPO3\CMS\Core\Locking;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Locking\Exception\LockAcquireException;
18 use TYPO3\CMS\Core\Locking\Exception\LockAcquireWouldBlockException;
19 use TYPO3\CMS\Core\Locking\Exception\LockCreateException;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * flock() locking
24 *
25 * @author Markus Klein <klein.t3@reelworx.at>
26 */
27 class FileLockStrategy implements LockingStrategyInterface {
28
29 const FILE_LOCK_FOLDER = 'typo3temp/locks/';
30
31 /**
32 * @var resource File pointer if using flock method
33 */
34 protected $filePointer;
35
36 /**
37 * @var string File used for locking
38 */
39 protected $filePath;
40
41 /**
42 * @var bool True if lock is acquired
43 */
44 protected $isAcquired = FALSE;
45
46 /**
47 * @param string $subject ID to identify this lock in the system
48 * @throws LockCreateException if the lock could not be created
49 */
50 public function __construct($subject) {
51 /*
52 * Tests if the directory for simple locks is available.
53 * If not, the directory will be created. The lock path is usually
54 * below typo3temp, typo3temp itself should exist already
55 */
56 $path = PATH_site . self::FILE_LOCK_FOLDER;
57 if (!is_dir($path)) {
58 // Not using mkdir_deep on purpose here, if typo3temp itself
59 // does not exist, this issue should be solved on a different
60 // level of the application.
61 if (!GeneralUtility::mkdir($path)) {
62 throw new LockCreateException('Cannot create directory ' . $path, 1395140007);
63 }
64 }
65 if (!is_writable($path)) {
66 throw new LockCreateException('Cannot write to directory ' . $path, 1396278700);
67 }
68 $this->filePath = $path . md5((string)$subject);
69 }
70
71 /**
72 * Destructor:
73 * Releases lock automatically when instance is destroyed and release resources
74 */
75 public function __destruct() {
76 $this->release();
77 }
78
79 /**
80 * Try to acquire an exclusive lock
81 *
82 * @param int $mode LOCK_CAPABILITY_EXCLUSIVE or LOCK_CAPABILITY_SHARED or self::LOCK_CAPABILITY_NOBLOCK
83 * @return bool Returns TRUE if the lock was acquired successfully
84 * @throws LockAcquireException if the lock could not be acquired
85 * @throws LockAcquireWouldBlockException if the acquire would have blocked and NOBLOCK was set
86 */
87 public function acquire($mode = self::LOCK_CAPABILITY_EXCLUSIVE) {
88 if ($this->isAcquired) {
89 return TRUE;
90 }
91
92 $this->filePointer = fopen($this->filePath, 'c');
93 if ($this->filePointer === FALSE) {
94 throw new LockAcquireException('Lock file could not be opened', 1294586099);
95 }
96
97 $operation = $mode & self::LOCK_CAPABILITY_EXCLUSIVE ? LOCK_EX : LOCK_SH;
98 if ($mode & self::LOCK_CAPABILITY_NOBLOCK) {
99 $operation |= LOCK_NB;
100 }
101
102 $wouldBlock = 0;
103 $this->isAcquired = flock($this->filePointer, $operation, $wouldBlock);
104
105 if ($mode & self::LOCK_CAPABILITY_NOBLOCK && !$this->isAcquired && $wouldBlock) {
106 throw new LockAcquireWouldBlockException('Failed to acquire lock because the request would block.', 1428700748);
107 }
108
109 return $this->isAcquired;
110 }
111
112 /**
113 * Release the lock
114 *
115 * @return bool Returns TRUE on success or FALSE on failure
116 */
117 public function release() {
118 if (!$this->isAcquired) {
119 return TRUE;
120 }
121 $success = TRUE;
122 if (is_resource($this->filePointer)) {
123 if (flock($this->filePointer, LOCK_UN) === FALSE) {
124 $success = FALSE;
125 }
126 fclose($this->filePointer);
127 }
128 $this->isAcquired = FALSE;
129 return $success;
130 }
131
132 /**
133 * Get status of this lock
134 *
135 * @return bool Returns TRUE if lock is acquired by this locker, FALSE otherwise
136 */
137 public function isAcquired() {
138 return $this->isAcquired;
139 }
140
141 /**
142 * @return int Returns a priority for the method. 0 to 100, 100 is highest
143 */
144 static public function getPriority() {
145 return 50;
146 }
147
148 /**
149 * @return int LOCK_CAPABILITY_* elements combined with bit-wise OR
150 */
151 static public function getCapabilities() {
152 if (PHP_SAPI === 'isapi') {
153 // From php docs: When using a multithreaded server API like ISAPI you may not be able to rely on flock()
154 // to protect files against other PHP scripts running in parallel threads of the same server instance!
155 return 0;
156 }
157 $capabilities = self::LOCK_CAPABILITY_EXCLUSIVE | self::LOCK_CAPABILITY_SHARED | self::LOCK_CAPABILITY_NOBLOCK;
158 return $capabilities;
159 }
160
161 }