Completed
Push — master ( 7769a7...8f7e2a )
by Simonas
03:09
created

JsonReader::getFileHandler()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 19
rs 9.2
cc 4
eloc 11
nc 4
nop 0
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\ElasticsearchBundle\Service\Json;
13
14
use ONGR\ElasticsearchBundle\Service\Manager;
15
use Symfony\Component\OptionsResolver\OptionsResolver;
16
17
/**
18
 * Reads records one by one.
19
 */
20
class JsonReader implements \Countable, \Iterator
21
{
22
    /**
23
     * @var string
24
     */
25
    private $filename;
26
27
    /**
28
     * @var resource A file system pointer resource.
29
     */
30
    private $handle;
31
32
    /**
33
     * @var int
34
     */
35
    private $key = 0;
36
37
    /**
38
     * @var string
39
     */
40
    private $currentLine;
41
42
    /**
43
     * @var mixed
44
     */
45
    private $metadata;
46
47
    /**
48
     * @var Manager
49
     */
50
    private $manager;
51
52
    /**
53
     * @var OptionsResolver
54
     */
55
    private $optionsResolver;
56
57
    /**
58
     * @var array
59
     */
60
    private $options;
61
62
    /**
63
     * Constructor.
64
     *
65
     * @param Manager $manager
66
     * @param string  $filename
67
     * @param array   $options
68
     *
69
     */
70
    public function __construct($manager, $filename, $options)
71
    {
72
        $this->manager = $manager;
73
        $this->filename = $filename;
74
        $this->options = $options;
75
    }
76
77
    /**
78
     * Destructor. Closes file handler if open.
79
     */
80
    public function __destruct()
81
    {
82
        if ($this->handle !== null) {
83
            @fclose($this->handle);
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...
84
        }
85
    }
86
87
    /**
88
     * @return Manager
89
     */
90
    public function getManager()
91
    {
92
        return $this->manager;
93
    }
94
95
    /**
96
     * Returns file handler.
97
     *
98
     * @return resource
99
     *
100
     * @throws \LogicException
101
     */
102
    protected function getFileHandler()
103
    {
104
        if ($this->handle === null) {
105
            $isGzip = array_key_exists('gzip', $this->options);
106
107
            $filename = !$isGzip?
108
                $this->filename:
109
                sprintf('compress.zlib://%s', $this->filename);
110
            $fileHandler = @fopen($filename, 'r');
111
112
            if ($fileHandler === false) {
113
                throw new \LogicException('Can not open file.');
114
            }
115
116
            $this->handle = $fileHandler;
117
        }
118
119
        return $this->handle;
120
    }
121
122
    /**
123
     * Reads metadata from file.
124
     *
125
     * @throws \InvalidArgumentException
126
     */
127
    protected function readMetadata()
128
    {
129
        if ($this->metadata !== null) {
130
            return;
131
        }
132
133
        $line = fgets($this->getFileHandler());
134
135
        if (trim($line) !== '[') {
136
            throw new \InvalidArgumentException('Given file does not match expected pattern.');
137
        }
138
139
        $line = trim(fgets($this->getFileHandler()));
140
        $this->metadata = json_decode(rtrim($line, ','), true);
141
    }
142
143
    /**
144
     * Reads single line from file.
145
     */
146
    protected function readLine()
147
    {
148
        $buffer = '';
149
150
        while ($buffer === '') {
151
            $buffer = fgets($this->getFileHandler());
152
153
            if ($buffer === false) {
154
                $this->currentLine = null;
155
156
                return;
157
            }
158
159
            $buffer = trim($buffer);
160
        }
161
162
        if ($buffer === ']') {
163
            $this->currentLine = null;
164
165
            return;
166
        }
167
168
        $data = json_decode(rtrim($buffer, ','), true);
169
        $this->currentLine = $this->getOptionsResolver()->resolve($data);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getOptionsResolver()->resolve($data) of type array is incompatible with the declared type string of property $currentLine.

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...
170
    }
171
172
    /**
173
     * Configures OptionResolver for resolving document metadata fields.
174
     *
175
     * @param OptionsResolver $resolver
176
     */
177
    protected function configureResolver(OptionsResolver $resolver)
178
    {
179
        $resolver
180
            ->setRequired(['_id', '_type', '_source'])
181
            ->setDefaults(['_score' => null, 'fields' => []])
182
            ->addAllowedTypes('_id', ['integer', 'string'])
183
            ->addAllowedTypes('_type', 'string')
184
            ->addAllowedTypes('_source', 'array')
185
            ->addAllowedTypes('fields', 'array');
186
    }
187
188
    /**
189
     * Returns parsed current line.
190
     *
191
     * @return mixed
192
     */
193
    public function current()
194
    {
195
        if ($this->currentLine === null) {
196
            $this->readLine();
197
        }
198
199
        return $this->currentLine;
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function next()
206
    {
207
        $this->readLine();
208
209
        $this->key++;
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215
    public function key()
216
    {
217
        return $this->key;
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223
    public function valid()
224
    {
225
        return !feof($this->getFileHandler()) && $this->currentLine;
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     */
231
    public function rewind()
232
    {
233
        rewind($this->getFileHandler());
234
        $this->metadata = null;
235
        $this->readMetadata();
236
        $this->readLine();
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242
    public function count()
243
    {
244
        $metadata = $this->getMetadata();
245
246
        if (!isset($metadata['count'])) {
247
            throw new \LogicException('Given file does not contain count of documents.');
248
        }
249
250
        return $metadata['count'];
251
    }
252
253
    /**
254
     * Returns metadata.
255
     *
256
     * @return array|null
257
     */
258
    public function getMetadata()
259
    {
260
        $this->readMetadata();
261
262
        return $this->metadata;
263
    }
264
265
    /**
266
     * Returns configured options resolver instance.
267
     *
268
     * @return OptionsResolver
269
     */
270
    private function getOptionsResolver()
271
    {
272
        if (!$this->optionsResolver) {
273
            $this->optionsResolver = new OptionsResolver();
274
            $this->configureResolver($this->optionsResolver);
275
        }
276
277
        return $this->optionsResolver;
278
    }
279
}
280