Passed
Push — master ( 1ed9d2...698c63 )
by Sergio
01:13
created

CSVParser::each()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: sergio.rodenas
5
 * Date: 15/5/18
6
 * Time: 18:59
7
 */
8
9
namespace Rodenastyle\StreamParser\Parsers;
10
11
12
use Rodenastyle\StreamParser\Exceptions\IncompleteParseException;
13
use Rodenastyle\StreamParser\StreamParserInterface;
14
use Tightenco\Collect\Support\Collection;
15
16
class CSVParser implements StreamParserInterface
17
{
18
	protected $reader, $source, $headers, $currentLine;
19
20
	public static $delimiters = [",", ";"];
21
	public static $skipsEmptyLines = true;
22
23 5
	public function from(String $source): StreamParserInterface
24
	{
25 5
		$this->source = $source;
26
27 5
		return $this;
28
	}
29
30 5
	public function each(callable $function)
31
	{
32 5
		$this->start();
33 5
		while($this->read()){
34 5
			if($this->currentLineIsValid()){
35 5
				$function($this->getCurrentLineAsCollection());
36
			}
37
		}
38 5
		$this->close();
39 5
	}
40
41 5
	private function start()
42
	{
43 5
		$this->reader = fopen($this->source, 'r');
44
45 5
		$this->read();
46 5
		$this->headers = $this->currentLine;
47
48 5
		return $this;
49
	}
50
51 5
	private function close(){
52 5
		if( ! fclose($this->reader)){
53
			throw new IncompleteParseException();
54
		}
55 5
	}
56
57 5
	private function read(): bool{
58 5
		$this->currentLine = new Collection(fgetcsv($this->reader));
59
60
		//EOF detection
61 5
		return $this->currentLine->first() !== false;
62
	}
63
64 5
	private function currentLineIsValid(): bool{
65 5
		if(static::$skipsEmptyLines){
66 4
			return ! $this->currentLineIsEmpty();
67
		}
68
69 1
		return true;
70
	}
71
72
	private function currentLineIsEmpty(){
73 4
		return $this->currentLine->reject(function($value){
74 4
			return $this->isEmptyValue($value);
75 4
		})->isEmpty();
76
	}
77
78 5
	private function isEmptyValue($value){
79 5
		return $value === "" || $value === null;
80
	}
81
82 5
	private function getCurrentLineAsCollection()
83
	{
84 5
		$headers = $this->headers;
85 5
		$values = $this->formatCurrentLineValues($this->currentLine);
86
87 5
		return $headers->intersectByKeys($this->currentLine)
88 5
			->combine($values->recursive())
89 5
			->reject(function($value){
90 5
				return $this->isEmptyValue($value);
91 5
			});
92
	}
93
94 5
	private function formatCurrentLineValues(Collection $collection){
95 5
		$this->explodeCollectionValues($collection);
96 5
		return $collection;
97
	}
98
99
	private function explodeCollectionValues(Collection $collection){
100
		$collection->transform(function($value){
101 5
			(new Collection(static::$delimiters))->each(function($delimiter) use (&$value){
102 5
				if( ! is_array($value) && strpos($value, $delimiter) !== false){
103 5
					$value = explode($delimiter, $value);
104
				}
105 5
			});
106 5
			if(is_array($value)){
107 5
				return (new Collection($value))->reject(function($value){
108 5
					return $this->isEmptyValue($value);
109 5
				});
110
			} else {
111 5
				return $value;
112
			}
113 5
		});
114 5
	}
115
}
116