Completed
Push — developer ( dc3c7c...6e5def )
by Никита
15:38
created

FileDBQueueTransport::is_equal_to()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.0488

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 8
cp 0.875
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 8
nc 5
nop 1
crap 5.0488
1
<?php
2
3
    namespace NokitaKaze\Queue;
4
5
    use NokitaKaze\Mutex\FileMutex;
6
7
    class FileDBQueueTransport extends AbstractQueueTransport {
8
        const FILE_DB_INDEX_VERSION = 1;
9
        const FILE_DB_CHUNK_VERSION = 1;
10
11
        protected $_folder;
12
        protected $_mutex_folder;
13
        protected $_prefix;
14
        protected $_name;
15
        protected $_db_file_count = Queue::DefaultDBFileCount;
16
        /**
17
         * @var FileDBQueueConstructionSettings|object
18
         */
19
        protected $_construction_settings;
20
21
        /**
22
         * @var FileMutex|null
23
         */
24
        protected $_producer_mutex = null;
25
26
        /**
27
         * @var FileMutex|null
28
         */
29
        protected $_index_mutex = null;
30
31
        /**
32
         * @var integer[][] Данные с индексом
33
         */
34
        protected $_index_data = [];
35
36
        /**
37
         * @var FileDBIndexFile
38
         */
39
        protected $_index_data_full = null;
40
41
        /**
42
         * @var boolean Эксклюзивный режим
43
         */
44
        protected $_exclusive_mode = false;
45
46
        /**
47
         * @param FileDBQueueConstructionSettings|object $settings
48
         *
49
         * @throws QueueException
50
         */
51 345
        function __construct($settings) {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
52 345
            if (!isset($settings->storage_type)) {
53 342
                $settings->storage_type = Queue::StorageTemporary;
54 85
            }
55 345
            if (!isset($settings->name)) {
56 3
                throw new QueueException('Settings don\'t have field name', 11);
57
            }
58 342
            $this->_name = $settings->name;
59 342
            $this->_construction_settings = $settings;
60
61 342
            $this->set_folder_settings($settings);
62 339
            if (isset($settings->prefix)) {
63 3
                $this->_prefix = $settings->prefix;
64 1
            } else {
65 336
                $this->_prefix = '';
66
            }
67 339
            if (isset($settings->mutex_folder)) {
68 21
                $this->_mutex_folder = $settings->mutex_folder;
69 7
            } else {
70 321
                $this->_mutex_folder = $this->_folder.'/mutex';
71
            }
72 339
            if (isset($settings->db_file_count)) {
73 3
                $this->_db_file_count = $settings->db_file_count;
74 1
            }
75 339
            $this->_index_data = array_fill(0, $this->_db_file_count, []);
76 339
        }
77
78 2457
        function __destruct() {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
79 2457
            if (!is_null($this->_producer_mutex)) {
80 2433
                $this->_producer_mutex->release_lock();
81 782
            }
82 2457
            if (!is_null($this->_index_mutex)) {
83 2433
                $this->_index_mutex->release_lock();
84 782
            }
85 2457
        }
86
87 2148
        function __clone() {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
88 2148
            parent::__clone();
89 2148
            $this->_construction_settings = clone $this->_construction_settings;
90 2148
            if (isset($this->_producer_mutex)) {
91
                $this->_producer_mutex = clone $this->_producer_mutex;
92
            }
93 2148
            if (isset($this->_index_mutex)) {
94
                $this->_index_mutex = clone $this->_producer_mutex;
95
            }
96 2148
            if (isset($this->_index_data_full)) {
97
                $this->_index_data_full = clone $this->_index_data_full;
98
            }
99 2148
        }
100
101
        /**
102
         * @param FileDBQueueConstructionSettings|object $settings
103
         *
104
         * @throws QueueException
105
         */
106 342
        protected function set_folder_settings($settings) {
107 342
            switch ($settings->storage_type) {
108 342
                case Queue::StorageTemporary:
109 339
                    $this->_folder = sys_get_temp_dir();
110 339
                    break;
111 6
                case Queue::StoragePersistent:
112 3
                    $this->_folder = FileMutex::getDirectoryString();
113 3
                    break;
114 1
                default:
115 3
                    throw new QueueException(
116
                        'Constructor settings is malformed. Storage type can not be equal '.
117 3
                        $settings->storage_type, 1
118 1
                    );
119 85
            }
120 339
            if (isset($settings->folder)) {
121 339
                $this->_folder = $settings->folder;
122 84
            }
123 339
        }
124
125
        /**
126
         * Блокируем index mutex
127
         *
128
         * @param double|integer $time
129
         *
130
         * @return boolean
131
         */
132 2430
        protected function index_mutex_lock($time = -1) {
133 2430
            if (!$this->_exclusive_mode) {
134 2430
                return $this->_index_mutex->get_lock($time);
135
            } else {
136 115
                return true;
137
            }
138
        }
139
140
        /**
141
         * Блокируем index mutex
142
         */
143 2430
        protected function index_mutex_release_lock() {
144 2430
            if (!$this->_exclusive_mode) {
145 2430
                $this->_index_mutex->release_lock();
146 781
            }
147 2430
        }
148
149
        /**
150
         * Устанавливаем или снимаем монопольный режим
151
         *
152
         * @param boolean $mode
153
         */
154 118
        function set_exclusive_mode($mode) {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
155 118
            $this->init_producer_mutex();
156 118
            $this->_exclusive_mode = $mode;
157 118
            if ($mode) {
158 118
                $this->_index_mutex->get_lock();
159 26
            } else {
160 3
                $this->_index_mutex->release_lock();
161
            }
162 118
        }
163
164
        /**
165
         * @return iMessage[]|object[]
166
         */
167 2430
        protected function get_used_keys_and_real_need_for_save() {
168 2430
            $real_need_save = [];
169 2430
            $used_keys = [];
170 2430
            foreach ($this->_pushed_for_save as &$message) {
171 2430
                if (is_null($message->name)) {
172 2359
                    $real_need_save[] = $message;
173 2359
                    continue;
174
                }
175 2026
                if (in_array($message->name, $used_keys)) {
176 3
                    continue;
177
                }
178 2026
                foreach ($this->_index_data as $sort_id => &$index_datum) {
179 2026
                    if (array_key_exists($message->name, $index_datum)) {
180 1379
                        continue 2;
181
                    }
182 648
                }
183 2026
                $used_keys[] = $message->name;
184 2026
                $real_need_save[] = $message;
185 781
            }
186
187 2430
            return $real_need_save;
188
        }
189
190
        /**
191
         * Сохраняем все сообщения, подготовленные для сохранения, в файл сообщений
192
         *
193
         * @throws QueueException
194
         */
195 2433
        function save() {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
196 2433
            if (count($this->_pushed_for_save) == 0) {
197 9
                return;
198
            }
199 2433
            $this->init_producer_mutex();
200
201 2433
            $this->index_mutex_lock();
202 2433
            $this->index_data_load();
203 2433
            $real_need_save = $this->get_used_keys_and_real_need_for_save();
204 2433
            if (count($real_need_save) == 0) {
205 3
                $this->index_mutex_release_lock();
206
207 3
                return;
208
            }
209
210 2433
            $this->_producer_mutex->get_lock();
211 2433
            $current_thread_id = static::get_current_producer_thread_id();
212 2433
            $data_in = $this->get_data_for_thread($current_thread_id);
213 2433
            foreach ($real_need_save as &$message) {
214 2433
                if (!isset($message->sort)) {
215 3
                    $message->sort = 5;
216 1
                }
217
                // Копируем уже существующий message в data_in
218 2433
                $data_in[] = (object) [
219 2433
                    'name' => $message->name,
220 2433
                    'data' => $message->data,
221 2433
                    'time_created' => $message->time_created,
222 2433
                    'time_last_update' => microtime(true),
223 2433
                    'time_rnd_postfix' => isset($message->time_rnd_postfix) ? $message->time_rnd_postfix : null,
224 2433
                    'sort' => $message->sort,
225 782
                    'is_read' => false,
226
                    // @codeCoverageIgnoreStart
227
                ];
228
                // @codeCoverageIgnoreEnd
229 2433
                $key = self::get_real_key_for_message($message);
230 2433
                $this->_index_data[$message->sort][$key] = $current_thread_id;
231 782
            }
232 2433
            $this->write_full_data_to_file($current_thread_id, $data_in);
233 2433
            unset($data_in);
234
235 2433
            $this->_producer_mutex->release_lock();
236 2433
            $this->index_data_save();
237 2433
            $this->index_mutex_release_lock();
238 2433
            $this->_pushed_for_save = [];
239 2433
        }
240
241
        /**
242
         * Берём данные для конкретного внутренного треда в очереди сообщений
243
         *
244
         * @param integer $thread_id
245
         *
246
         * @return iMessage[]|object[]
247
         * @throws QueueException
248
         */
249 2430
        protected function get_data_for_thread($thread_id) {
250 2430
            $filename = $this->get_producer_filename_for_thread($thread_id);
251 2430
            if (!file_exists($filename)) {
252 2430
                return [];
253
            }
254 2418
            if (!is_readable($filename)) {
255
                throw new QueueException('Chunk DB File "'.$filename.'" is not readable/writable');
256
            }
257 2418
            $buf = file_get_contents($filename, LOCK_EX);
258 2418
            if (empty($buf)) {
259
                throw new QueueException('Chunk DB File "'.$filename.'" is malformed');
260
            }
261
            /**
262
             * @var FileDBChunkFile $object
263
             */
264 2418
            $object = unserialize($buf);
265 2418
            if (!is_object($object)) {
266
                throw new QueueException('Chunk DB File "'.$filename.'" is malformed');
267
            }
268 2418
            if ($object->version != self::FILE_DB_CHUNK_VERSION) {
0 ignored issues
show
Bug introduced by
Accessing version on the interface NokitaKaze\Queue\FileDBChunkFile suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
269
                throw new QueueException('Version mismatch ('.
270
                                         $object->version.' instead of '.self::FILE_DB_CHUNK_VERSION.')');
0 ignored issues
show
Bug introduced by
Accessing version on the interface NokitaKaze\Queue\FileDBChunkFile suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
271
            }
272
273 2418
            return $object->queue;
0 ignored issues
show
Bug introduced by
Accessing queue on the interface NokitaKaze\Queue\FileDBChunkFile suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
274
        }
275
276
        /**
277
         * Внутренний id треда
278
         *
279
         * @return integer
280
         */
281 1565
        protected static function get_current_producer_thread_id() {
282 1565
            return posix_getpid() % Queue::ProducerThreadCount;
283
        }
284
285
        /**
286
         * Берём название файла с данными для конкретного внутренного треда в очереди сообщений
287
         *
288
         * @param integer $thread_id
289
         *
290
         * @return string
291
         */
292 2430
        protected function get_producer_filename_for_thread($thread_id) {
293 2430
            return $this->_folder.'/smartqueue_'.$this->_prefix.'_'.hash('sha512', $this->_name).'-'.$thread_id.'.que';
294
        }
295
296
        /**
297
         * Инициализируем мьютекс для текущего треда
298
         */
299 2433
        protected function init_producer_mutex() {
300 2433
            if (is_null($this->_producer_mutex)) {
301 2433
                $this->_producer_mutex = $this->get_mutex_for_thread(static::get_current_producer_thread_id());
302 2433
                $this->_index_mutex = $this->get_index_mutex();
303 782
            }
304 2433
        }
305
306
        /**
307
         * @return string
308
         */
309 2433
        function get_mutex_folder() {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
310 2433
            return $this->_mutex_folder;
311
        }
312
313
        /**
314
         * Мьютекс для конкретного треда
315
         *
316
         * @param integer $thread_id
317
         *
318
         * @return FileMutex
319
         */
320 2436
        protected function get_mutex_for_thread($thread_id) {
321
            // @todo впилить сюда mutex resolver
322
            /**
323
             * @var \NokitaKaze\Mutex\MutexSettings $settings
324
             */
325 2436
            $settings = (object) [];
326 2436
            $settings->folder = $this->get_mutex_folder();
0 ignored issues
show
Bug introduced by
Accessing folder on the interface NokitaKaze\Mutex\MutexSettings suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
327 2436
            FileMutex::create_folders_in_path($settings->folder);
0 ignored issues
show
Bug introduced by
Accessing folder on the interface NokitaKaze\Mutex\MutexSettings suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
328 2436
            $settings->name = 'smartqueue_'.$this->_prefix.'_'.hash('sha512', $this->_name).'-'.$thread_id;
0 ignored issues
show
Bug introduced by
Accessing name on the interface NokitaKaze\Mutex\MutexSettings suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
329
330 2436
            return new FileMutex($settings);
331
        }
332
333
        /**
334
         * Создание мьютекса для файла с индексом на всю текущую очередь сообщений
335
         *
336
         * @return FileMutex
337
         */
338 2436
        protected function get_index_mutex() {
339
            // @todo впилить сюда mutex resolver
340
            /**
341
             * @var \NokitaKaze\Mutex\MutexSettings $settings
342
             */
343 2436
            $settings = (object) [];
344 2436
            $settings->folder = $this->get_mutex_folder();
0 ignored issues
show
Bug introduced by
Accessing folder on the interface NokitaKaze\Mutex\MutexSettings suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
345 2436
            FileMutex::create_folders_in_path($settings->folder);
0 ignored issues
show
Bug introduced by
Accessing folder on the interface NokitaKaze\Mutex\MutexSettings suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
346 2436
            $settings->name = 'smartqueue_'.$this->_prefix.'_'.hash('sha512', $this->_name).'-index';
0 ignored issues
show
Bug introduced by
Accessing name on the interface NokitaKaze\Mutex\MutexSettings suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
347
348 2436
            return new FileMutex($settings);
349
        }
350
351
        /**
352
         * Сохраняем данные в файл внутреннего треда (без индекса)
353
         *
354
         * @param integer             $thread_id
355
         * @param iMessage[]|object[] $data_in
356
         *
357
         * @throws QueueException
358
         */
359 2433
        protected function write_full_data_to_file($thread_id, $data_in) {
360 2433
            $filename = $this->get_producer_filename_for_thread($thread_id);
361 2433
            if (!file_exists(dirname($filename))) {
362 3
                throw new QueueException('Folder "'.dirname($filename).'" does not exist', 7);
363 2433
            } elseif (!is_dir(dirname($filename))) {
364 3
                throw new QueueException('Folder "'.dirname($filename).'" is not a folder', 14);
365 2433
            } elseif (!is_writable(dirname($filename))) {
366 3
                throw new QueueException('Folder "'.dirname($filename).'" is not writable', 8);
367 2433
            } elseif (file_exists($filename) and !is_writable($filename)) {
368 3
                throw new QueueException('File "'.$filename.'" is not writable', 9);
369
            }
370 2430
            $filename_tmp = $filename.'-'.mt_rand(10000, 99999).'.tmp';
371 2430
            touch($filename_tmp);
372
            // @todo fileperms
373 2430
            chmod($filename_tmp, 6 << 6);
374
375
            /**
376
             * @var FileDBChunkFile $object
377
             */
378 2430
            $object = (object) [];
379 2430
            $object->version = self::FILE_DB_CHUNK_VERSION;
0 ignored issues
show
Bug introduced by
Accessing version on the interface NokitaKaze\Queue\FileDBChunkFile suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
380 2430
            $object->time_last_update = microtime(true);
0 ignored issues
show
Bug introduced by
Accessing time_last_update on the interface NokitaKaze\Queue\FileDBChunkFile suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
381 2430
            $object->queue_name = $this->get_queue_name();
0 ignored issues
show
Bug introduced by
Accessing queue_name on the interface NokitaKaze\Queue\FileDBChunkFile suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
382 2430
            $object->queue = $data_in;
0 ignored issues
show
Bug introduced by
Accessing queue on the interface NokitaKaze\Queue\FileDBChunkFile suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
383 2430
            if (@file_put_contents($filename_tmp, serialize($object), LOCK_EX) === false) {
384
                // @codeCoverageIgnoreStart
385
                throw new QueueException('Can not save queue stream', 2);
386
                // @codeCoverageIgnoreEnd
387
            }
388 2430
            if (!rename($filename_tmp, $filename)) {
389
                // @codeCoverageIgnoreStart
390
                throw new QueueException('Can not rename temporary chunk database file', 15);
391
                // @codeCoverageIgnoreEnd
392
            }
393 2430
        }
394
395
        /**
396
         * Получаем номера DB, в которых лежат сообщения, которые нам надо удалить
397
         *
398
         * @param iMessage[]|object[] $messages
399
         *
400
         * @return string[][]|integer[][]
401
         */
402 2202
        protected function get_keys_for_delete_group_by_thread($messages) {
403 2202
            $keys = [];
404 2202
            foreach ($messages as &$message) {
405 2202
                $keys[] = self::get_real_key_for_message($message);
406 705
            }
407 2202
            unset($message);
408
409 2202
            $threads_keys = array_fill(0, Queue::ProducerThreadCount, []);
410 2202
            foreach ($this->_index_data as $sort_id => &$index_datum) {
411 2202
                if (empty($index_datum)) {
412 2202
                    continue;
413
                }
414 2202
                foreach ($index_datum as $key => &$thread_id) {
415 2202
                    if (in_array($key, $keys)) {
416 2202
                        $threads_keys[$thread_id][] = $key;
417 2202
                        unset($index_datum[$key]);
418 705
                    }
419 705
                }
420 705
            }
421
422 2202
            return $threads_keys;
423
        }
424
425
        /**
426
         * Удалить сообщения и сразу же записать это в БД
427
         *
428
         * @param iMessage[]|object[] $messages
429
         *
430
         * @return string[]|integer[]
431
         */
432 2298
        function delete_messages(array $messages) {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
433 2298
            $this->init_producer_mutex();
434 2298
            $this->index_mutex_lock();
435 2298
            $this->index_data_load();
436
437 2298
            $threads_keys = $this->get_keys_for_delete_group_by_thread($messages);
438
439 2298
            $deleted_keys = [];
440 2298
            foreach ($threads_keys as $thread_id => &$thread_keys) {
441 2298
                if (count($thread_keys) == 0) {
442 2298
                    continue;
443
                }
444 2244
                $mutex = $this->get_mutex_for_thread($thread_id);
445 2244
                $mutex->get_lock();
446 2244
                $data_in = $this->get_data_for_thread($thread_id);
447 2244
                $u = false;
448 2244
                foreach ($data_in as $inner_id => &$inner_message) {
449 2244
                    $real_key = self::get_real_key_for_message($inner_message);
450 2244
                    if (in_array($real_key, $thread_keys)) {
451 2244
                        unset($data_in[$inner_id]);
452 2244
                        $deleted_keys[] = $real_key;
453 2244
                        $u = true;
454 723
                    }
455 723
                }
456 2244
                if ($u) {
457
                    // @hint Это always true condition. Иначе данные неконсистентны
458 2244
                    if (count($data_in) > 0) {
459 1566
                        $this->write_full_data_to_file($thread_id, $data_in);
460 493
                    } else {
461 2238
                        unlink($this->get_producer_filename_for_thread($thread_id));
462
                    }
463 723
                }
464
465 2244
                $mutex->release_lock();
466 2244
                $this->index_data_save();
467 737
            }
468 2298
            $this->index_mutex_release_lock();
469
470 2298
            return $deleted_keys;
471
        }
472
473
        /**
474
         * Обновляем сообщение и сразу же сохраняем всё
475
         *
476
         * Эта функция не рейзит ошибку, если сообщение не найдено
477
         *
478
         * @param iMessage|object $message
479
         * @param string|null     $key форсированно задаём ключ сообщения
480
         *
481
         * @return boolean
482
         */
483 109
        function update_message($message, $key = null) {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
484 109
            $this->init_producer_mutex();
485 109
            $this->index_mutex_lock();
486 109
            $this->index_data_load();
487
488 109
            if (is_null($key)) {
489 109
                $key = self::get_real_key_for_message($message);
490 26
            }
491 109
            $exists = false;
492 109
            foreach ($this->_index_data as $index_id => &$index_datum) {
493 109
                if (!array_key_exists($key, $index_datum)) {
494 109
                    continue;
495
                }
496 109
                $thread_id = $index_datum[$key];
497 109
                $mutex = $this->get_mutex_for_thread($thread_id);
498 109
                $mutex->get_lock();
499 109
                $data_in = $this->get_data_for_thread($thread_id);
500
                // Ищем то же сообщение и заменяем его
501 109
                $u = $this->change_message_in_array($data_in, $message, $key);
502 109
                if ($u) {
503 109
                    $exists = true;
504 26
                }
505 109
                $this->write_full_data_to_file($thread_id, $data_in);
506 109
                $mutex->release_lock();
507 109
                $this->index_data_save();
508 109
                break;
509 26
            }
510 109
            $this->index_mutex_release_lock();
511
512 109
            return $exists;
513
        }
514
515
        /**
516
         * @param double|integer $wait_time
517
         *
518
         * @return iMessage|object|null
519
         */
520 2433
        function consume_next_message($wait_time = -1) {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
521 2433
            $this->set_same_time_flag(2);
522 2433
            $start = microtime(true);
523 2433
            $this->init_producer_mutex();
524 2433
            $this->index_mutex_lock();
525 2433
            $this->index_data_load();
526 2433
            while (true) {
527 2433
                for ($sort_id = 0; $sort_id < $this->_db_file_count; $sort_id++) {
528 2433
                    foreach ($this->_index_data[$sort_id] as $key => $thread_id) {
529 2421
                        if (isset($this->_consumed_keys[$key])) {
530 2321
                            continue;
531
                        }
532 2421
                        $mutex = $this->get_mutex_for_thread($thread_id);
533 2421
                        $mutex->get_lock();
534 2421
                        $data_in = $this->get_data_for_thread($thread_id);
535 2421
                        $this->_consumed_keys[$key] = 1;
536 2421
                        foreach ($data_in as $message) {
537 2418
                            $this_key = self::get_real_key_for_message($message);
538 2418
                            if ($this_key == $key) {
539 2418
                                $message->time_consumed = microtime(true);
540 2418
                                $message->thread_consumed = $thread_id;
541 2418
                                $message->queue = $this;
542 2418
                                $mutex->release_lock();
543 2418
                                $this->index_mutex_release_lock();
544
545 2418
                                return $message;
546
                            }
547 708
                        }
548
                        // @hint Сюда может передаться код только в случае неконсистентности БД, когда в
549
                        // index'е ключ есть, а в data его нет
550 3
                        unset($mutex);
551 782
                    }
552 782
                }
553 2409
                $this->index_mutex_release_lock();
554 2409
                if (($wait_time !== -1) and ($start + $wait_time < microtime(true))) {
555 2409
                    return null;
556
                }
557 6
                $this->index_mutex_lock();
558 6
                $this->index_data_load();
559 2
            }
560
561
            // @hint Это для IDE
562
            // @codeCoverageIgnoreStart
563
            return null;
564
            // @codeCoverageIgnoreEnd
565
        }
566
567
        /**
568
         * @return string
569
         */
570 2439
        function get_queue_name() {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
571 2439
            return $this->_name;
572
        }
573
574
        /**
575
         * Поддерживает ли очередь сортировку event'ов при вставке
576
         *
577
         * @return boolean
578
         */
579 6
        static function is_support_sorted_events() {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
580 6
            return true;
581
        }
582
583
        /**
584
         * Индексы
585
         */
586
587
        /**
588
         * Название файла для содержания даты
589
         *
590
         * @return string
591
         */
592 2433
        protected function get_index_filename() {
593 2433
            return $this->_folder.'/smartqueue_'.$this->_prefix.'_'.hash('sha512', $this->_name).'-index.que';
594
        }
595
596
        /**
597
         * @throws QueueException
598
         */
599 2430
        protected function index_data_load() {
600
            // @todo Проверять время-размер
601 2430
            $filename = $this->get_index_filename();
602 2430
            if (!file_exists($filename)) {
603 2430
                $this->_index_data = array_fill(0, $this->_db_file_count, []);
604
605 2430
                return;
606
            }
607 2418
            if (!is_readable($filename) or !is_writable($filename)) {
608
                throw new QueueException('Index File "'.$filename.'" is not readable/writable');
609
            }
610 2418
            $buf = file_get_contents($filename);
611 2418
            if (empty($buf)) {
612
                throw new QueueException('Index File "'.$filename.'" is empty');
613
            }
614 2418
            $this->_index_data_full = unserialize($buf);
615 2418
            if (!is_object($this->_index_data_full)) {
616
                throw new QueueException('Index File "'.$filename.'" is empty');
617
            }
618 2418
            if ($this->_index_data_full->version != self::FILE_DB_INDEX_VERSION) {
619
                throw new QueueException('Version mismatch ('.
620
                                         $this->_index_data_full->version.' instead of '.self::FILE_DB_INDEX_VERSION.')');
621
            }
622 2418
            $this->_index_data = $this->_index_data_full->data;
623 2418
            unset($this->_index_data_full->data);
624 2418
        }
625
626
        /**
627
         * Сохраняем данные в индекс
628
         *
629
         * @throws QueueException
630
         */
631 2433
        protected function index_data_save() {
632 2433
            $filename = $this->get_index_filename();
633 2433
            if (!file_exists(dirname($filename))) {
634 3
                throw new QueueException('Folder "'.dirname($filename).'" does not exist', 4);
635 2433
            } elseif (!is_dir(dirname($filename))) {
636 3
                throw new QueueException('Folder "'.dirname($filename).'" is not a folder', 13);
637 2433
            } elseif (!is_writable(dirname($filename))) {
638 3
                throw new QueueException('Folder "'.dirname($filename).'" is not writable', 5);
639 2433
            } elseif (file_exists($filename) and !is_writable($filename)) {
640 3
                throw new QueueException('File "'.$filename.'" is not writable', 6);
641
            }
642 2430
            $filename_tmp = $filename.'-'.mt_rand(0, 50000).'.tmp';
643 2430
            touch($filename_tmp);
644 2430
            chmod($filename_tmp, 6 << 6);
645
            // @todo fileperms
646
647 2430
            if (is_null($this->_index_data_full)) {
648 2430
                $this->_index_data_full = (object) [];
649 2430
                $this->_index_data_full->time_create = microtime(true);
650 781
            }
651 2430
            $this->_index_data_full->version = self::FILE_DB_INDEX_VERSION;
652 2430
            $this->_index_data_full->time_last_update = microtime(true);
653 2430
            $this->_index_data_full->queue_name = $this->get_queue_name();
654 2430
            $temporary_data = clone $this->_index_data_full;
655 2430
            $temporary_data->data = $this->_index_data;
656
657 2430
            if (@file_put_contents($filename_tmp, serialize($temporary_data)) === false) {
658
                // @codeCoverageIgnoreStart
659
                throw new QueueException('Can not save index file', 10);
660
                // @codeCoverageIgnoreEnd
661
            }
662 2430
            if (!rename($filename_tmp, $filename)) {
663
                // @codeCoverageIgnoreStart
664
                throw new QueueException('Can not rename temporary index file', 16);
665
                // @codeCoverageIgnoreEnd
666
            }
667 2430
        }
668
669
        /**
670
         * @param FileDBQueueTransport $queue
671
         *
672
         * @return boolean
673
         */
674 750
        function is_equal_to($queue) {
1 ignored issue
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
675 750
            if (spl_object_hash($this) == spl_object_hash($queue)) {
676 3
                return true;
677
            }
678 750
            if (!parent::is_equal_to($queue)) {
679
                return false;
680
            }
681
682 750
            return (($queue->_folder == $this->_folder) and
683 750
                    ($queue->get_queue_name() == $this->get_queue_name()) and
684 750
                    ($queue->_prefix == $this->_prefix));
685
        }
686
    }
687
688
?>