Passed
Pull Request — master (#7)
by Wilmer
12:36
created

Command   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 47
c 0
b 0
f 0
dl 0
loc 119
rs 10
wmc 15

4 Methods

Rating   Name   Duplication   Size   Complexity  
A extractUsedParams() 0 16 4
A splitStatements() 0 23 5
A queryInternal() 0 27 3
A execute() 0 23 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Sqlite\Command;
6
7
use Yiisoft\Db\Commands\Command as BaseCommand;
8
use Yiisoft\Db\Sqlite\Token\SqlToken;
9
use Yiisoft\Db\Sqlite\Token\SqlTokenizer;
10
use Yiisoft\Strings\StringHelper;
11
12
/**
13
 * Command represents an SQLite's SQL statement to be executed against a database.
14
 *
15
 * {@inheritdoc}
16
 */
17
class Command extends BaseCommand
18
{
19
    /**
20
     * {@inheritdoc}
21
     */
22
    public function execute(): int
23
    {
24
        $sql = $this->getSql();
25
26
        $params = $this->params;
27
28
        $statements = $this->splitStatements($sql, $params);
29
30
        if ($statements === false) {
0 ignored issues
show
introduced by
The condition $statements === false is always true.
Loading history...
31
            return parent::execute();
32
        }
33
34
        $result = 0;
35
36
        foreach ($statements as $statement) {
37
            [$statementSql, $statementParams] = $statement;
38
            $this->setSql($statementSql)->bindValues($statementParams);
39
            $result = parent::execute();
40
        }
41
42
        $this->setSql($sql)->bindValues($params);
43
44
        return $result;
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    protected function queryInternal($method, $fetchMode = null)
51
    {
52
        $sql = $this->getSql();
53
54
        $params = $this->params;
55
56
        $statements = $this->splitStatements($sql, $params);
57
58
        if ($statements === false) {
0 ignored issues
show
introduced by
The condition $statements === false is always true.
Loading history...
59
            return parent::queryInternal($method, $fetchMode);
60
        }
61
62
        [$lastStatementSql, $lastStatementParams] = array_pop($statements);
63
64
        foreach ($statements as $statement) {
65
            [$statementSql, $statementParams] = $statement;
66
            $this->setSql($statementSql)->bindValues($statementParams);
67
            parent::execute();
68
        }
69
70
        $this->setSql($lastStatementSql)->bindValues($lastStatementParams);
71
72
        $result = parent::queryInternal($method, $fetchMode);
73
74
        $this->setSql($sql)->bindValues($params);
75
76
        return $result;
77
    }
78
79
    /**
80
     * Splits the specified SQL code into individual SQL statements and returns them or `false` if there's a single
81
     * statement.
82
     *
83
     * @param string $sql
84
     * @param array $params
85
     *
86
     * @return string[]|false
87
     */
88
    private function splitStatements($sql, $params)
89
    {
90
        $semicolonIndex = strpos($sql, ';');
91
92
        if ($semicolonIndex === false || $semicolonIndex === StringHelper::byteLength($sql) - 1) {
93
            return false;
94
        }
95
96
        $tokenizer = new SqlTokenizer($sql);
97
98
        $codeToken = $tokenizer->tokenize();
99
100
        if (count($codeToken->getChildren()) === 1) {
101
            return false;
102
        }
103
104
        $statements = [];
105
106
        foreach ($codeToken->getChildren() as $statement) {
107
            $statements[] = [$statement->getSql(), $this->extractUsedParams($statement, $params)];
108
        }
109
110
        return $statements;
111
    }
112
113
    /**
114
     * Returns named bindings used in the specified statement token.
115
     *
116
     * @param SqlToken $statement
117
     * @param array $params
118
     * @return array
119
     */
120
    private function extractUsedParams(SqlToken $statement, $params)
121
    {
122
        preg_match_all('/(?P<placeholder>[:][a-zA-Z0-9_]+)/', $statement->getSql(), $matches, PREG_SET_ORDER);
123
124
        $result = [];
125
126
        foreach ($matches as $match) {
127
            $phName = ltrim($match['placeholder'], ':');
128
            if (isset($params[$phName])) {
129
                $result[$phName] = $params[$phName];
130
            } elseif (isset($params[':' . $phName])) {
131
                $result[':' . $phName] = $params[':' . $phName];
132
            }
133
        }
134
135
        return $result;
136
    }
137
}
138