Test Failed
Push — master ( 3499c9...ef8c92 )
by Никита
01:30
created

FileMutex   C

Complexity

Total Complexity 59

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 84.67%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 59
c 1
b 0
f 0
lcom 1
cbo 1
dl 0
loc 261
ccs 116
cts 137
cp 0.8467
rs 6.1904

12 Methods

Rating   Name   Duplication   Size   Complexity  
D __construct() 0 29 9
A __destruct() 0 3 1
B getDomainString() 0 11 5
B getDirectoryString() 0 11 6
B is_free() 0 21 7
C get_lock() 0 52 15
B release_lock() 0 16 5
A get_acquired_time() 0 3 1
A get_mutex_name() 0 3 1
A get_delete_on_release() 0 3 1
A is_acquired() 0 3 1
C create_folders_in_path() 0 24 7

How to fix   Complexity   

Complex Class

Complex classes like FileMutex often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FileMutex, and based on these observations, apply Extract Interface, too.

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 1
                return true;
142
            }
143
144 3
            self::create_folders_in_path(dirname($this->filename));
145 3
            if (!is_dir(dirname($this->filename))) {
146
                throw new MutexException('Folder "'.dirname($this->filename).'" is not a folder', 4);
147 3
            } elseif (!is_writable(dirname($this->filename))) {
148
                throw new MutexException('Folder "'.dirname($this->filename).'" is not writable', 2);
149 3
            } elseif (file_exists($this->filename) and !is_writable($this->filename)) {
150
                throw new MutexException('File "'.$this->filename.'" is not writable', 3);
151
            }
152
153
            // Открываем файл
154 3
            $this->_file_handler = fopen($this->filename, file_exists($this->filename) ? 'ab' : 'wb');
155 3
            while (($this->_file_handler === false) and (
156
                    ($tmp_time + $time >= microtime(true)) or ($time == -1)
157 3
                )) {
158
                usleep(10000);
159
                $this->_file_handler = fopen($this->filename, 'ab');
160
            }
161 3
            if ($this->_file_handler === false) {
162
                return false;
163
            }
164
165
            // Блочим файл
166 3
            if ($time >= 0) {
167 3
                $result = flock($this->_file_handler, LOCK_EX | LOCK_NB);
168 3
                while (!$result and ($tmp_time + $time >= microtime(true))) {
169
                    // U MAD?
170
                    usleep(10000);
171
                    $result = flock($this->_file_handler, LOCK_EX | LOCK_NB);
172
                }
173 3
            } else {
174
                $result = flock($this->_file_handler, LOCK_EX);
175
            }
176
177 3
            if ($result) {
178 3
                $this->_lock_acquired_time = microtime(true);
179
                // @todo Не работает под Windows
180 3
                fwrite($this->_file_handler, posix_getpid()."\n".microtime(true)."\n".posix_getuid()."\n\n");
181 3
                fflush($this->_file_handler);
182 3
                $this->_lock_acquired = true;
183 3
            } else {
184
                fclose($this->_file_handler);
185
                $this->_file_handler = false;
186
            }
187
188 3
            return $result;
189
        }
190
191 4
        function release_lock() {
192 4
            if (!$this->_lock_acquired) {
193 3
                return;
194
            }
195 3
            if (is_resource($this->_file_handler)) {// @hint По неизвестным причинам это не always true condition
196 3
                flock($this->_file_handler, LOCK_UN);
197 3
                fclose($this->_file_handler);
198 3
            }
199 3
            $this->_file_handler = false;
200 3
            $this->_lock_acquired = false;
201 3
            $this->_lock_acquired_time = null;
202
203 3
            if ($this->_delete_on_release and file_exists($this->filename)) {
204 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...
205 1
            }
206 3
        }
207
208
        /**
209
         * @return double|null
210
         */
211
        function get_acquired_time() {
212
            return $this->_lock_acquired_time;
213
        }
214
215
        /**
216
         * @return string
217
         */
218 1
        function get_mutex_name() {
219 1
            return $this->_mutex_name;
220
        }
221
222
        /**
223
         * @return boolean
224
         */
225 1
        function get_delete_on_release() {
226 1
            return $this->_delete_on_release;
227
        }
228
229
        /**
230
         * @return boolean
231
         */
232 2
        function is_acquired() {
233 2
            return $this->_lock_acquired;
234
        }
235
236
        /**
237
         * @param string $path
238
         *
239
         * @throws \NokitaKaze\Mutex\MutexException
240
         */
241 10
        static function create_folders_in_path($path) {
242 10
            $path = rtrim(str_replace('\\', '/', $path), '/');
243
            do {
244 10
                $old_path = $path;
245 10
                $path = str_replace('//', '/', str_replace('/./', '/', $path));
246 10
            } while ($path != $old_path);
247
            do {
248 10
                $path = preg_replace('_/([^/]+?)/../_', '/', $path, -1, $count);
249 10
            } while ($count > 0);
250 10
            unset($old_path, $count);
251
252 10
            $chunks = explode('/', $path);
253 10
            unset($path);
254 10
            $full_path = '';
255 10
            foreach ($chunks as $chunk) {
256
                // @hint Такая логика из-за структуры файловой системы Windows
257 10
                $full_path = str_replace('//', '/', $full_path.'/'.$chunk);
258 10
                if (!file_exists($full_path) and !@mkdir($full_path)) {
259 1
                    throw new MutexException('Can not create folder');
260 10
                } elseif (!is_dir($full_path)) {
261 1
                    throw new MutexException($full_path.' is not a directory');
262
                }
263 10
            }
264 8
        }
265
    }
266
267
?>