Completed
Push — master ( a3d473...b7bc61 )
by René
02:28
created

Reader::toArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 7
rs 9.4285
cc 2
eloc 4
nc 2
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
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 Collection
90
     */
91
    public function getHeaders()
92
    {
93
        return $this->headers;
94
    }
95
96
    /**
97
     * @return Collection
98
     */
99
    public function getDefinitions()
100
    {
101
        return $this->definitions;
102
    }
103
104
    /**
105
     * @return Collection
106
     */
107
    public function getData()
108
    {
109
        return $this->data;
110
    }
111
112
113
    /**
114
     * Validates a string
115
     * @param string $contents
116
     * @return bool Returns true if content is valid
117
     */
118
    protected function containsValidBLM($contents)
119
    {
120
        $contains = $this->containsHeader($contents) ? self::CONTAINS_HEADERS : 0;
121
        $contains += $this->containsDefinition($contents) ? self::CONTAINS_DEFINITIONS : 0;
122
        $contains += $this->containsData($contents) ? self::CONTAINS_DATA : 0;
123
124
        $shouldContain = self::CONTAINS_HEADERS | self::CONTAINS_DEFINITIONS | self::CONTAINS_DATA;
125
126
        return (bool) ($shouldContain & $contains);
127
    }
128
129
    /**
130
     * Verifies if contents contain a headers sections
131
     * @param string $contents
132
     * @return bool
133
     */
134
    protected function containsHeader($contents)
135
    {
136
        return (bool) $this->getRegexFor('#HEADER#')->test($contents);
137
    }
138
139
    /**
140
     * Verifies if contents contain a headers sections
141
     * @param string $contents
142
     * @return bool
143
     */
144
    protected function containsDefinition($contents)
145
    {
146
        return (bool) $this->getRegexFor('#DEFINITION#')->test($contents);
147
    }
148
149
    /**
150
     * Verifies if contents contain a headers sections
151
     * @param string $contents
152
     * @return bool
153
     */
154
    protected function containsData($contents)
155
    {
156
        return (bool) $this->getRegexFor('#DATA#')->test($contents);
157
    }
158
159
    /**
160
     * @param string $contents
161
     * @return Reader
162
     */
163
    protected function parse($contents)
164
    {
165
        $this->contents = $contents;
166
167
        $this->parseHeader($contents);
168
169
        $this->parseDefinition($contents);
170
171
        $this->parseData($contents);
172
173
        return $this;
174
    }
175
176
    /**
177
     * Parses the #HEADER# section of the BLM file and stores it in $this->headers
178
     * @param string $contents
179
     */
180
    protected function parseHeader($contents)
181
    {
182
        preg_match($this->getRegexFor('#HEADER#'), $contents, $match);
183
184
        $this->headers = collect(explode("\n", $match[0]))
185
            ->map(function ($linesWithSpaces) {
186
                return trim($linesWithSpaces);
187
            })
188
            ->reject(function ($line) {
189
                return '' === $line || false === strpos($line, ':');
190
            })->map(function ($line) {
191
                return explode(' : ', $line);
192
            })->flatMap(function ($keyValuePairWithQuotes) {
193
                return [
194
                    $keyValuePairWithQuotes[0] => preg_replace('/(^[\'"]|[\'"]$)/', '',
195
                        trim($keyValuePairWithQuotes[1]))
196
                ];
197
            });
198
    }
199
200
    /**
201
     * Parses the #DEFINITION# section of the BLM file and stores it in $this->definitions
202
     * @param string $contents
203
     */
204
    protected function parseDefinition($contents)
205
    {
206
        preg_match($this->getRegexFor('#DEFINITION#'), $contents, $match);
207
208
        $eof = $this->headers->get('EOF');
209
        $eor = $this->headers->get('EOR');
210
211
        $this->definitions = collect(explode("\n", $match[0]))
212
            ->reject(function ($line) {
213
                return $this->isValidLineWithoutHash($line);
214
            })->flatMap(function ($definitions) use ($eof) {
215
                return explode($eof, $definitions);
216
            })->map(function ($definition) {
217
                return trim($definition);
218
            })->reject(function ($defintion) use ($eor) {
219
                return '' === $defintion || $eor === $defintion;
220
            });
221
    }
222
223
    /**
224
     * Parses the #DATA# section of the BLM file and stores it in $this->data
225
     * @param $contents
226
     */
227
    protected function parseData($contents)
228
    {
229
        preg_match($this->getRegexFor('#DATA#'), $contents, $match);
230
231
        $eof = $this->headers->get('EOF');
232
        $eor = $this->headers->get('EOR');
233
234
        $this->data = collect(explode("\n", $match[0]))
235
            ->reject(function ($line) {
236
                return $this->isValidLineWithoutHash($line);
237
            })->flatMap(function ($data) use ($eor) {
238
                return explode($eor, $data);
239
            })->reject(function ($line) {
240
                return '' === $line;
241
            })->map(function ($record) {
242
                return trim($record);
243
            })->map(function ($record) use ($eof) {
244
                return explode($eof, $record);
245
            })->map(function ($record) {
246
                return $this->mapRecordToDefinitionValue($record);
247
            })->values();
248
    }
249
250
    /**
251
     * @param $string
252
     * @return VerbalExpressions
253
     */
254
    private function getRegexFor($string)
255
    {
256
        return (new VerbalExpressions)
257
            ->startOfLine()
258
            ->find($string)
259
            ->anythingBut('#')
260
            ->addModifier('sm');
261
    }
262
263
    /**
264
     * @param $line
265
     * @return bool
266
     */
267
    private function isValidLineWithoutHash($line)
268
    {
269
        return false !== strpos($line, '#') || '' === trim($line);
270
    }
271
    /**
272
     * @param array $record
273
     * @return array
274
     */
275
    private function mapRecordToDefinitionValue($record)
276
    {
277
        $definitions = $this->definitions;
278
279
        return collect($record)
280
            ->filter(function ($column, $offset) use ($definitions) {
281
                return $definitions->offsetExists($offset);
282
            })
283
            ->flatMap(function ($column, $index) use ($definitions) {
284
                return [$definitions[$index] => $column];
285
            })->toArray();
286
    }
287
}
288