MysqlTableExporter::export()   C
last analyzed

Complexity

Conditions 9
Paths 56

Size

Total Lines 62
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 62
c 0
b 0
f 0
rs 6.6867
cc 9
eloc 39
nc 56
nop 1

How to fix   Long Method   

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
namespace Graze\DataDb\Export\Table;
4
5
use Graze\DataDb\Export\Query\QueryExporter;
6
use Graze\DataDb\Helper\MysqlHelper;
7
use Graze\DataDb\QueryNode;
8
use Graze\DataDb\SourceTableNodeInterface;
9
use Graze\DataDb\TableNodeInterface;
10
use Graze\DataFile\Format\FormatAwareInterface;
11
use Graze\DataFile\Format\FormatInterface;
12
use Graze\DataFile\Helper\Builder\BuilderAwareInterface;
13
use Graze\DataFile\Helper\FileHelper;
14
use Graze\DataFile\Helper\OptionalLoggerTrait;
15
use Graze\DataFile\Helper\Process\ProcessTrait;
16
use Graze\DataFile\Modify\ReFormat;
17
use Graze\DataFile\Node\FileNodeInterface;
18
use Graze\DataFile\Node\LocalFileNodeInterface;
19
use InvalidArgumentException;
20
use Psr\Log\LoggerAwareInterface;
21
use Psr\Log\LogLevel;
22
23
class MysqlTableExporter implements TableExporterInterface, BuilderAwareInterface, LoggerAwareInterface
24
{
25
    use OptionalLoggerTrait;
26
    use ProcessTrait;
27
    use FileHelper;
28
29
    /** @var FileNodeInterface */
30
    private $file;
31
    /** @var FormatInterface */
32
    private $format;
33
    /** @var string */
34
    private $host;
35
    /** @var int */
36
    private $port;
37
    /** @var string */
38
    private $user;
39
    /** @var string */
40
    private $pass;
41
42
    /**
43
     * MysqlTableExporter constructor.
44
     *
45
     * @param string            $host
46
     * @param int               $port
47
     * @param string            $user
48
     * @param string            $pass
49
     * @param FileNodeInterface $file
50
     * @param FormatInterface   $format
51
     */
52
    public function __construct(
53
        $host,
54
        $port,
55
        $user,
56
        $pass,
57
        FileNodeInterface $file = null,
58
        FormatInterface $format = null
59
    ) {
60
        $this->host = $host;
61
        $this->port = $port;
62
        $this->user = $user;
63
        $this->pass = $pass;
64
        $this->file = $file;
65
        $this->format = $format;
66
67
        if (!is_null($file)
68
            && is_null($this->format)
69
            && $file instanceof FormatAwareInterface
70
        ) {
71
            $this->format = $file->getFormat();
72
        }
73
74
        if ($this->file) {
75
            if ($this->file->exists()) {
76
                throw new InvalidArgumentException("The provided file: {$this->file} already exists");
77
            }
78
            if (!$this->file instanceof LocalFileNodeInterface) {
79
                throw new InvalidArgumentException("The provided file: {$this->file} is not a local file");
80
            }
81
        }
82
    }
83
84
    /**
85
     * @param TableNodeInterface $table
86
     *
87
     * @return FileNodeInterface
88
     */
89
    public function export(TableNodeInterface $table)
90
    {
91
        if (is_null($this->file)) {
92
            $this->file = $this->getTemporaryFile();
93
        }
94
95
        $targetFormat = $this->format;
96
97
        $helper = $this->getBuilder()->build(MysqlHelper::class);
98
99
        if (is_null($this->format)
100
            || !$helper->isValidExportFormat($this->format)
101
        ) {
102
            $this->format = $helper->getDefaultExportFormat();
103
        }
104
        $targetFormat = $targetFormat ?: $this->format;
105
106
        $this->log(LogLevel::INFO, "Exporting mysql table: {table} to file: {file}", [
107
            'table' => $table,
108
            'file'  => $this->file,
109
        ]);
110
111
        if (count($table->getColumns()) > 0) {
112
            $exporter = $this->getBuilder()->build(QueryExporter::class, $this->file, $targetFormat);
113
            list ($sql, $bind) = $table->getAdapter()->getDialect()->getSelectSyntax($table);
114
            return $exporter->export($this->getBuilder()->build(QueryNode::class, $table->getAdapter(), $sql, $bind));
115
        }
116
117
        if ($table instanceof SourceTableNodeInterface) {
118
            $where = ($table->getWhere() ? "--where=" . escapeshellarg($table->getWhere()) . " " : '');
119
        } else {
120
            $where = '';
121
        }
122
123
        $cmd = "set -e; set -o pipefail; " .
124
            "mysqldump -h{$this->host} -u{$this->user} -p{$this->pass} -P{$this->port} " .
125
            "--no-create-info --compact --compress --quick --skip-extended-insert --single-transaction " .
126
            "--skip-tz-utc --order-by-primary " .
127
            $where .
128
            "{$table->getSchema()} {$table->getTable()} " .
129
            "| grep '^INSERT INTO' " .
130
            "| perl -p -e 's/^INSERT INTO `[^`]+` VALUES \\((.+)\\)\\;\\s?$/\\1\\n/' " .
131
            "| perl -p -e 's/(?<!\\\\)((?:\\\\{2})*)\\\\n/\\1\\\\\\n/g;s/(?<!\\\\)((?:\\\\{2})*)\\\\r/\\1\\\\\\r/g' " . // convert fake new lines into actual new lines again
132
            ">> " . escapeshellarg($this->file->getPath());
133
134
        $this->log(LogLevel::DEBUG, "Executing Command:\n{cmd}", ['cmd' => $cmd]);
135
136
        // add bash -c here to allow set -e and set -o pipefail to handle when a mid pipe process fails
137
        $cmd = sprintf("bash -c %s", escapeshellarg($cmd));
138
139
        $process = $this->getProcess($cmd);
140
        $process->setTimeout(null); // no timeout
141
        $process->mustRun();
142
143
        if ($targetFormat !== $this->format) {
144
            /** @var ReFormat $reFormat */
145
            $reFormat = $this->getBuilder()->build(ReFormat::class);
146
            return $reFormat->reFormat($this->file, $targetFormat, null, $this->format, ['keepOldFile' => false]);
147
        }
148
149
        return $this->file;
150
    }
151
}
152