Completed
Push — master ( 6b36b9...7b2e38 )
by René
9s
created

Reader::getData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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