Parser   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 234
Duplicated Lines 9.4 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 24
c 0
b 0
f 0
lcom 1
cbo 6
dl 22
loc 234
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A getSource() 0 8 2
A setSource() 0 4 1
A getDataDirectory() 0 8 2
A setDataDirectory() 0 13 1
B checkDirectory() 0 11 5
A isDirectoryReadable() 0 4 1
A isDirectoryWritable() 0 4 1
A setPropertyFilter() 0 11 1
A getDataVersionHash() 0 13 1
A getReader() 11 16 4
A getWriter() 11 16 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
declare(strict_types=1);
3
4
namespace Crossjoin\Browscap\Parser\Sqlite;
5
6
use Crossjoin\Browscap\Exception\InvalidArgumentException;
7
use Crossjoin\Browscap\Exception\ParserConditionNotSatisfiedException;
8
use Crossjoin\Browscap\Exception\ParserConfigurationException;
9
use Crossjoin\Browscap\Exception\UnexpectedValueException;
10
use Crossjoin\Browscap\Parser\ParserInterface;
11
use Crossjoin\Browscap\Parser\ReaderInterface;
12
use Crossjoin\Browscap\Parser\WriterInterface;
13
use Crossjoin\Browscap\PropertyFilter\PropertyFilterInterface;
14
use Crossjoin\Browscap\PropertyFilter\PropertyFilterTrait;
15
use Crossjoin\Browscap\Source\SourceFactory;
16
use Crossjoin\Browscap\Source\SourceInterface;
17
18
/**
19
 * Class Parser
20
 *
21
 * @package Crossjoin\Browscap\Parser\Sqlite
22
 * @author Christoph Ziegenberg <[email protected]>
23
 * @link https://github.com/crossjoin/browscap
24
 */
25
class Parser implements ParserInterface
26
{
27
    use PropertyFilterTrait;
28
29
    const SUB_DIRECTORY = 'browscap' . DIRECTORY_SEPARATOR . 'sqlite';
30
    const LINK_FILENAME = 'browscap.link';
31
32
    /**
33
     * Version that is saved in the generated data. Has to be increased
34
     * to trigger the invalidation of the data.
35
     */
36
    const VERSION = '1.1.0';
37
38
    /**
39
     * @var SourceInterface
40
     */
41
    protected $source;
42
43
    /**
44
     * @var ReaderInterface
45
     */
46
    protected $reader;
47
48
    /**
49
     * @var WriterInterface
50
     */
51
    protected $writer;
52
53
    /**
54
     * @var string
55
     */
56
    protected $dataDirectory;
57
58
    /**
59
     * Parser constructor.
60
     *
61
     * @param string|null $dataDirectory
62
     *
63
     * @throws ParserConfigurationException
64
     */
65
    public function __construct(string $dataDirectory = null)
66
    {
67
        if ($dataDirectory !== null) {
68
            $this->setDataDirectory($dataDirectory);
69
        }
70
    }
71
72
    /**
73
     * @return SourceInterface
74
     * @throws InvalidArgumentException
75
     * @throws UnexpectedValueException
76
     */
77
    public function getSource() : SourceInterface
78
    {
79
        if ($this->source === null) {
80
            $this->setSource(SourceFactory::getInstance());
81
        }
82
83
        return $this->source;
84
    }
85
86
    /**
87
     * @param SourceInterface $source
88
     *
89
     * @throws InvalidArgumentException
90
     */
91
    public function setSource(SourceInterface $source)
92
    {
93
        $this->source = $source;
94
    }
95
96
    /**
97
     * @return string
98
     *
99
     * @throws ParserConfigurationException
100
     */
101
    public function getDataDirectory()
102
    {
103
        if ($this->dataDirectory === null) {
104
            $this->setDataDirectory(sys_get_temp_dir());
105
        }
106
107
        return $this->dataDirectory;
108
    }
109
110
    /**
111
     * @param string $directory
112
     *
113
     * @throws ParserConfigurationException
114
     */
115
    public function setDataDirectory(string $directory)
116
    {
117
        $directory = rtrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $directory), DIRECTORY_SEPARATOR);
118
119
        // Check main data directory
120
        $this->checkDirectory($directory, false);
121
122
        // Check/create sub directory
