NativeClient::getProcess()   F
last analyzed

Complexity

Conditions 28
Paths 385

Size

Total Lines 146
Code Lines 87

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 87
c 2
b 0
f 0
dl 0
loc 146
rs 1.0208
cc 28
nc 385
nop 2

How to fix   Long Method    Complexity   

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
0 ignored issues
show
Coding Style introduced by
Missing file doc comment
Loading history...
3
namespace Db3v4l\Core\SqlExecutor\Forked;
4
5
use Db3v4l\API\Interfaces\SqlExecutor\Forked\CommandExecutor;
6
use Db3v4l\API\Interfaces\SqlExecutor\Forked\FileExecutor;
7
use Db3v4l\API\Interfaces\SqlExecutor\Forked\ShellExecutor;
8
use Db3v4l\Util\Process;
9
10
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
11
 * @todo allow to inject path of db clients via setter/constructor
12
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
13
class NativeClient extends ForkedExecutor implements CommandExecutor, FileExecutor, ShellExecutor
14
{
15
    const EXECUTION_STRATEGY = 'NativeClient';
16
    const EXECUTE_SHELL = 2;
17
18
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
19
     * @param string $sql
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
20
     * @return Process
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
21
     */
22
    public function getExecuteStatementProcess($sql)
23
    {
24
        return $this->getProcess($sql, self::EXECUTE_COMMAND);
25
    }
26
27
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
28
     * @param string $filename
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
29
     * @return Process
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
30
     */
31
    public function getExecuteFileProcess($filename)
32
    {
33
        return $this->getProcess($filename, self::EXECUTE_FILE);
34
    }
35
36
    public function getExecuteShellProcess()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getExecuteShellProcess()
Loading history...
37
    {
38
        return $this->getProcess(null, self::EXECUTE_SHELL);
39
    }
40
41
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
42
     * @param string $sqlOrFilename
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
43
     * @param int $action
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 4 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
44
     * @return Process
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
45
     */
46
    protected function getProcess($sqlOrFilename, $action = self::EXECUTE_COMMAND)
47
    {
48
        $clientType = $this->getDbClientType($this->databaseConfiguration);
49
50
        // pass on _all_ env vars, including PATH. Not doing so is deprecated...
51
        $env = null;
52
53
        switch ($clientType) {
54
            case 'mysql':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
55
                $command = 'mysql';
56
                $options = [
57
                    '--host=' . $this->databaseConfiguration['host'],
58
                    '--port=' . $this->databaseConfiguration['port'] ?? '3306',
59
                    '--user=' . $this->databaseConfiguration['user'],
60
                    '-p' . $this->databaseConfiguration['password'],
61
                    '--binary-mode', // 'It also disables all mysql commands except charset and delimiter in non-interactive mode (for input piped to mysql or loaded using the source command)'
62
                    '-t',
63
                ];
64
                if (isset($this->databaseConfiguration['dbname'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
65
                    $options[] = $this->databaseConfiguration['dbname'];
66
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
67
                if ($action == self::EXECUTE_COMMAND) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
68
                    $options[] = '--execute=' . $sqlOrFilename;
69
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
70
               // $env = [
71
                    // problematic when wrapping the process in a call to `time`...
72
                    //'MYSQL_PWD' => $this->databaseConfiguration['password'],
73
                //];
74
                break;
75
76
            case 'psql':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
77
                $command = 'psql';
78
                $connectString = "postgresql://".$this->databaseConfiguration['user'].":".$this->databaseConfiguration['password'].
79
                    "@{$this->databaseConfiguration['host']}:".($this->databaseConfiguration['port'] ?? '5432').'/';
80
                if (isset($this->databaseConfiguration['dbname'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
81
                    $connectString .= $this->databaseConfiguration['dbname'];
82
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
83
                $options = [
84
                    $connectString,
85
                    '-Pfooter=off'
86
                ];
87
                // NB: this triggers a different behaviour that piping multiple commands to stdin, namely
88
                // it wraps all of the commands in a transaction and allows either sql commands or a single meta-command
89
                if ($action == self::EXECUTE_COMMAND) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
90
                    $options[] = '--command=' . $sqlOrFilename;
91
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
92
                //$env = [
93
                    // problematic when wrapping the process in a call to `time`...
94
                    //'PGPASSWORD' => $this->databaseConfiguration['password'],
95
                //];
96
                break;
97
98
            case 'sqlcmd':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
99
                $command = 'sqlcmd';
100
                $options = [
101
                    '-S' . $this->databaseConfiguration['host'] . ($this->databaseConfiguration['port'] != '' ?  ',' . $this->databaseConfiguration['port'] : ''),
102
                    '-U' . $this->databaseConfiguration['user'],
103
                    '-P' . $this->databaseConfiguration['password'],
104
                    '-r1',
105
                    '-b',
106
                ];
107
                if (isset($this->databaseConfiguration['dbname'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
108
                    $options[] = '-d' . $this->databaseConfiguration['dbname'];
109
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
110
                if ($action == self::EXECUTE_FILE) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
111
                    $options[] = '-i' . $sqlOrFilename;
112
                } elseif ($action == self::EXECUTE_COMMAND) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
113
                    $options[] = '-Q' . $sqlOrFilename;
114
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
115
                break;
116
117
            case 'sqlite':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
118
                $command = 'sqlite3';
119
                // 'path' is the full path to the 'master' db (for Doctrine compatibility).
120
                //  non-master dbs are supposed to reside in the same directory
121
                if (isset($this->databaseConfiguration['dbname'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
122
                    $options[] = dirname($this->databaseConfiguration['path']) . '/' . $this->databaseConfiguration['dbname'] . '.sqlite';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.
Loading history...
123
                } else {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
124
                    $options[] = $this->databaseConfiguration['path'];
125
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
126
127
                if ($action == self::EXECUTE_COMMAND) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
128
                    $options[] = $sqlOrFilename;
129
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
130
                break;
131
132
            case 'sqlplus':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
133
                /// @todo disable execution of dangerous (or all) SQLPLUS commands.
134
                ///       See the list at https://docs.oracle.com/en/database/oracle/oracle-database/18/sqpug/SQL-Plus-command-reference.html#GUID-177F24B7-D154-4F8B-A05B-7568079800C6
135
                $command = 'sqlplus';
136
                $connectionIdentifier = '//' . $this->databaseConfiguration['host'] .
137
                    ($this->databaseConfiguration['port'] != '' ?  ':' . $this->databaseConfiguration['port'] : '');
138
                // nb: for oracle, if we use pdbs to map 'databases', they get a new service name
139
                /// @todo allow support for _not_ doing that, and using schemas as 'databases'
140
                if (isset($this->databaseConfiguration['servicename'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
141
                    $connectionIdentifier .= '/' . $this->databaseConfiguration['servicename'];
142
                } else {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
143
                    if ($this->databaseConfiguration['dbname'] != '') {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
144
                        $connectionIdentifier .= '/' . $this->databaseConfiguration['dbname'];
145
                    }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
146
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
147
                $options = [
148
                    '-L', // 'attempts to log in just once, instead of reprompting on error'
149
                    '-NOLOGINTIME',
150
                    '-S',
151
                    $this->databaseConfiguration['user'] . '/' . $this->databaseConfiguration['password'] . '@' . $connectionIdentifier,
152
                ];
153
                /// @todo make this optional somehow / allow SYSOPER as alternative
154
                if (strtolower($this->databaseConfiguration['user']) === 'sys') {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
155
                    $options[] = 'AS';
156
                    $options[] = 'SYSDBA';
157
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
158
                if ($action == self::EXECUTE_FILE) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
159
                    /// @todo wouldn't it be better to create another temp file with prefixed the sqlplus commands?
160
                    //$options[] = '@' . $sqlOrFilename;
161
                    $sqlOrFilename = "WHENEVER OSERROR EXIT FAILURE;\nWHENEVER SQLERROR EXIT SQL.SQLCODE;\nSET PAGESIZE 50000;\nSET FEEDBACK OFF;\n" . file_get_contents($sqlOrFilename);
162
                    $action = self::EXECUTE_COMMAND;
163
                } else {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
164
                    $sqlOrFilename = "WHENEVER OSERROR EXIT FAILURE;\nWHENEVER SQLERROR EXIT SQL.SQLCODE;\nSET PAGESIZE 50000;\nSET FEEDBACK OFF;\n" . $sqlOrFilename;
165
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
166
167
                break;
168
169
                default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 16
Loading history...
170
                throw new \OutOfBoundsException("Unsupported db client '$clientType'");
0 ignored issues
show
Coding Style introduced by
Case breaking statement indented incorrectly; expected 20 spaces, found 16
Loading history...
171
        }
172
173
        $commandLine = $this->buildCommandLine($command, $options);
174
175
        /// @todo investigate: for psql is this better done via --file ?
176
        if ($action == self::EXECUTE_FILE && $clientType != 'sqlsrv' && $clientType != 'sqlplus') {
177
            $commandLine .= ' < ' . escapeshellarg($sqlOrFilename);
178
        }
179
180
        if ($action == self::EXECUTE_COMMAND && $clientType == 'sqlplus') {
181
            $commandLine .= " << 'SQLEOF'\n" . $sqlOrFilename . "\nSQLEOF";
182
        }
183
184
        $process = Process::fromShellCommandline($commandLine, null, $env);
185
186
        if ($action == self::EXECUTE_COMMAND && $clientType == 'sqlplus') {
187
            // The way Symfony Process deals with sighchildEnabled breaks EOF handling. We disable it
188
            $process->forceSigchildEnabledIndividually(false);
189
        }
190
191
        return $process;
192
    }
193
194
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
195
     * @see https://www.doctrine-project.org/projects/doctrine-dbal/en/2.10/reference/configuration.html for supported aliases
0 ignored issues
show
Coding Style introduced by
Tag @see cannot be grouped with parameter tags in a doc comment
Loading history...
Coding Style introduced by
Tag value for @see tag indented incorrectly; expected 4 spaces but found 1
Loading history...
196
     * @param array $connectionConfiguration
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
197
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
198
     */
199
    protected function getDbClientType(array $connectionConfiguration)
200
    {
201
        $vendor = $connectionConfiguration['vendor'];
202
        return str_replace(
203
            array('mariadb', 'mssql', 'oracle', 'percona', 'postgresql'),
204
            array('mysql', 'sqlcmd', 'sqlplus', 'mysql', 'psql'),
205
            $vendor
206
        );
207
    }
208
209
    /**
210
     * Transforms a resultSet string, formatted as per the default way of the db client, into an array
211
     * @todo tested on single-column SELECTs so far
0 ignored issues
show
Coding Style introduced by
There must be exactly one blank line before the tags in a doc comment
Loading history...
Coding Style introduced by
Tag @todo cannot be grouped with parameter tags in a doc comment
Loading history...
Coding Style introduced by
Tag value for @todo tag indented incorrectly; expected 3 spaces but found 1
Loading history...
212
     * @param $string
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
213
     * @return string[]
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
214
     */
215
    public function resultSetToArray($string)
216
    {
217
        switch ($this->databaseConfiguration['vendor']) {
218
            case 'mariadb':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
219
            case 'mysql':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
220
            case 'percona':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
221
                // 'table format', triggered by using the -t option for the client
222
                // NB: both mariadb and mysql output no headers line when resultset has 0 rows
223
                $output = explode("\n", $string);
224
                array_shift($output); // '+--+'
225
                array_shift($output); // headers
226
                array_shift($output); // '+--+'
227
                array_pop($output); // '+--+'
228
                foreach($output as &$line) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
Coding Style introduced by
Expected "foreach (...) {\n"; found "foreach(...) {\n"
Loading history...
229
                    $line = trim($line, '|');
230
                    $line = trim($line);
231
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
232
                return $output;
233
            case 'oracle':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
234
                $output = explode("\n", $string);
235
                array_shift($output); // empty line
236
                array_shift($output); // headers
237
                array_shift($output); // '---'
238
                foreach($output as &$line) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
Coding Style introduced by
Expected "foreach (...) {\n"; found "foreach(...) {\n"
Loading history...
239
                    $line = trim($line);
240
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
241
                return $output;
242
            case 'postgresql':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
243
                $output = explode("\n", $string);
244
                array_shift($output); // headers
245
                array_shift($output); // '---'
246
                //array_pop($output); // '(N rows)'
247
                foreach($output as &$line) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
Coding Style introduced by
Expected "foreach (...) {\n"; found "foreach(...) {\n"
Loading history...
248
                    $line = trim($line);
249
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
250
                return $output;
251
            case 'sqlite':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
252
                $output = explode("\n", $string);
253
                return $output;
254
            case 'mssql':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
255
                $output = explode("\n", $string);
256
                array_shift($output);
257
                array_shift($output); // '---'
258
                array_pop($output); // blank line
259
                array_pop($output); // '(N rows affected)'
260
                foreach($output as &$line) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
Coding Style introduced by
Expected "foreach (...) {\n"; found "foreach(...) {\n"
Loading history...
261
                    $line = trim($line);
262
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
263
                return $output;
264
            default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
265
                throw new \OutOfBoundsException("Unsupported database type '{$this->databaseConfiguration['vendor']}'");
266
        }
267
    }
268
}
269