Passed
Push — master ( 777bbe...3499c9 )
by Никита
02:30
created

FileMutex::get_delete_on_release()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
    namespace NokitaKaze\Mutex;
4
5
    class FileMutex implements MutexInterface {
6
        /**
7
         * @var string Название файла, на котором будет работать мьютекс
8
         */
9
        var $filename = '';
10
        protected $_mutex_type;
11
        /**
12
         * @var string|null Название мьютекса, должно состоять из валидных для имени файлов символов
13
         */
14
        var $_mutex_name;
15
        /**
16
         * @var string|null Место, где расположен файл.
17
         * Не используется в логике класса
18
         */
19
        protected $_mutex_folder;
20
        /**
21
         * @var double|null Время, когда мьютекс был получен
22
         */
23
        protected $_lock_acquired_time = null;
24
        /**
25
         * @var boolean Текущее состояние мьютекса
26
         */
27
        protected $_lock_acquired = false;
28
        /**
29
         * @var resource|false Файл, открывающийся через fopen
30
         */
31
        protected $_file_handler = false;
32
        /**
33
         * @var boolean Удалять файл при анлоке
34
         * В данный момент это unsafe поведение, из-за немандаторного доступа к файлам из PHP
35
         */
36
        protected $_delete_on_release = false;
37
38
        /**
39
         * @param MutexSettings|array $settings
40
         */
41 4
        function __construct($settings) {
42
            /**
43
             * @var MutexSettings $settings
44
             */
45 4
            $settings = (object) $settings;
46 4
            if (!isset($settings->type)) {
47 1
                $settings->type = null;
48 1
            }
49 4
            if (!isset($settings->folder)) {
50 1
                $settings->folder = null;
51 1
            }
52
53 4
            $this->_mutex_type = is_null($settings->type) ? self::SERVER : $settings->type;
54 4
            $this->_mutex_folder = is_null($settings->folder) ? sys_get_temp_dir() : $settings->folder;
55 4
            $this->_mutex_name = $settings->name;
56 4
            if (isset($settings->delete_on_release)) {
57
                $this->_delete_on_release = $settings->delete_on_release;
58
            }
59
60 4
            $prefix = '';
61 4
            if (isset($settings->prefix)) {
62 1
                $prefix = $settings->prefix;
63 4
            } elseif ($this->_mutex_type == self::DOMAIN) {
64 1
                $prefix = hash('sha512', self::getDomainString()).'_';
65 4
            } elseif ($this->_mutex_type == self::DIRECTORY) {
66 1
                $prefix = hash('sha512', strtolower(self::getDirectoryString())).'_';
67 1
            }
68 4
            $this->filename = $this->_mutex_folder.DIRECTORY_SEPARATOR.'smartmutex_'.$prefix.$this->_mutex_name.'.lock';
69 4
        }
70
71 4
        function __destruct() {
72 4
            $this->release_lock();
73 4
        }
74
75
        /**
76
         * @return string
77
         */
78 3
        static function getDomainString() {
79 3
            if (isset($_SERVER['HTTP_HOST']) and ($_SERVER['HTTP_HOST'] != '')) {
80 1
                return $_SERVER['HTTP_HOST'];
81 3
            } elseif (gethostname() != '') {
82 3
                return gethostname();
83
            } elseif (isset($_SERVER['SCRIPT_NAME'])) {
84
                return $_SERVER['SCRIPT_NAME'];
85
            } else {
86
                return 'none';
87
            }
88
        }
89
90
        /**
91
         * @return string
92
         */
93 2
        static function getDirectoryString() {
94 2
            if (isset($_SERVER['DOCUMENT_ROOT']) and ($_SERVER['DOCUMENT_ROOT'] != '')) {
95 1
                return $_SERVER['DOCUMENT_ROOT'];
96 2
            } elseif (isset($_SERVER['PWD']) and ($_SERVER['PWD'] != '')) {
97 2
                return $_SERVER['PWD'];
98 1
            } elseif (isset($_SERVER['SCRIPT_NAME'])) {
99 1
                return dirname($_SERVER['SCRIPT_NAME']);
100
            } else {
101 1
                return 'none';
102
            }
103
        }
104
105
        /**
106
         * @return boolean
107
         *
108
         * @throws MutexException
109
         */
110 1
        function is_free() {
111 1
            if ($this->_lock_acquired) {
112 1
                return false;
113
            }
114 1
            if (!file_exists(dirname($this->filename))) {
115 1
                throw new MutexException('Folder "'.dirname($this->filename).'" does not exist', 1);
116 1
            } elseif (!is_dir(dirname($this->filename))) {
117 1
                throw new MutexException('Folder "'.dirname($this->filename).'" does not exist', 4);
118 1
            } elseif (!is_writable(dirname($this->filename))) {
119 1
                throw new MutexException('Folder "'.dirname($this->filename).'" is not writable', 2);
120 1
            } elseif (file_exists($this->filename) and !is_writable($this->filename)) {
121 1
                throw new MutexException('File "'.$this->filename.'" is not writable', 3);
122
            }
123
124 1
            $fo = fopen($this->filename, 'ab');
125 1
            $result = flock($fo, LOCK_EX | LOCK_NB);
126 1
            flock($fo, LOCK_UN);
127 1
            fclose($fo);
128
129 1
            return $result;
130
        }
131
132
        /**
133
         * @param double|integer $time
134
         *
135
         * @return bool
136
         * @throws MutexException
137
         */
138 3
        function get_lock($time = -1) {
139 3
            $tmp_time = microtime(true);
140 3
            if ($this->_lock_acquired) {
141 2
                return true;
142
            }
143
144 3
            if (!file_exists(dirname($this->filename))) {
145 1
                throw new MutexException('Folder "'.dirname($this->filename).'" does not exist', 1);
146 3
            } elseif (!is_dir(dirname($this->filename))) {
147 1
                throw new MutexException('Folder "'.dirname($this->filename).'" is not a folder', 4);
148 3
            } elseif (!is_writable(dirname($this->filename))) {
149 1
                throw new MutexException('Folder "'.dirname($this->filename).'" is not writable', 2);
150 3
            } elseif (file_exists($this->filename) and !is_writable($this->filename)) {
151 1
                throw new MutexException('File "'.$this->filename.'" is not writable', 3);
152
            }
153
154
            // Открываем файл
155 3
            $this->_file_handler = fopen($this->filename, file_exists($this->filename) ? 'ab' : 'wb');
156 3
            while (($this->_file_handler === false) and (
157
                    ($tmp_time + $time >= microtime(true)) or ($time == -1)
158 3
                )) {
159
                usleep(10000);
160
                $this->_file_handler = fopen($this->filename, 'ab');
161
            }
162 3
            if ($this->_file_handler === false) {
163
                return false;
164
            }
165
166
            // Блочим файл
167 3
            if ($time >= 0) {
168 3
                $result = flock($this->_file_handler, LOCK_EX | LOCK_NB);
169 3
                while (!$result and ($tmp_time + $time >= microtime(true))) {
170
                    // U MAD?
171 1
                    usleep(10000);
172 1
                    $result = flock($this->_file_handler, LOCK_EX | LOCK_NB);
173 1
                }
174 3
            } else {
175
                $result = flock($this->_file_handler, LOCK_EX);
176
            }
177
178 3
            if ($result) {
179 3
                $this->_lock_acquired_time = microtime(true);
180
                // @todo Не работает под Windows
181 3
                fwrite($this->_file_handler, posix_getpid()."\n".microtime(true)."\n".posix_getuid()."\n\n");
182 3
                fflush($this->_file_handler);
183 3
                $this->_lock_acquired = true;
184 3
            } else {
185 1
                fclose($this->_file_handler);
186 1
                $this->_file_handler = false;
187
            }
188
189 3
            return $result;
190
        }
191
192 4
        function release_lock() {
193 4
            if (!$this->_lock_acquired) {
194 4
                return;
195
            }
196 3
            if (is_resource($this->_file_handler)) {// @hint По неизвестным причинам это не always true condition
197 3
                flock($this->_file_handler, LOCK_UN);
198 3
                fclose($this->_file_handler);
199 3
            }
200 3
            $this->_file_handler = false;
201 3
            $this->_lock_acquired = false;
202 3
            $this->_lock_acquired_time = null;
203
204 3
            if ($this->_delete_on_release and file_exists($this->filename)) {
205 1
                @unlink($this->filename);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
206 1
            }
207 3
        }
208
209
        /**
210
         * @return double|null
211
         */
212 1
        function get_acquired_time() {
213 1
            return $this->_lock_acquired_time;
214
        }
215
216
        /**
217
         * @return string
218
         */
219 1
        function get_mutex_name() {
220 1
            return $this->_mutex_name;
221
        }
222
223
        /**
224
         * @return boolean
225
         */
226 1
        function get_delete_on_release() {
227 1
            return $this->_delete_on_release;
228
        }
229
230
        /**
231
         * @return boolean
232
         */
233 3
        function is_acquired() {
234 3
            return $this->_lock_acquired;
235
        }
236
237
        /**
238
         * @param string $path
239
         *
240
         * @throws \NokitaKaze\Mutex\MutexException
241
         */
242 7
        static function create_folders_in_path($path) {
243 7
            $path = rtrim(str_replace('\\', '/', $path), '/');
244
            do {
245 7
                $old_path = $path;
246 7
                $path = str_replace('//', '/', str_replace('/./', '/', $path));
247 7
            } while ($path != $old_path);
248
            do {
249 7
                $path = preg_replace('_/([^/]+?)/../_', '/', $path, -1, $count);
250 7
            } while ($count > 0);
251 7
            unset($old_path, $count);
252
253 7
            $chunks = explode('/', $path);
254 7
            unset($path);
255 7
            $full_path = '';
256 7
            foreach ($chunks as $chunk) {
257
                // @hint Такая логика из-за структуры файловой системы Windows
258 7
                $full_path = str_replace('//', '/', $full_path.'/'.$chunk);
259 7
                if (!file_exists($full_path) and !@mkdir($full_path)) {
260 1
                    throw new MutexException('Can not create folder');
261 7
                } elseif (!is_dir($full_path)) {
262 1
                    throw new MutexException($full_path.' is not a directory');
263
                }
264 7
            }
265 5
        }
266
    }
267
268
?>