123
        $directory = $directory . DIRECTORY_SEPARATOR . self::SUB_DIRECTORY;
124
        $this->checkDirectory($directory, true);
125
126
        $this->dataDirectory = $directory;
127
    }
128
129
    /**
130
     * @param string $directory
131
     * @param bool $create
132
     *
133
     * @throws ParserConfigurationException
134
     */
135
    protected function checkDirectory(string $directory, bool $create = false)
136
    {
137
        if (!file_exists($directory) &&
138
            ($create === false || (!@mkdir($directory, 0777, true) && !is_dir($directory)))
139
        ) {
140
            throw new ParserConfigurationException(
141
                "Directory '$directory' does not exist and could not be created.",
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $directory instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
142
                1458974127
143
            );
144
        }
145
    }
146
147
    /**
148
     * @param string $directory
149
     *
150
     * @return bool
151
     */
152
    protected function isDirectoryReadable(string $directory) : bool
153
    {
154
        return is_readable($directory);
155
    }
156
157
    /**
158
     * @param string $directory
159
     *
160
     * @return bool
161
     */
162
    protected function isDirectoryWritable(string $directory) : bool
163
    {
164
        return is_writable($directory);
165
    }
166
167
    /**
168
     * @inheritdoc
169
     *
170
     * @throws ParserConditionNotSatisfiedException
171
     * @throws ParserConfigurationException
172
     * @throws InvalidArgumentException
173
     * @throws UnexpectedValueException
174
     */
175
    public function setPropertyFilter(PropertyFilterInterface $filter)
176
    {
177
        $this->propertyFilter = $filter;
178
179
        $this->getReader()->setPropertyFilter($filter);
180
        $this->getWriter()->setPropertyFilter($filter);
181
182
        $dataVersionHash = $this->getDataVersionHash();
183
        $this->getReader()->setDataVersionHash($dataVersionHash);
184
        $this->getWriter()->setDataVersionHash($dataVersionHash);
185
    }
186
187
    /**
188
     * Generates a hash from relevant information that influence the
189
     * validity of
190
     *
191
     * @return string
192
     */
193
    protected function getDataVersionHash()
194
    {
195
        $filter = $this->getPropertyFilter();
196
        $properties = $filter->getProperties();
197
        sort($properties);
198
        
199
        return sha1(
200
            static::class . '|' .
201
            static::VERSION . '|' .
202
            get_class($filter) . '|' .
203
            implode(',', $properties)
204
        );
205
    }
206
207
    /**
208
     * @inheritdoc
209
     *
210
     * @return Reader
211
     *
212
     * @throws ParserConditionNotSatisfiedException
213
     * @throws ParserConfigurationException
214
     */
215
    public function getReader($reInitiate = false) : ReaderInterface
216
    {
217 View Code Duplication
        if ($reInitiate === true || $this->reader === null) {
218
            $directory = $this->getDataDirectory();
219
220
            if (!$this->isDirectoryReadable($directory)) {
221
                throw new ParserConfigurationException("Directory '$directory' is not readable.", 1458974128);
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $directory instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
222
            }
223
224
            $this->reader = new Reader($directory);
225
            $this->reader->setPropertyFilter($this->getPropertyFilter());
226
            $this->reader->setDataVersionHash($this->getDataVersionHash());
227
        }
228
229
        return $this->reader;
230
    }
231
232
    /**
233
     * @inheritdoc
234
     *
235
     * @return Writer
236
     *
237
     * @throws ParserConditionNotSatisfiedException
238
     * @throws ParserConfigurationException
239
     * @throws InvalidArgumentException
240
     * @throws UnexpectedValueException
241
     */
242
    public function getWriter() : WriterInterface
243
    {
244 View Code Duplication
        if ($this->writer === null) {
245
            $directory = $this->getDataDirectory();
246
247
            if (!$this->isDirectoryWritable($directory)) {
248
                throw new ParserConfigurationException("Directory '$directory' is not writable.", 1458974129);
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $directory instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
249
            }
250
251
            $this->writer = new Writer($directory, $this->getSource());
252
            $this->writer->setPropertyFilter($this->getPropertyFilter());
253
            $this->writer->setDataVersionHash($this->getDataVersionHash());
254
        }
255
256
        return $this->writer;
257
    }
258
}
259