Passed
Branch master (6a7148)
by Curtis
01:48
created

Parser::parse()   D

Complexity

Conditions 26
Paths 26

Size

Total Lines 59
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 26
eloc 38
nc 26
nop 1
dl 0
loc 59
rs 4.1666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * php-gedcom
4
 *
5
 * php-gedcom is a library for parsing, manipulating, importing and exporting
6
 * GEDCOM 5.5 files in PHP 5.3+.
7
 *
8
 * @author          Kristopher Wilson <[email protected]>
9
 * @copyright       Copyright (c) 2010-2013, Kristopher Wilson
10
 * @package         php-gedcom
11
 * @license         MIT
12
 * @link            http://github.com/mrkrstphr/php-gedcom
13
 */
14
15
namespace PhpGedcom;
16
17
/**
18
 *
19
 */
20
class Parser {
21
	/**
22
	 *
23
	 */
24
	protected $_file = null;
25
26
	/**
27
	 *
28
	 */
29
	protected $_gedcom = null;
30
31
	/**
32
	 *
33
	 */
34
	protected $_errorLog = array();
35
36
	/**
37
	 *
38
	 */
39
	protected $_linesParsed = 0;
40
41
	/**
42
	 *
43
	 */
44
	protected $_line = '';
45
46
	/**
47
	 *
48
	 */
49
	protected $_lineRecord = null;
50
51
	/**
52
	 *
53
	 */
54
	protected $_linePieces = 0;
55
56
	/**
57
	 *
58
	 */
59
	protected $_returnedLine = '';
60
61
	/**
62
	 *
63
	 */
64
	public function __construct(\PhpGedcom\Gedcom $gedcom = null) {
65
		if (!is_null($gedcom)) {
66
			$this->_gedcom = $gedcom;
67
		} else {
68
			$this->_gedcom = new \PhpGedcom\Gedcom();
69
		}
70
	}
71
72
	/**
73
	 *
74
	 */
75
	public function forward() {
76
		// if there was a returned line by back(), set that as our current
77
		// line and blank out the returnedLine variable, otherwise grab
78
		// the next line from the file
79
80
		if (!empty($this->_returnedLine)) {
81
			$this->_line = $this->_returnedLine;
82
			$this->_returnedLine = '';
83
		} else {
84
			$this->_line = fgets($this->_file);
85
			$this->_lineRecord = null;
86
			$this->_linesParsed++;
87
		}
88
89
		return $this;
90
	}
91
92
	/**
93
	 *
94
	 */
95
	public function back() {
96
		// our parser object encountered a line it wasn't meant to parse
97
		// store this line for the previous parser to analyze
98
99
		$this->_returnedLine = $this->_line;
100
101
		return $this;
102
	}
103
104
	/**
105
	 * Jump to the next level in the GEDCOM that is <= $level. This will leave the parser at the line above
106
	 * this level, such that calling $parser->forward() will result in landing at the correct level.
107
	 *
108
	 * @param int $level
109
	 */
110
	public function skipToNextLevel($level) {
111
		$currentDepth = 999;
112
113
		while ($currentDepth > $level) {
114
			$this->forward();
115
			$record = $this->getCurrentLineRecord();
116
			$currentDepth = (int) $record[0];
117
		}
118
119
		$this->back();
120
	}
121
122
	/**
123
	 *
124
	 */
125
	public function getGedcom() {
126
		return $this->_gedcom;
127
	}
128
129
	/**
130
	 *
131
	 */
132
	public function eof() {
133
		return feof($this->_file);
134
	}
135
136
	/**
137
	 *
138
	 * @return string
139
	 */
140
	public function parseMultiLineRecord() {
141
		$record = $this->getCurrentLineRecord();
142
143
		$depth = (int) $record[0];
144
		$data = isset($record[2]) ? trim($record[2]) : '';
145
146
		$this->forward();
147
148
		while (!$this->eof()) {
149
			$record = $this->getCurrentLineRecord();
150
			$recordType = strtoupper(trim($record[1]));
151
			$currentDepth = (int) $record[0];
152
153
			if ($currentDepth <= $depth) {
154
				$this->back();
155
				break;
156
			}
157
158
			switch ($recordType) {
159
			case 'CONT':
160
				$data .= "\n";
161
162
				if (isset($record[2])) {
163
					$data .= trim($record[2]);
164
				}
165
				break;
166
			case 'CONC':
167
				if (isset($record[2])) {
168
					$data .= ' ' . trim($record[2]);
169
				}
170
				break;
171
			default:
172
				$this->back();
173
				break 2;
174
			}
175
176
			$this->forward();
177
		}
178
179
		return $data;
180
	}
181
182
	/**
183
	 *
184
	 * @return string The current line
185
	 */
186
	public function getCurrentLine() {
187
		return $this->_line;
188
	}
189
190
	/**
191
	 *
192
	 */
193
	public function getCurrentLineRecord($pieces = 3) {
194
		if (!is_null($this->_lineRecord)) {
195
			if ($this->_linePieces == $pieces) {
196
				return $this->_lineRecord;
197
			}
198
		}
199
200
		if (empty($this->_line)) {
201
			return false;
202
		}
203
204
		$line = trim($this->_line);
205
206
		$this->_lineRecord = explode(' ', $line, $pieces);
207
		$this->_linePieces = $pieces;
208
209
		return $this->_lineRecord;
210
	}
211
212
	/**
213
	 *
214
	 */
215
	protected function logError($error) {
216
		$this->_errorLog[] = $error;
217
	}
218
219
	/**
220
	 *
221
	 */
222
	public function logUnhandledRecord($additionalInfo = '') {
223
		$this->logError(
224
			$this->_linesParsed . ': (Unhandled) ' . trim(implode('|', $this->getCurrentLineRecord())) .
0 ignored issues
show
Bug introduced by
It seems like $this->getCurrentLineRecord() can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

224
			$this->_linesParsed . ': (Unhandled) ' . trim(implode('|', /** @scrutinizer ignore-type */ $this->getCurrentLineRecord())) .
Loading history...
225
			(!empty($additionalInfo) ? ' - ' . $additionalInfo : '')
226
		);
227
	}
228
229
	public function logSkippedRecord($additionalInfo = '') {
230
		$this->logError(
231
			$this->_linesParsed . ': (Skipping) ' . trim(implode('|', $this->getCurrentLineRecord())) .
0 ignored issues
show
Bug introduced by
It seems like $this->getCurrentLineRecord() can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

231
			$this->_linesParsed . ': (Skipping) ' . trim(implode('|', /** @scrutinizer ignore-type */ $this->getCurrentLineRecord())) .
Loading history...
232
			(!empty($additionalInfo) ? ' - ' . $additionalInfo : '')
233
		);
234
	}
235
236
	/**
237
	 *
238
	 */
239
	public function getErrors() {
240
		return $this->_errorLog;
241
	}
242
243
	/**
244
	 *
245
	 */
246
	public function normalizeIdentifier($identifier) {
247
		$identifier = trim($identifier);
248
		$identifier = trim($identifier, '@');
249
250
		return $identifier;
251
	}
252
253
	/**
254
	 *
255
	 * @param string $fileName
256
	 * @return Gedcom
257
	 */
258
	public function parse($fileName) {
259
		$this->_file = fopen($fileName, 'r'); #explode("\n", mb_convert_encoding($contents, 'UTF-8'));
260
261
		if (!$this->_file) {
262
			return null;
263
		}
264
265
		$this->forward();
266
267
		while (!$this->eof()) {
268
			$record = $this->getCurrentLineRecord();
269
270
			if ($record === false) {
271
				continue;
272
			}
273
274
			$depth = (int) $record[0];
275
276
			// We only process 0 level records here. Sub levels are processed
277
			// in methods for those data types (individuals, sources, etc)
278
279
			if ($depth == 0) {
280
				// Although not always an identifier (HEAD,TRLR):
281
				if (isset($record[1])) {
282
					$identifier = $this->normalizeIdentifier($record[1]);
0 ignored issues
show
Unused Code introduced by
The assignment to $identifier is dead and can be removed.
Loading history...
283
				}
284
285
				if (isset($record[1]) && trim($record[1]) == 'HEAD') {
286
					Parser\Head::parse($this);
287
				} else if (isset($record[2]) && trim($record[2]) == 'SUBN') {
288
					Parser\Subn::parse($this);
289
				} else if (isset($record[2]) && trim($record[2]) == 'SUBM') {
290
					Parser\Subm::parse($this);
291
				} else if (isset($record[2]) && $record[2] == 'SOUR') {
292
					Parser\Sour::parse($this);
293
				} else if (isset($record[2]) && $record[2] == 'INDI') {
294
					Parser\Indi::parse($this);
295
				} else if (isset($record[2]) && $record[2] == 'FAM') {
296
					Parser\Fam::parse($this);
297
				} else if (isset($record[2]) && substr(trim($record[2]), 0, 4) == 'NOTE') {
298
					Parser\Note::parse($this);
299
				} else if (isset($record[2]) && $record[2] == 'REPO') {
300
					Parser\Repo::parse($this);
301
				} else if (isset($record[2]) && $record[2] == 'OBJE') {
302
					Parser\Obje::parse($this);
303
				} else if (isset($record[1]) && trim($record[1]) == 'TRLR') {
304
					// EOF
305
					break;
306
				} else {
307
					$this->logUnhandledRecord(get_class() . ' @ ' . __LINE__);
308
				}
309
			} else {
310
				$this->logUnhandledRecord(get_class() . ' @ ' . __LINE__);
311
			}
312
313
			$this->forward();
314
		}
315
316
		return $this->getGedcom();
317
	}
318
}