Passed
Push — develop ( ab61e9...2bce29 )
by nguereza
01:46
created

CsvExtractor::setOptions()   A

Complexity

Conditions 6
Paths 16

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 19
rs 9.2222
cc 6
nc 16
nop 1
1
<?php
2
3
/**
4
 * Platine ETL
5
 *
6
 * Platine ETL is a library to Extract-Transform-Load Data from various sources
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine ETL
11
 * Copyright (c) 2019 Benoit POLASZEK
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
declare(strict_types=1);
33
34
namespace Platine\Etl\Extractor;
35
36
use InvalidArgumentException;
37
use Platine\Etl\Etl;
38
use Platine\Etl\Iterator\CsvFileIterator;
39
use Platine\Etl\Iterator\CsvKeysAwareIterator;
40
use Platine\Etl\Iterator\CsvStringIterator;
41
use SplFileObject;
42
43
/**
44
 * @class CsvExtractor
45
 * @package Platine\Etl\Extractor
46
 */
47
class CsvExtractor implements ExtractorInterface
48
{
49
    public const EXTRACT_AUTO = 0;
50
    public const EXTRACT_FROM_STRING = 1;
51
    public const EXTRACT_FROM_FILE = 2;
52
53
    /**
54
     * The CSV delimiter
55
     * @var string
56
     */
57
    protected string $delimiter = ',';
58
59
    /**
60
     * The CSV enclosure
61
     * @var string
62
     */
63
    protected string $enclosure = '"';
64
65
    /**
66
     * The CSV escape string
67
     * @var string
68
     */
69
    protected string $escapeString = '\\';
70
71
    /**
72
     * Whether to create keys (fields)
73
     * @var bool
74
     */
75
    protected bool $createKeys = false;
76
77
    /**
78
     * The extract source type
79
     * @var int
80
     */
81
    protected int $type;
82
83
    /**
84
     * Create new instance
85
     * @param int $type
86
     * @param string $delimiter
87
     * @param string $enclosure
88
     * @param string $escapeString
89
     * @param bool $createKeys
90
     */
91
    public function __construct(
92
        int $type = self::EXTRACT_AUTO,
93
        string $delimiter = ',',
94
        string $enclosure = '"',
95
        string $escapeString = '\\',
96
        bool $createKeys = false
97
    ) {
98
        $this->delimiter = $delimiter;
99
        $this->enclosure = $enclosure;
100
        $this->escapeString = $escapeString;
101
        $this->createKeys = $createKeys;
102
        $this->type = $type;
103
    }
104
105
    /**
106
     * {@inheritodc}
107
     */
108
    public function extract($input, Etl $etl, array $options = []): iterable
109
    {
110
        $this->setOptions($options);
111
112
        switch ($this->type) {
113
            case self::EXTRACT_FROM_FILE:
114
                $iterator = $this->extractFromFile($input);
115
                break;
116
            case self::EXTRACT_FROM_STRING:
117
                 $iterator = $this->extractFromString($input);
118
                break;
119
            case self::EXTRACT_AUTO:
120
                $iterator = $this->extractAuto($input);
121
                break;
122
            default:
123
                throw new InvalidArgumentException(sprintf(
124
                    'Invalid extract source data type provided [%d], must be one of [%s]',
125
                    $this->type,
126
                    implode(',', [
127
                        self::EXTRACT_AUTO,
128
                        self::EXTRACT_FROM_STRING,
129
                        self::EXTRACT_FROM_FILE,
130
                    ])
131
                ));
132
        }
133
134
        return $this->createKeys ? new CsvKeysAwareIterator($iterator) : $iterator;
135
    }
136
137
    /**
138
     * Extract source data from string
139
     * @param string $data
140
     * @return iterable<int|string, mixed>
141
     */
142
    protected function extractFromString(string $data): iterable
143
    {
144
        return CsvStringIterator::createFromText(
145
            $data,
146
            $this->delimiter,
147
            $this->enclosure,
148
            $this->escapeString
149
        );
150
    }
151
152
    /**
153
     * Extract source data from file
154
     * @param SplFileObject|string $file
155
     * @return iterable<int|string, mixed>
156
     */
157
    protected function extractFromFile($file): iterable
158
    {
159
        if ($file instanceof SplFileObject) {
160
            return new CsvFileIterator(
161
                $file,
162
                $this->delimiter,
163
                $this->enclosure,
164
                $this->escapeString
165
            );
166
        }
167
168
        return CsvFileIterator::createFromFilename(
169
            $file,
170
            $this->delimiter,
171
            $this->enclosure,
172
            $this->escapeString
173
        );
174
    }
175
176
    /**
177
     * Extract source data by detect the type
178
     * @param string $data
179
     * @return iterable<int|string, mixed>
180
     */
181
    protected function extractAuto(string $data): iterable
182
    {
183
        if (strlen($data) < 3000 && file_exists($data)) {
184
            return $this->extractFromFile($data);
185
        }
186
187
        return $this->extractFromString($data);
188
    }
189
190
    /**
191
     * Set the options
192
     * @param array<string, mixed> $options
193
     * @return $this
194
     */
195
    protected function setOptions(array $options)
196
    {
197
        if (isset($options['delimiter'])) {
198
            $this->delimiter = $options['delimiter'];
199
        }
200
201
        if (isset($options['enclosure'])) {
202
            $this->enclosure = $options['enclosure'];
203
        }
204
205
        if (isset($options['escape_string'])) {
206
            $this->escapeString = $options['escape_string'];
207
        }
208
209
        if (isset($options['create_keys']) && is_bool($options['create_keys'])) {
210
            $this->createKeys = $options['create_keys'];
211
        }
212
213
        return $this;
214
    }
215
}
216