Completed
Push — phpscpec ( f31341...75ace1 )
by Anatoliy
04:42
created

File::createPidFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 0
cts 7
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
crap 6
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Created by PhpStorm.
5
 * User: danchukas
6
 * Date: 2017-07-24 14:08
7
 */
8
9
namespace DanchukAS\DenyMultiplyRun;
10
11
use DanchukAS\DenyMultiplyRun\Exception\CloseFileFail;
12
use DanchukAS\DenyMultiplyRun\Exception\DeleteFileFail;
13
use DanchukAS\DenyMultiplyRun\Exception\FileExisted;
14
use DanchukAS\DenyMultiplyRun\Exception\LockFileFail;
15
use DanchukAS\DenyMultiplyRun\Exception\OpenFileFail;
16
17
/**
18
 * Class File
19
 * @package DanchukAS\DenyMultiplyRun
20
 */
21
class File
22
{
23
    /**
24
     * @param $file_resource
25
     *
26
     * @throws \Exception
27
     */
28
    public static function lockPidFile($file_resource)
29
    {
30
        $locked = flock($file_resource, LOCK_EX | LOCK_NB);
31
32
        if (false === $locked) {
33
            $error = ErrorHandler::$lastError;
34
35
            // перехоплювач на 1 команду, щоб в разі потреби потім дізнатись причину несправності.
36
            // помилку в записує в ErrorHandler::$lastError
37
            ErrorHandler::startErrorHandle();
38
39
            // собачка потрібна щоб не засоряти логи.
40
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
41
            $resource_data = @stream_get_meta_data($file_resource);
42
43
            // Відновлюєм попередній обробник наче нічого і не робили.
44
            restore_error_handler();
45
46
            throw new LockFileFail($resource_data['uri'] . ' - ' . $error);
47
        }
48
    }
49
50
    /**
51
     * @param $file_resource
52
     * @throws \DanchukAS\DenyMultiplyRun\Exception\CloseFileFail
53
     */
54
    public static function safeCloseFile($file_resource)
55
    {
56
        try {
57
            self::unlockFile($file_resource);
58
        } finally {
59
            self::closeFile($file_resource);
60
        }
61
    }
62
63
    /**
64
     * @param $pidFileResource
65
     */
66
    public static function unlockFile($pidFileResource)
67
    {
68
        $unlocked = flock($pidFileResource, LOCK_UN | LOCK_NB);
69
        if (false === $unlocked) {
70
            trigger_error('не вдось розблокувати pid-файл.');
71
        }
72
    }
73
74
75
    /**
76
     * @param $pidFileResource
77
     *
78
     * @throws CloseFileFail
79
     */
80
    public static function closeFile($pidFileResource)
81
    {
82
        // перехоплювач на 1 команду, щоб в разі потреби потім дізнатись причину несправності.
83
        // помилку в записує в ErrorHandler::$lastError
84
        ErrorHandler::startErrorHandle();
85
86
        try {
87
            // собачка потрібна щоб не засоряти логи.
88
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
89
            $closed = @fclose($pidFileResource);
90
        } catch (\Throwable $error) {
91
            $closed = false;
92
            ErrorHandler::$lastError = $error;
0 ignored issues
show
Documentation Bug introduced by
$error is of type Throwable, but the property $lastError was declared to be of type LogicException. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
93
        } finally {
94
            // Відновлюєм попередній обробник наче нічого і не робили.
95
            restore_error_handler();
96
        }
97
98
        if (false === $closed) {
99
            self::closeFileFailed($pidFileResource);
100
        }
101
    }
102
103
104
    /**
105
     * @param $pidFileResource
106
     * @throws CloseFileFail
107
     */
108
    private static function closeFileFailed($pidFileResource)
109
    {
110
        $file_close_error = ErrorHandler::$lastError;
111
112
        // перехоплювач на 1 команду, щоб в разі потреби потім дізнатись причину несправності.
113
        // помилку в записує в ErrorHandler::$lastError
114
        ErrorHandler::startErrorHandle();
115
116
        try {
117
            // собачка потрібна щоб не засоряти логи.
118
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
119
            $resource_data = @stream_get_meta_data($pidFileResource);
120
        } catch (\Throwable $error) {
121
            $resource_data = ['uri' => ''];
122
        } finally {
123
            // Відновлюєм попередній обробник наче нічого і не робили.
124
            restore_error_handler();
125
        }
126
127
        throw new CloseFileFail($resource_data['uri'], 457575, $file_close_error);
128
    }
129
130
131
    /**
132
     * Відключає встановлену заборону паралельного запуска у яких спільний $pidFilePath
133
     * @todo добавити перевірку що цей файл ще для цього процеса,
134
     * може цей файл вже був видалений вручну, і створений іншим процесом.
135
     *
136
     * @param string $filePath
137
     *
138
     * @throws DeleteFileFail
139
     */
140
    public static function deleteFile($filePath)
141
    {
142
        // перехоплювач на 1 команду, щоб в разі потреби потім дізнатись причину несправності.
143
        // помилку в записує в ErrorHandler::$lastError
144
        ErrorHandler::startErrorHandle();
145
146
        try {
147
            // собачка потрібна щоб не засоряти логи.
148
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
149
            @unlink($filePath);
150
        } catch (\Throwable $error) {
151
            ErrorHandler::$lastError = $error;
0 ignored issues
show
Documentation Bug introduced by
$error is of type Throwable, but the property $lastError was declared to be of type LogicException. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
152
        } finally {
153
            // Відновлюєм попередній обробник наче нічого і не робили.
154
            restore_error_handler();
155
        }
156
157
        if (null !== ErrorHandler::$lastError) {
158
            throw new DeleteFileFail(ErrorHandler::$lastError);
159
        }
160
    }
161
162
163
    /**
164
     * @param string $pidFilePath
165
     * @param string $mode
166
     * @return resource
167
     */
168
    public static function openFile($pidFilePath, string $mode)
169
    {
170
        ErrorHandler::startErrorHandle();
171
        try {
172
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
173
            $pid_file_handle = @fopen($pidFilePath, $mode);
174
        } catch (\Throwable $error) {
175
            ErrorHandler::$lastError = $error;
0 ignored issues
show
Documentation Bug introduced by
$error is of type Throwable, but the property $lastError was declared to be of type LogicException. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
176
        } finally {
177
            restore_error_handler();
178
        }
179
180
        if (null !== ErrorHandler::$lastError) {
181
            throw new OpenFileFail((string)ErrorHandler::$lastError);
182
        }
183
184
        /** @noinspection PhpUndefinedVariableInspection */
185
        return $pid_file_handle;
186
    }
187
188
189
    /**
190
     * @param string $filePath
191
     *
192
     * @throws \Exception
193
     */
194
    public static function prepareDir($filePath)
195
    {
196
        $dir = \dirname($filePath);
197
198
        /** @noinspection MkdirRaceConditionInspection */
199
        if ('' !== $dir
200
            && !\is_dir($dir)
201
            && !\mkdir($dir, 0777, true)
202
        ) {
203
            throw new \RuntimeException('Директорія відсутня і неможливо створити: ' . $dir);
204
        }
205
    }
206
207
208
    /**
209
     * @param string $pidFilePath
210
     * @return resource
211
     * @throws \Throwable
212
     * @throws FileExisted
213
     * @throws \Exception
214
     */
215
    public static function createPidFile($pidFilePath)
216
    {
217
        // перехоплювач на 1 команду, щоб в разі потреби потім дізнатись причину несправності.
218
        // помилку в записує в ErrorHandler::$lastError
219
        ErrorHandler::startErrorHandle();
220
221
        // собачка потрібна щоб не засоряти логи.
222
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
223
        $pid_file_handle = @fopen($pidFilePath, 'xb');
224
225
        // Відновлюєм попередній обробник наче нічого і не робили.
226
        restore_error_handler();
227
228
        // файл не створений. сталась помилка
229
        if (null !== ErrorHandler::$lastError) {
230
            self::createPidFileFailed($pidFilePath);
231
        }
232
233
        // файл створений успішно.
234
        return $pid_file_handle;
235
    }
236
237
238
    /**
239
     * @param $pidFilePath
240
     * @throws FileExisted
241
     * @throws \Throwable
242
     */
243
    private static function createPidFileFailed($pidFilePath)
244
    {
245
        // Файла і нема і не створився - повідомляєм про несправність проекта.
246
        if (!\is_file($pidFilePath)) {
247
            throw ErrorHandler::$lastError;
248
        }
249
250
        // Файл вже існує, тому не створився.
251
        throw new FileExisted($pidFilePath);
252
    }
253
254
255
    /**
256
     * @param $fileResource
257
     *
258
     * @throws \Exception
259
     */
260
    public static function truncateFile($fileResource)
261
    {
262
        $truncated = ftruncate($fileResource, 0);
263
        if (false === $truncated) {
264
            throw new \RuntimeException('не вдалось очистити pid-файл.');
265
        }
266
267
        $cursor_to_begin = rewind($fileResource);
268
        if (!$cursor_to_begin) {
269
            throw new \RuntimeException('не вдалось перемістити курсор на початок pid-файла.');
270
        }
271
    }
272
273
}