MySQLDump::checkSizendLen()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 16
rs 9.9
cc 3
nc 4
nop 3
1
<?php
2
3
namespace Ballybran\Database;
4
5
use Ballybran\Helpers\Stdlib\CreateFiles;
6
use mysqli;
7
8
/**
9
 * MySQL database dump.
10
 *
11
 * @version    1.0
12
 */
13
class MySQLDump
14
{
15
    use CreateFiles;
16
    const MAX_SQL_SIZE = 1e6;
17
18
    const NONE = 0;
19
    const DROP = 1;
20
    const CREATE = 2;
21
    const DATA = 4;
22
    const TRIGGERS = 8;
23
    const ALL = 15; // DROP | CREATE | DATA | TRIGGERS
24
25
    private $size = 0;
26
    private $delTable;
27
    /** @var array */
28
    public $tables = [
29
        '*' => self::ALL,
30
    ];
31
32
    /** @var mysqli */
33
    private $connection;
34
35
36
    /**
37
     * Connects to database.
38
     * @param  mysqli connection
0 ignored issues
show
Bug introduced by
The type Ballybran\Database\connection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
39
     */
40
    public function __construct(mysqli $connection, $charset = 'utf8')
41
    {
42
        $this->connection = $connection;
43
44
        if ($connection->connect_errno) {
45
            throw new \Exception($connection->connect_error);
46
47
        } elseif (!$connection->set_charset($charset)) { // was added in MySQL 5.0.7 and PHP 5.0.5, fixed in PHP 5.1.5)
48
            throw new \Exception($connection->error);
49
        }
50
    }
51
52
53
    /**
54
     * Saves dump to the file.
55
     * @param  string filename
0 ignored issues
show
Bug introduced by
The type Ballybran\Database\filename was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
56
     * @return void
57
     */
58
    public function save($file)
59
    {
60
        $this->createWritableFolder($file);
61
        $file = DIR_STORAGE. $file;
62
        $handle = strcasecmp(substr($file, -3), '.gz') ? fopen($file, 'wb') : gzopen($file, 'wb');
63
        if (!$handle) {
0 ignored issues
show
introduced by
$handle is of type resource, thus it always evaluated to false.
Loading history...
64
            throw new \Exception("ERROR: Cannot write file '$file'.");
65
        }
66
        $this->write($handle);
67
    }
68
69
70
    /**
71
     * Writes dump to logical file.
72
     * @param  resource
73
     * @return void
74
     */
75
    public function write($handle = null)
76
    {
77
        if ($handle === null) {
78
            $handle = fopen('php://output', 'wb');
79
        } elseif (!is_resource($handle) || get_resource_type($handle) !== 'stream') {
80
            throw new \Exception('Argument must be stream resource.');
81
        }
82
83
        $tables = $views = [];
84
85
        $res = $this->connection->query('SHOW FULL TABLES');
86
        while ($row = $res->fetch_row()) {
87
            if ($row[1] === 'VIEW') {
88
                $views[] = $row[0];
89
            } else {
90
                $tables[] = $row[0];
91
            }
92
        }
93
        $res->close();
94
95
        $tables = array_merge($tables, $views); // views must be last
96
97
        $this->connection->query('LOCK TABLES `' . implode('` READ, `', $tables) . '` READ');
98
99
        $db = $this->connection->query('SELECT DATABASE()')->fetch_row();
100
        fwrite($handle, '-- Created at ' . date('j.n.Y G:i') . " using David Grudl MySQL Dump Utility\n"
101
            . (isset($_SERVER['HTTP_HOST']) ? "-- Host: $_SERVER[HTTP_HOST]\n" : '')
102
            . '-- MySQL Server: ' . $this->connection->server_info . "\n"
103
            . '-- Database: ' . $db[0] . "\n"
104
            . "\n"
105
            . "SET NAMES utf8;\n"
106
            . "SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO';\n"
107
            . "SET FOREIGN_KEY_CHECKS=0;\n"
108
            . "SET UNIQUE_CHECKS=0;\n"
109
            . "SET AUTOCOMMIT=0;\n"
110
        );
111
112
        foreach ($tables as $table) {
113
            $this->dumpTable($handle, $table);
114
        }
115
116
        fwrite($handle, "COMMIT;\n");
117
        fwrite($handle, "-- THE END\n");
118
119
        $this->connection->query('UNLOCK TABLES');
120
    }
121
122
123
    /**
124
     * Dumps table to logical file.
125
     * @param  resource
126
     * @return void
127
     */
128
    public function dumpTable($handle, $table)
129
    {
130
        $this->delTable = $this->delimite($table);
131
        $res = $this->connection->query("SHOW CREATE TABLE $this->delTable");
132
        $row = $res->fetch_assoc();
133
        $res->close();
134
135
        fwrite($handle, "-- --------------------------------------------------------\n\n");
136
137
        $mode = isset($this->tables[$table]) ? $this->tables[$table] : $this->tables['*'];
138
        $view = isset($row['Create View']);
139
140
        if ($mode & self::DROP) {
141
            fwrite($handle, 'DROP ' . ($view ? 'VIEW' : 'TABLE') . " IF EXISTS $this->delTable;\n\n");
142
        }
143
144
        if ($mode & self::CREATE) {
145
            fwrite($handle, $row[$view ? 'Create View' : 'Create Table'] . ";\n\n");
146
        }
147
148
        if (!$view && ($mode & self::DATA)) {
149
            fwrite($handle, 'ALTER ' . ($view ? 'VIEW' : 'TABLE') . ' ' . $this->delTable . " DISABLE KEYS;\n\n");
0 ignored issues
show
introduced by
The condition $view is always false.
Loading history...
150
            $numeric = [];
151
            $res = $this->connection->query("SHOW COLUMNS FROM $this->delTable");
152
            $cols = [];
153
            while ($row = $res->fetch_assoc()) {
154
                $col = $row['Field'];
155
                $cols[] = $this->delimite($col);
156
                $numeric[$col] = (bool)preg_match('#^[^(]*(BYTE|COUNTER|SERIAL|INT|LONG$|CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER)#i', $row['Type']);
157
            }
158
            $cols = '(' . implode(', ', $cols) . ')';
159
            $res->close();
160
161
            echo $this->mysqliUseResult($mode, $table, $numeric, $cols, $view, $handle);
162
        }
163
    }
164
165
        private function mysqliUseResult($mode, $table, $numeric, $cols, $view, $handle) {
166
            $this->size = 0;
167
            $res = $this->connection->query("SELECT * FROM $this->delTable", MYSQLI_USE_RESULT);
168
            while ($row = $res->fetch_assoc()) {
169
                $s = '(';
170
                foreach ($row as $key => $value) {
171
                    if ($value === null) {
172
                        $s .= "NULL,\t";
173
                    } elseif ($numeric[$key]) {
174
                        $s .= $value . ",\t";
175
                    } else {
176
                        $s .= "'" . $this->connection->real_escape_string($value) . "',\t";
177
                    }
178
                }
179
180
                echo $this->checkSizendLen($cols, $s, $handle);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->checkSizendLen($cols, $s, $handle) targeting Ballybran\Database\MySQLDump::checkSizendLen() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
181
            }
182
183
            $res->close();
184
            if ($this->size) {
185
                fwrite($handle, ";\n");
186
            }
187
            fwrite($handle, 'ALTER ' . ($view ? 'VIEW' : 'TABLE') . ' ' . $this->delTable . " ENABLE KEYS;\n\n");
188
            fwrite($handle, "\n");
189
        
190
191
192
            echo $this->trigger($mode, $handle, $table);
193
        
194
        fwrite($handle, "\n");
195
    
196
    }
197
    private function checkSizendLen($cols, $s, $handle) {
198
199
        if ($this->size == 0) {
200
            $s = "INSERT INTO $this->delTable $cols VALUES\n$s";
201
        } else {
202
            $s = ",\n$s";
203
        }
204
205
        $len = strlen($s) - 1;
206
        $s[$len - 1] = ')';
207
        fwrite($handle, $s, $len);
208
209
        $this->size += $len;
210
        if ($this->size > self::MAX_SQL_SIZE) {
211
            fwrite($handle, ";\n");
212
            $this->size = 0;
213
        }
214
    }
215
    private function trigger($mode, $handle, $table) {
216
        if ($mode & self::TRIGGERS) {
217
            $res = $this->connection->query("SHOW TRIGGERS LIKE '" . $this->connection->real_escape_string($table) . "'");
218
            if ($res->num_rows) {
219
                fwrite($handle, "DELIMITER ;;\n\n");
220
                while ($row = $res->fetch_assoc()) {
221
                    fwrite($handle, "CREATE TRIGGER {$this->delimite($row['Trigger'])} $row[Timing] $row[Event] ON $this->delTable FOR EACH ROW\n$row[Statement];;\n\n");
222
                }
223
                fwrite($handle, "DELIMITER ;\n\n");
224
            }
225
            $res->close();
226
        }
227
228
    }
229
230
231
    private function delimite($s)
232
    {
233
        return '`' . str_replace('`', '``', $s) . '`';
234
    }
235
236
}
237