Completed
Push — master ( a06baa...504d87 )
by René
02:30
created

Reader::isValidLineWithoutHash()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 2
eloc 2
nc 2
nop 1
1
<?php
2
3
namespace Renedekat\Blm;
4
5
use Illuminate\Support\Collection;
6
use Renedekat\Blm\Exceptions\InvalidBlmFileException;
7
use Renedekat\Blm\Exceptions\InvalidBlmStringException;
8
use Renedekat\PHPVerbalExpressions\VerbalExpressions;
9
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
10
11
class Reader
12
{
13
    const CONTAINS_HEADERS = 1;
14
    const CONTAINS_DEFINITIONS = 2;
15
    const CONTAINS_DATA = 4;
16
17
    /**
18
     * @var string Raw contents
19
     */
20
    protected $contents = null;
21
22
    /**
23
     * @var Collection Parsed header
24
     */
25
    protected $headers = null;
26
27
    /**
28
     * @var Collection Parsed definition
29
     */
30
    protected $definitions = null;
31
32
    /**
33
     * @var Collection Parsed data
34
     */
35
    protected $data = null;
36
37
    /**
38
     * @return Reader
39
     */
40
    public static function create()
41
    {
42
        return new static();
43
    }
44
45
    /**
46
     * @param string $filePath Path to the BLM file
47
     * @return $this
48
     * @throws FileNotFoundException
49
     * @throws InvalidBlmFileException
50
     */
51
    public function loadFromFile($filePath)
52
    {
53
        if (!file_exists($filePath)) {
54
            throw new FileNotFoundException();
55
        }
56
57
        $contents = file_get_contents($filePath);
58
59
        if (!$this->containsValidBLM($contents)) {
60
            throw new InvalidBlmFileException('No valid BLM file found.');
61
        }
62
63
        return $this->parse($contents);
64
    }
65
66
    /**
67
     * @param string $contents Contents of a BLM file
68
     * @return Reader
69
     * @throws InvalidBlmFileException
70
     */
71
    public function loadFromString($contents)
72
    {
73
        if (!$this->containsValidBLM($contents)) {
74
            throw new InvalidBlmStringException('Invalid BLM content found.');
75
        }
76
77
        return $this->parse($contents);
78
    }
79
80
    /**
81
     * @return string
82
     */
83
    public function getRawContents()
84
    {
85
        return $this->contents;
86
    }
87
88
    /**
89
     * Return array of DATA
90
     * @return array
91
     */
92
    public function toArray()
93
    {
94
        if (is_null($this->data)) {
95
            return [];
96
        }
97
        return $this->data->toArray();
98
    }
99
100
    /**
101
     * @return Collection
102
     */
103
    public function getData()
104
    {
105
        return $this->data;
106
    }
107
108
    /**
109
     * @return Collection
110
     */
111
    protected function getHeaders()
112
    {
113
        return $this->headers;
114
    }
115
116
    /**
117
     * @return Collection
118
     */
119
    protected function getDefinitions()
120
    {
121
        return $this->definitions;
122
    }
123
124
125
    /**
126
     * Validates a string
127
     * @param string $contents
128
     * @return bool Returns true if content is valid
129
     */
130
    protected function containsValidBLM($contents)
131
    {
132
        $contains = $this->containsHeader($contents) ? self::CONTAINS_HEADERS : 0;
133
        $contains += $this->containsDefinition($contents) ? self::CONTAINS_DEFINITIONS : 0;
134
        $contains += $this->containsData($contents) ? self::CONTAINS_DATA : 0;
135
136
        $shouldContain = self::CONTAINS_HEADERS | self::CONTAINS_DEFINITIONS | self::CONTAINS_DATA;
137
138
        return ($shouldContain & $contains);
139
    }
140
141
    /**
142
     * Verifies if contents contain a headers sections
143
     * @param string $contents
144
     * @return bool
145
     */
146
    protected function containsHeader($contents)
147
    {
148
        return $this->getRegexFor('#HEADER#')->test($contents);
149
    }
150
151
    /**
152
     * Verifies if contents contain a headers sections
153
     * @param string $contents
154
     * @return bool
155
     */
156
    protected function containsDefinition($contents)
157
    {
158
        return $this->getRegexFor('#DEFINITION#')->test($contents);
159
    }
160
161
    /**
162
     * Verifies if contents contain a headers sections
163
     * @param string $contents
164
     * @return bool
165
     */
166
    protected function containsData($contents)
167
    {
168
        return $this->getRegexFor('#DATA#')->test($contents);
169
    }
170
171
    /**
172
     * @param string $contents
173
     * @return Reader
174
     */
175
    protected function parse($contents)
176
    {
177
        $this->contents = $contents;
178
179
        $this->parseHeader($contents);
180
181
        $this->parseDefinition($contents);
182
183
        $this->parseData($contents);
184
185
        return $this;
186
    }
187
188
    /**
189
     * Parses the #HEADER# section of the BLM file and stores it in $this->headers
190
     * @param string $contents
191
     */
192
    protected function parseHeader($contents)
193
    {
194
        preg_match($this->getRegexFor('#HEADER#'), $contents, $match);
195
196
        $this->headers = collect(explode("\n", $match[0]))
197
            ->map(function ($linesWithSpaces) {
198
                return trim($linesWithSpaces);
199
            })
200
            ->reject(function ($line) {
201
                return '' === $line || false === strpos($line, ':');
202
            })->map(function ($line) {
203
                return explode(' : ', $line);
204
            })->flatMap(function ($keyValuePairWithQuotes) {
205
                return [
206
                    $keyValuePairWithQuotes[0] => preg_replace('/(^[\'"]|[\'"]$)/', '',
207
                        trim($keyValuePairWithQuotes[1]))
208
                ];
209
            });
210
    }
211
212
    /**
213
     * Parses the #DEFINITION# section of the BLM file and stores it in $this->definitions
214
     * @param string $contents
215
     */
216
    protected function parseDefinition($contents)
217
    {
218
        preg_match($this->getRegexFor('#DEFINITION#'), $contents, $match);
219
220
        $eof = $this->headers->get('EOF');
221
        $eor = $this->headers->get('EOR');
222
223
        $this->definitions = collect(explode("\n", $match[0]))
224
            ->reject(function ($line) {
225
                return $this->isValidLineWithoutHash($line);
226
            })->flatMap(function ($definitions) use ($eof) {
227
                return explode($eof, $definitions);
228
            })->map(function ($definition) {
229
                return trim($definition);
230
            })->reject(function ($defintion) use ($eor) {
231
                return '' === $defintion || $eor === $defintion;
232
            });
233
    }
234
235
    /**
236
     * Parses the #DATA# section of the BLM file and stores it in $this->data
237
     * @param $contents
238
     */
239
    protected function parseData($contents)
240
    {
241
        preg_match($this->getRegexFor('#DATA#'), $contents, $match);
242
243
        $eof = $this->headers->get('EOF');
244
        $eor = $this->headers->get('EOR');
245
246
        $this->data = collect(explode("\n", $match[0]))
247
            ->reject(function ($line) {
248
                return $this->isValidLineWithoutHash($line);
249
            })->flatMap(function ($data) use ($eor) {
250
                return explode($eor, $data);
251
            })->reject(function ($line) {
252
                return '' === $line;
253
            })->map(function ($record) {
254
                return trim($record);
255
            })->map(function ($record) use ($eof) {
256
                return explode($eof, $record);
257
            })->map(function ($record) {
258
                return $this->mapRecordToDefinitionValue($record);
259
            })->values();
260
    }
261
262
    /**
263
     * @param $string
264
     * @return VerbalExpressions
265
     */
266
    private function getRegexFor($string)
267
    {
268
        return (new VerbalExpressions)
269
            ->startOfLine()
270
            ->find($string)
271
            ->anythingBut('#')
272
            ->addModifier('sm');
273
    }
274
275
    /**
276
     * @param $line
277
     * @return bool
278
     */
279
    private function isValidLineWithoutHash($line)
280
    {
281
        return false !== strpos($line, '#') || '' === trim($line);
282
    }
283
    /**
284
     * @param array $record
285
     * @return array
286
     */
287
    private function mapRecordToDefinitionValue($record)
288
    {
289
        $definitions = $this->definitions;
290
291
        return collect($record)
292
            ->filter(function ($column, $offset) use ($definitions) {
293
                return $definitions->offsetExists($offset);
294
            })
295
            ->flatMap(function ($column, $index) use ($definitions) {
296
                return [$definitions[$index] => $column];
297
            })->toArray();
298
    }
299
}
300