[FEATURE] Use dynamic path for typo3temp/var/
[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\Core\Environment;
18 use TYPO3\CMS\Core\Locking\Exception\LockAcquireException;
19 use TYPO3\CMS\Core\Locking\Exception\LockAcquireWouldBlockException;
20 use TYPO3\CMS\Core\Locking\Exception\LockCreateException;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23 /**
24 * flock() locking
25 */
26 class FileLockStrategy implements LockingStrategyInterface
27 {
28 const FILE_LOCK_FOLDER = 'lock/';
29
30 /**
31 * @var resource File pointer if using flock method
32 */
33 protected $filePointer;
34
35 /**
36 * @var string File used for locking
37 */
38 protected $filePath;
39
40 /**
41 * @var bool True if lock is acquired
42 */
43 protected $isAcquired = false;
44
45 /**
46 * @param string $subject ID to identify this lock in the system
47 * @throws LockCreateException if the lock could not be created
48 */
49 public function __construct($subject)
50 {
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/var, typo3temp/var itself should exist already (or root-path/var/ respectively)
55 */
56 $path = Environment::getVarPath() . '/' . 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 . 'flock_' . 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 {
77 $this->release();
78 }
79
80 /**
81 * Try to acquire an exclusive lock
82 *
83 * @param int $mode LOCK_CAPABILITY_EXCLUSIVE or LOCK_CAPABILITY_SHARED or self::LOCK_CAPABILITY_NOBLOCK
84 * @return bool Returns TRUE if the lock was acquired successfully
85 * @throws LockAcquireException if the lock could not be acquired
86 * @throws LockAcquireWouldBlockException if the acquire would have blocked and NOBLOCK was set
87 */
88 public function acquire($mode = self::LOCK_CAPABILITY_EXCLUSIVE)
89 {
90 if ($this->isAcquired) {
91 return true;
92 }
93
94 $this->filePointer = fopen($this->filePath, 'c');
95 if ($this->filePointer === false) {
96 throw new LockAcquireException('Lock file could not be opened', 1294586099);
97 }
98 GeneralUtility::fixPermissions($this->filePath);
99
100 $operation = $mode & self::LOCK_CAPABILITY_EXCLUSIVE ? LOCK_EX : LOCK_SH;
101 if ($mode & self::LOCK_CAPABILITY_NOBLOCK) {
102 $operation |= LOCK_NB;
103 }
104
105 $wouldBlock = 0;
106 $this->isAcquired = flock($this->filePointer, $operation, $wouldBlock);
107
108 if ($mode & self::LOCK_CAPABILITY_NOBLOCK && !$this->isAcquired && $wouldBlock) {
109 throw new LockAcquireWouldBlockException('Failed to acquire lock because the request would block.', 1428700748);
110 }
111
112 return $this->isAcquired;
113 }
114
115 /**
116 * Release the lock
117 *
118 * @return bool Returns TRUE on success or FALSE on failure
119 */
120 public function release()
121 {
122 if (!$this->isAcquired) {
123 return true;
124 }
125 $success = true;
126 if (is_resource($this->filePointer)) {
127 if (flock($this->filePointer, LOCK_UN) === false) {
128 $success = false;
129 }
130 fclose($this->filePointer);
131 }
132 $this->isAcquired = false;
133 return $success;
134 }
135
136 /**
137 * Get status of this lock
138 *
139 * @return bool Returns TRUE if lock is acquired by this locker, FALSE otherwise
140 */
141 public function isAcquired()
142 {
143 return $this->isAcquired;
144 }
145
146 /**
147 * @return int Returns a priority for the method. 0 to 100, 100 is highest
148 */
149 public static function getPriority()
150 {
151 return 75;
152 }
153
154 /**
155 * @return int LOCK_CAPABILITY_* elements combined with bit-wise OR
156 */
157 public static function getCapabilities()
158 {
159 if (PHP_SAPI === 'isapi') {
160 // From php docs: When using a multi-threaded server API like ISAPI you may not be able to rely on flock()
161 // to protect files against other PHP scripts running in parallel threads of the same server instance!
162 return 0;
163 }
164 $capabilities = self::LOCK_CAPABILITY_EXCLUSIVE | self::LOCK_CAPABILITY_SHARED | self::LOCK_CAPABILITY_NOBLOCK;
165
166 return $capabilities;
167 }
168
169 /**
170 * Destroys the resource associated with the lock
171 */
172 public function destroy()
173 {
174 @unlink($this->filePath);
175 }
176 }