Issues (10)

src/Writer/SQLLoadDataLocalWriter.php (2 issues)

Labels
Severity
1
<?php
2
3
namespace Jackal\Copycat\Writer;
4
5
use SplFileInfo;
6
use Symfony\Component\OptionsResolver\OptionsResolver;
7
8
/**
9
 * Class SQLLoadDataLocalWriter
10
 * @package Jackal\Copycat\Writer
11
 */
12
class SQLLoadDataLocalWriter extends CSVFileWriter
13
{
14
    /**
15
     * @var string
16
     */
17
    protected $sqlOutputFilePathname;
18
19
    /**
20
     * @var string
21
     */
22
    protected $tableName;
23
24
    /**
25
     * @var array
26
     */
27
    protected $headers = [];
28
29
    /**
30
     * SQLLoadDataLocalWriter constructor.
31
     * @param $tableName
32
     * @param $outputFilePathname
33
     * @param array $options
34
     */
35
    public function __construct($tableName, $outputFilePathname, $options = [])
36
    {
37
        if (!is_dir(dirname($outputFilePathname))) {
38
            mkdir(dirname($outputFilePathname), 0775, true);
39
        }
40
41
        touch($outputFilePathname);
42
43
        $outputFile = new SplFileInfo($outputFilePathname);
44
45
        $localPath = realpath($outputFile->getPathname());
46
        $csvOutputFilePathname = str_replace('.' . $outputFile->getExtension(), '.csv', $localPath);
47
48
        $opts = [
49
            'header' => true,
50
            'delimiter' => "\t",
51
        ];
52
        foreach (['columns','delimiter','enclosure','header','replace_file'] as $option) {
53
            if (array_key_exists($option, $options)) {
54
                $opts[$option] = $options[$option];
55
            }
56
        }
57
58
        parent::__construct($csvOutputFilePathname, $opts);
59
60
        $resolver = new OptionsResolver();
61
        $resolver->setDefaults([
62
            'replace_file' => false,
63
            'delimiter' => $opts['delimiter'],
64
            'enclosure' => '"',
65
            'header' => $opts['header'],
66
            'autoincrement_field' => false,
67
            'drop_data' => false,
68
            'create_table' => false,
69
            'columns' => [],
70
        ]);
71
72
        $this->options = $resolver->resolve(array_merge($options, $opts));
73
74
        $this->sqlOutputFilePathname = $localPath;
75
        $this->tableName = $tableName;
76
    }
77
78
    /**
79
     * @param array $item
80
     */
81
    public function writeItem(array $item)
82
    {
83
        if ($this->index == 0) {
84
            $this->headers = array_keys($item);
85
        }
86
        parent::writeItem($item);
87
    }
88
89
    /**
90
     *
91
     */
92
    public function finish()
93
    {
94
        parent::finish();
95
96
        $dropRecordsString = $this->options['drop_data'] ? sprintf("\n" . 'DELETE FROM %s;', $this->tableName) : null;
97
        $autoIncrementString = $this->options['autoincrement_field'] ? sprintf('SET %s = NULL', $this->options['autoincrement_field']) : null;
98
        $rowsToIgnore = $this->options['header'] ? 1 : 0;
99
        $headers = '(' . implode(', ', $this->headers) . ')';
100
101
        $delimiter = str_replace(["\t","\n","\r"], ['\\t','\\n','\\r'], $this->options['delimiter']);
102
        $enclosure = $this->options['enclosure'];
103
104
        $createTableSql = null;
105
        if ($this->options['create_table']) {
106
            $h = fopen($this->outputFilePathname, 'r');
107
            $columnNames = fgetcsv($h, 0, $this->options['delimiter'], $this->options['enclosure']);
0 ignored issues
show
It seems like $h can also be of type false; however, parameter $handle of fgetcsv() does only seem to accept resource, 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

107
            $columnNames = fgetcsv(/** @scrutinizer ignore-type */ $h, 0, $this->options['delimiter'], $this->options['enclosure']);
Loading history...
108
109
            $autoIncrementField = $this->options['autoincrement_field'] ? $this->options['autoincrement_field'] . ' int auto_increment not null, ' : '';
110
            $primaryKeyField = $this->options['autoincrement_field'] ? ', primary key (' . $this->options['autoincrement_field'] . ')' : '';
111
            $createTableSql = sprintf('DROP TABLE IF EXISTS %s;CREATE TABLE %s (%s%s%s);', $this->tableName, $this->tableName, $autoIncrementField, implode(', ', array_map(function ($value) {
112
                return $value . ' text';
113
            }, $columnNames)), $primaryKeyField);
0 ignored issues
show
It seems like $columnNames can also be of type false and null; however, parameter $arr1 of array_map() 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

113
            }, /** @scrutinizer ignore-type */ $columnNames)), $primaryKeyField);
Loading history...
114
        }
115
116
        $contents = <<<SQL
117
{$createTableSql}{$dropRecordsString}        
118
LOAD DATA LOCAL INFILE '{$this->outputFilePathname}' 
119
INTO TABLE {$this->tableName}
120
CHARACTER SET utf8
121
FIELDS 
122
    TERMINATED BY '{$delimiter}'
123
    ENCLOSED BY '{$enclosure}'
124
    ESCAPED BY ''
125
LINES 
126
    TERMINATED BY '\\n'
127
IGNORE $rowsToIgnore LINES
128
$headers
129
$autoIncrementString
130
;
131
SQL;
132
        file_put_contents($this->sqlOutputFilePathname, $contents);
133
    }
134
}
135