Test Failed
Push — master ( 52ee83...9b5d7d )
by Никита
08:21
created

FileMutex::get_lock()   C

Complexity

Conditions 15
Paths 14

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 16.5238

Importance

Changes 0
Metric Value
dl 0
loc 52
ccs 30
cts 37
cp 0.8108
rs 5.9166
c 0
b 0
f 0
cc 15
nc 14
nop 1
crap 16.5238

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
    namespace NokitaKaze\Mutex;
4
5
    class FileMutex implements MutexInterface
6
    {
7
        /**
8
         * @var string Название файла, на котором будет работать мьютекс
9
         */
10
        var $filename = '';
11
        protected $_mutex_type;
12
        /**
13
         * @var string|null Название мьютекса, должно состоять из валидных для имени файлов символов
14
         */
15
        var $_mutex_name;
16
        /**
17
         * @var string|null Место, где расположен файл.
18
         * Не используется в логике класса
19
         */
20
        protected $_mutex_folder;
21
        /**
22
         * @var double|null Время, когда мьютекс был получен
23
         */
24
        protected $_lock_acquired_time = null;
25
        /**
26
         * @var boolean Текущее состояние мьютекса
27
         */
28
        protected $_lock_acquired = false;
29
        /**
30
         * @var resource|false Файл, открывающийся через fopen
31
         */
32
        protected $_file_handler = false;
33
        /**
34
         * @var boolean Удалять файл при анлоке
35
         * В данный момент это unsafe поведение, из-за немандаторного доступа к файлам из PHP
36
         */
37
        protected $_delete_on_release = false;
38
39
        /**
40
         * @param MutexSettings|array $settings
41 5
         */
42
        function __construct($settings)
43
        {
44
            /**
45 5
             * @var MutexSettings $settings
46 5
             */
47 1
            $settings = (object)$settings;
48 1
            if (!isset($settings->type)) {
49 5
                $settings->type = null;
50 1
            }
51 1
            if (!isset($settings->folder)) {
52
                $settings->folder = null;
53 5
            }
54 5
55 5
            $this->_mutex_type = is_null($settings->type) ? self::SERVER : $settings->type;
56 5
            $this->_mutex_folder = is_null($settings->folder) ? sys_get_temp_dir() : $settings->folder;
57 1
            $this->_mutex_name = $settings->name;
58 1
            if (isset($settings->delete_on_release)) {
59
                $this->_delete_on_release = $settings->delete_on_release;
60 5
            }
61 5
62 1
            $prefix = '';
63 5
            if (isset($settings->prefix)) {
64 1
                $prefix = $settings->prefix;
65 5
            } elseif ($this->_mutex_type == self::DOMAIN) {
66 1
                $prefix = hash('sha512', self::getDomainString()).'_';
67 1
            } elseif ($this->_mutex_type == self::DIRECTORY) {
68 5
                $prefix = hash('sha512', strtolower(self::getDirectoryString())).'_';
69 5
            }
70
            $this->filename = $this->_mutex_folder.DIRECTORY_SEPARATOR.'smartmutex_'.$prefix.$this->_mutex_name.'.lock';
71 5
        }
72 5
73 5
        function __destruct()
74
        {
75
            $this->release_lock();
76
        }
77
78 3
        /**
79 3
         * @return string
80 1
         */
81 3
        static function getDomainString()
82 3
        {
83
            if (isset($_SERVER['HTTP_HOST']) and ($_SERVER['HTTP_HOST'] != '')) {
84
                return $_SERVER['HTTP_HOST'];
85
            } elseif (gethostname() != '') {
86
                return gethostname();
87
            } elseif (isset($_SERVER['SCRIPT_NAME'])) {
88
                return $_SERVER['SCRIPT_NAME'];
89
            } else {
90
                return 'none';
91
            }
92
        }
93 2
94 2
        /**
95 1
         * @return string
96 2
         */
97 2
        static function getDirectoryString()
98 1
        {
99 1
            if (isset($_SERVER['DOCUMENT_ROOT']) and ($_SERVER['DOCUMENT_ROOT'] != '')) {
100
                return $_SERVER['DOCUMENT_ROOT'];
101 1
            } elseif (isset($_SERVER['PWD']) and ($_SERVER['PWD'] != '')) {
102
                return $_SERVER['PWD'];
103
            } elseif (isset($_SERVER['SCRIPT_NAME'])) {
104
                return dirname($_SERVER['SCRIPT_NAME']);
105
            } else {
106
                return 'none';
107
            }
108
        }
109
110 1
        /**
111 1
         * @return boolean
112 1
         *
113
         * @throws MutexException
114 1
         */
115 1
        function is_free()
116 1
        {
117 1
            if ($this->_lock_acquired) {
118 1
                return false;
119 1
            }
120 1
            if (!file_exists(dirname($this->filename))) {
121 1
                throw new MutexException('Folder "'.dirname($this->filename).'" does not exist', 1);
122
            } elseif (!is_dir(dirname($this->filename))) {
123
                throw new MutexException('Folder "'.dirname($this->filename).'" does not exist', 4);
124 1
            } elseif (!is_writable(dirname($this->filename))) {
125 1
                throw new MutexException('Folder "'.dirname($this->filename).'" is not writable', 2);
126 1
            } elseif (file_exists($this->filename) and !is_writable($this->filename)) {
127 1
                throw new MutexException('File "'.$this->filename.'" is not writable', 3);
128
            }
129 1
130
            $fo = fopen($this->filename, 'ab');
131
            $result = flock($fo, LOCK_EX | LOCK_NB);
132
            flock($fo, LOCK_UN);
133
            fclose($fo);
134
135
            return $result;
136
        }
137
138 4
        /**
139 4
         * @param double|integer $time
140 4
         *
141 3
         * @return bool
142
         * @throws MutexException
143
         */
144 4
        function get_lock($time = -1)
145 4
        {
146
            $tmp_time = microtime(true);
147 4
            if ($this->_lock_acquired) {
148 1
                return true;
149 4
            }
150 1
151
            self::create_folders_in_path(dirname($this->filename));
152
            if (!is_dir(dirname($this->filename))) {
153
                throw new MutexException('Folder "'.dirname($this->filename).'" is not a folder', 4);
154 4
            } elseif (!is_writable(dirname($this->filename))) {
155 4
                throw new MutexException('Folder "'.dirname($this->filename).'" is not writable', 2);
156
            } elseif (file_exists($this->filename) and !is_writable($this->filename)) {
157 4
                throw new MutexException('File "'.$this->filename.'" is not writable', 3);
158
            }
159
160
            // Открываем файл
161 4
            $this->_file_handler = fopen($this->filename, file_exists($this->filename) ? 'ab' : 'wb');
162
            while (($this->_file_handler === false) and (
163
                    ($tmp_time + $time >= microtime(true)) or ($time == -1)
164
                )) {
165
                // Active locks. Yes, this is programming language we have
166 4
                usleep(10000);
167 4
                $this->_file_handler = fopen($this->filename, 'ab');
168 4
            }
169
            if ($this->_file_handler === false) {
170 1
                return false;
171 1
            }
172 1
173 4
            // Блочим файл
174
            if ($time >= 0) {
175
                $result = flock($this->_file_handler, LOCK_EX | LOCK_NB);
176
                while (!$result and ($tmp_time + $time >= microtime(true))) {
177 4
                    usleep(10000);
178 4
                    $result = flock($this->_file_handler, LOCK_EX | LOCK_NB);
179
                }
180 4
            } else {
181 4
                $result = flock($this->_file_handler, LOCK_EX);
182 4
            }
183 4
184 1
            if ($result) {
185 1
                $this->_lock_acquired_time = microtime(true);
186
                fwrite($this->_file_handler, self::getpid()."\n".microtime(true)."\n".self::getuid()."\n\n");
187
                fflush($this->_file_handler);
188 4
                $this->_lock_acquired = true;
189
            } else {
190
                fclose($this->_file_handler);
191 5
                $this->_file_handler = false;
192 5
            }
193 5
194
            return $result;
195 4
        }
196 4
197 4
        function release_lock()
198 4
        {
199 4
            if (!$this->_lock_acquired) {
200 4
                return;
201 4
            }
202
            if (is_resource($this->_file_handler)) {// @hint По неизвестным причинам это не always true condition
203 4
                flock($this->_file_handler, LOCK_UN);
204 2
                fclose($this->_file_handler);
205 2
            }
206 4
            $this->_file_handler = false;
207
            $this->_lock_acquired = false;
208
            $this->_lock_acquired_time = null;
209
210
            if ($this->_delete_on_release and file_exists($this->filename)) {
211 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...
212 1
            }
213
        }
214
215
        /**
216
         * @return double|null
217
         */
218 1
        function get_acquired_time()
219 1
        {
220
            return $this->_lock_acquired_time;
221
        }
222
223
        /**
224
         * @return string
225 2
         */
226 2
        function get_mutex_name()
227
        {
228
            return $this->_mutex_name;
229
        }
230
231
        /**
232 4
         * @return boolean
233 4
         */
234
        function get_delete_on_release()
235
        {
236
            return $this->_delete_on_release;
237
        }
238
239 2
        /**
240 2
         * @return boolean
241 2
         */
242
        function is_acquired()
243
        {
244
            return $this->_lock_acquired;
245 2
        }
246 2
247
        /**
248
         * @return string
249
         */
250
        static function get_last_php_error_as_string()
251
        {
252
            $error = error_get_last();
253
            if (empty($error)) {
254 20
                return '';
255 20
            }
256
257 20
            return sprintf('%s%s', $error['message'],
258 20
                isset($error['code']) ? ' (#'.$error['code'].')' : '');
259 20
        }
260
261 20
        /**
262 20
         * @param string $path
263 20
         *
264
         * @return string
265 20
         */
266
        static function sanify_path($path)
267
        {
268
            $path = rtrim(str_replace('\\', '/', $path), '/').'/';
269
            do {
270
                $old_path = $path;
271
                $path = str_replace('//', '/', str_replace('/./', '/', $path));
272
            } while ($path != $old_path);
273 11
            do {
274 11
                $path = preg_replace('_/([^/]+?)/\\.\\./_', '/', $path, -1, $count);
275 11
            } while ($count > 0);
276 11
            $path = rtrim($path, '/');
277
278 11
            return $path;
279
        }
280 11
281 1
        /**
282
         * @param string $path
283 1
         *
284
         * @throws \NokitaKaze\Mutex\MutexException
285 11
         */
286 2
        static function create_folders_in_path($path)
287
        {
288 11
            $chunks = explode('/', self::sanify_path($path));
289 9
            $full_path = '';
290
            foreach ($chunks as $chunk) {
291
                $full_path = str_replace('//', '/', $full_path.'/'.$chunk);
292
                if (DIRECTORY_SEPARATOR === '\\') {
293
                    // Удаляем ведущие слеши на Windows. Не важно абсолютный путь или относительный,
294
                    // но ведущий слеш надо удалять
295
                    $full_path = ltrim($full_path, '/');
296
                }
297
                // @hint warning всё равно пойдет в error handler
298
                if (!file_exists($full_path) and !@mkdir($full_path)) {
299
                    if (!file_exists($full_path)) {
300
                        // Синхронизация, она такая, да
301
                        throw new MutexException('Can not create folder: '.self::get_last_php_error_as_string());
302
                    }
303
                } elseif (!is_dir($full_path)) {
304
                    throw new MutexException($full_path.' is not a directory');
305
                }
306
            }
307
        }
308
309
        static function getpid()
310
        {
311
            return function_exists('posix_getpid') ? posix_getpid() : getmypid();
312
        }
313
314
        static function getuid()
315
        {
316
            return function_exists('posix_getuid') ? posix_getuid() : getmyuid();
317
        }
318
    }
319
320
?>