Completed
Push — master ( b5c840...3d5461 )
by Marcus
04:46
created

PidFileHandler::unlock()   B

Complexity

Conditions 7
Paths 21

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 25
c 0
b 0
f 0
ccs 0
cts 19
cp 0
rs 8.5866
cc 7
nc 21
nop 0
crap 56
1
<?php
2
3
/**
4
 * TechDivision\Import\Handlers\PidFileHandler
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2020 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Handlers;
22
23
use TechDivision\Import\Configuration\ConfigurationInterface;
24
use TechDivision\Import\Exceptions\LineNotFoundException;
25
use TechDivision\Import\Exceptions\FileNotFoundException;
26
use TechDivision\Import\Exceptions\ImportAlreadyRunningException;
27
28
/**
29
 * A PID file handler implementation.
30
 *
31
 * @author    Tim Wagner <[email protected]>
32
 * @copyright 2020 TechDivision GmbH <[email protected]>
33
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
 * @link      https://github.com/techdivision/import
35
 * @link      http://www.techdivision.com
36
 */
37
class PidFileHandler implements PidFileHandlerInterface
38
{
39
40
    /**
41
     * The filehandle for the PID file.
42
     *
43
     * @var resource
44
     */
45
    private $fh;
46
47
    /**
48
     * The PID for the running processes.
49
     *
50
     * @var array
51
     */
52
    private $pid;
53
54
    /**
55
     * The system configuration.
56
     *
57
     * @var \TechDivision\Import\Configuration\ConfigurationInterface
58
     */
59
    private $configuration;
60
61
    /**
62
     * The generic file handler instance.
63
     *
64
     * @var \TechDivision\Import\Handlers\GenericFileHandlerInterface
65
     */
66
    private $genericFileHandler;
67
68
    /**
69
     * Initializes the file handler instance.
70
     *
71
     * @param \TechDivision\Import\Configuration\ConfigurationInterface $configuration      The actual configuration instance
72
     * @param \TechDivision\Import\Handlers\GenericFileHandlerInterface $genericFileHandler The actual file handler instance
73
     */
74
    public function __construct(
75
        ConfigurationInterface $configuration,
76
        GenericFileHandlerInterface $genericFileHandler
77
    ) {
78
        $this->configuration = $configuration;
79
        $this->genericFileHandler = $genericFileHandler;
80
    }
81
82
    /**
83
     * Return's the system configuration.
84
     *
85
     * @return \TechDivision\Import\Configuration\ConfigurationInterface The system configuration
86
     */
87
    protected function getConfiguration()
88
    {
89
        return $this->configuration;
90
    }
91
92
    /**
93
     * Return's the generic file handler instance.
94
     *
95
     * @return \TechDivision\Import\Handlers\GenericFileHandlerInterface The generic file handler instance
96
     */
97
    protected function getGenericFileHandler()
98
    {
99
        return $this->genericFileHandler;
100
    }
101
102
    /**
103
     * Return's the PID filename to use.
104
     *
105
     * @return string The PID filename
106
     */
107
    protected function getPidFilename()
108
    {
109
        return $this->getConfiguration()->getPidFilename();
110
    }
111
112
    /**
113
     * Return's the unique serial for this import process.
114
     *
115
     * @return string The unique serial
116
     */
117
    protected function getSerial()
118
    {
119
        return $this->getConfiguration()->getSerial();
120
    }
121
122
    /**
123
     * Remove's the passed line from the file with the passed name.
124
     *
125
     * @param string   $line The line to be removed
126
     * @param resource $fh   The file handle of the file the line has to be removed
127
     *
128
     * @return void
129
     * @throws \Exception Is thrown, if the file doesn't exists, the line is not found or can not be removed
130
     */
131
    protected function removeLineFromFile($line, $fh)
132
    {
133
        return $this->getGenericFileHandler()->removeLineFromFile($line, $fh);
134
    }
135
136
    /**
137
     * Persist the UUID of the actual import process to the PID file.
138
     *
139
     * @return void
140
     * @throws \Exception Is thrown, if the PID can not be locked or the PID can not be added
141
     * @throws \TechDivision\Import\Exceptions\ImportAlreadyRunningException Is thrown, if a import process is already running
142
     */
143
    public function lock()
144
    {
145
146
        // query whether or not, the PID has already been set
147
        if ($this->pid === $this->getSerial()) {
148
            return;
149
        }
150
151
        // if not, initialize the PID
152
        $this->pid = $this->getSerial();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getSerial() of type string is incompatible with the declared type array of property $pid.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
153
154
        // open the PID file
155
        $this->fh = fopen($filename = $this->getPidFilename(), 'a+');
156
157
        // try to lock the PID file exclusive
158
        if (!flock($this->fh, LOCK_EX|LOCK_NB)) {
159
            throw new ImportAlreadyRunningException(sprintf('PID file %s is already in use', $filename));
160
        }
161
162
        // append the PID to the PID file
163
        if (fwrite($this->fh, $this->pid . PHP_EOL) === false) {
164
            throw new \Exception(sprintf('Can\'t write PID %s to PID file %s', $this->pid, $filename));
165
        }
166
    }
167
168
    /**
169
     * Remove's the UUID of the actual import process from the PID file.
170
     *
171
     * @return void
172
     * @throws \Exception Is thrown, if the PID can not be removed
173
     */
174
    public function unlock()
175
    {
176
        try {
177
            // remove the PID from the PID file if set
178
            if ($this->pid === $this->getSerial() && is_resource($this->fh)) {
179
                // remove the PID from the file
180
                $this->removeLineFromFile($this->pid, $this->fh);
181
182
                // finally unlock/close the PID file
183
                flock($this->fh, LOCK_UN);
184
                fclose($this->fh);
185
186
                // if the PID file is empty, delete the file
187
                if (filesize($filename = $this->getPidFilename()) === 0) {
188
                    unlink($filename);
189
                }
190
            }
191
        } catch (FileNotFoundException $fnfe) {
192
            $this->getSystemLogger()->notice(sprintf('PID file %s doesn\'t exist', $this->getPidFilename()));
0 ignored issues
show
Bug introduced by
The method getSystemLogger() does not seem to exist on object<TechDivision\Impo...andlers\PidFileHandler>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
193
        } catch (LineNotFoundException $lnfe) {
194
            $this->getSystemLogger()->notice(sprintf('PID %s is can not be found in PID file %s', $this->pid, $this->getPidFilename()));
0 ignored issues
show
Bug introduced by
The method getSystemLogger() does not seem to exist on object<TechDivision\Impo...andlers\PidFileHandler>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
195
        } catch (\Exception $e) {
196
            throw new \Exception(sprintf('Can\'t remove PID %s from PID file %s', $this->pid, $this->getPidFilename()), null, $e);
197
        }
198
    }
199
}
200