• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nextras / dbal / 24104451934

07 Apr 2026 09:07PM UTC coverage: 87.088% (+0.2%) from 86.873%
24104451934

push

github

web-flow
Merge pull request #130 from nextras/sqlite

Add Sqlite PDO driver

173 of 194 new or added lines in 9 files covered. (89.18%)

2165 of 2486 relevant lines covered (87.09%)

5.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.43
/src/Platforms/SqlitePlatform.php
1
<?php declare(strict_types = 1);
2

3
namespace Nextras\Dbal\Platforms;
4

5

6
use DateInterval;
7
use DateTimeInterface;
8
use Nextras\Dbal\Drivers\IDriver;
9
use Nextras\Dbal\Exception\NotSupportedException;
10
use Nextras\Dbal\IConnection;
11
use Nextras\Dbal\Platforms\Data\Column;
12
use Nextras\Dbal\Platforms\Data\ForeignKey;
13
use Nextras\Dbal\Platforms\Data\Fqn;
14
use Nextras\Dbal\Platforms\Data\Table;
15
use Nextras\Dbal\Utils\DateTimeHelper;
16
use Nextras\Dbal\Utils\JsonHelper;
17
use Nextras\Dbal\Utils\StrictObjectTrait;
18
use Nextras\MultiQueryParser\IMultiQueryParser;
19
use Nextras\MultiQueryParser\SqliteMultiQueryParser;
20
use function bin2hex;
21
use function explode;
22
use function intdiv;
23
use function str_replace;
24
use function strtr;
25
use function strval;
26

27

28
class SqlitePlatform implements IPlatform
29
{
30
        use StrictObjectTrait;
31

32

33
        public const NAME = 'sqlite';
34

35
        /** @var IConnection */
36
        private $connection;
37

38
        /** @var IDriver */
39
        private $driver;
40

41

42
        public function __construct(IConnection $connection)
43
        {
44
                $this->connection = $connection;
6✔
45
                $this->driver = $connection->getDriver();
6✔
46
        }
6✔
47

48

49
        public function getName(): string
50
        {
51
                return self::NAME;
6✔
52
        }
53

54

55
        public function getTables(?string $schema = null): array
56
        {
57
                $result = $this->connection->query(/** @lang SQLite */ "
6✔
58
                        SELECT name, type FROM sqlite_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
59
                        UNION ALL
60
                        SELECT name, type FROM sqlite_temp_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
61
                ");
62

63
                $tables = [];
6✔
64
                foreach ($result as $row) {
6✔
65
                        $table = new Table(
6✔
66
                                fqnName: new Fqn('', (string) $row->name),
6✔
67
                                isView: $row->type === 'view',
6✔
68
                        );
69
                        $tables[$table->fqnName->getUnescaped()] = $table;
6✔
70
                }
71
                return $tables;
6✔
72
        }
73

74

75
        public function getColumns(string $table, ?string $schema = null): array
76
        {
77
                $raw = $this->connection->query(/** @lang SQLite */ "
6✔
78
                        SELECT sql FROM sqlite_master WHERE type = 'table' AND name = %s
79
                        UNION ALL
80
                        SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = %s
81
                ", $table, $table)->fetchField();
6✔
82

83
                $result = $this->connection->query(/** @lang SQLite */ "
6✔
84
                        PRAGMA table_info(%table)
85
                ", $table);
86

87
                $columns = [];
6✔
88
                foreach ($result as $row) {
6✔
89
                        $column = $row->name;
6✔
90
                        $pattern = "~(\"$column\"|`$column`|\\[$column\\]|$column)\\s+[^,]+\\s+PRIMARY\\s+KEY\\s+AUTOINCREMENT~Ui";
6✔
91

92
                        $type = explode('(', $row->type);
6✔
93
                        $column = new Column(
6✔
94
                                name: (string) $row->name,
6✔
95
                                type: $type[0],
6✔
96
                                size: isset($type[1]) ? (int) $type[1] : 0,
6✔
97
                                default: $row->dflt_value !== null ? (string) $row->dflt_value : null,
6✔
98
                                isPrimary: $row->pk === 1,
6✔
99
                                isAutoincrement: preg_match($pattern, (string) $raw) === 1,
6✔
100
                                isUnsigned: false,
6✔
101
                                isNullable: $row->notnull === 0,
6✔
102
                                meta: [],
6✔
103
                        );
104
                        $columns[$column->name] = $column;
6✔
105
                }
106
                return $columns;
6✔
107
        }
108

109

110
        public function getForeignKeys(string $table, ?string $schema = null): array
111
        {
112
                $result = $this->connection->query(/** @lang SQLite */ "
6✔
113
                        PRAGMA foreign_key_list(%table)
114
                ", $table);
115

116
                $foreignKeys = [];
6✔
117
                foreach ($result as $row) {
6✔
118
                        $foreignKey = new ForeignKey(
6✔
119
                                fqnName: new Fqn('', (string) $row->id),
6✔
120
                                column: (string) $row->from,
6✔
121
                                refTable: new Fqn('', (string) $row->table),
6✔
122
                                refColumn: (string) $row->to,
6✔
123
                        );
124
                        $foreignKeys[$foreignKey->column] = $foreignKey;
6✔
125
                }
126
                return $foreignKeys;
6✔
127
        }
128

129

130
        public function getPrimarySequenceName(string $table, ?string $schema = null): ?string
131
        {
132
                return null;
6✔
133
        }
134

135

136
        public function formatString(string $value): string
137
        {
138
                return $this->driver->convertStringToSql($value);
6✔
139
        }
140

141

142
        public function formatStringLike(string $value, int $mode)
143
        {
144
                $value = strtr($value, [
6✔
145
                        "'" => "''",
2✔
146
                        '!' => '!!',
147
                        '%' => '!%',
148
                        '_' => '!_',
149
                ]);
150
                return ($mode <= 0 ? "'%" : "'") . $value . ($mode >= 0 ? "%'" : "'") . " ESCAPE '!'";
6✔
151
        }
152

153

154
        public function formatJson(mixed $value): string
155
        {
NEW
156
                $encoded = JsonHelper::safeEncode($value);
×
NEW
157
                return $this->formatString($encoded);
×
158
        }
159

160

161
        public function formatBool(bool $value): string
162
        {
163
                return $value ? '1' : '0';
6✔
164
        }
165

166

167
        public function formatIdentifier(string $value): string
168
        {
169
                return '[' . str_replace([']', '.'], [']]', '].['], $value) . ']';
6✔
170
        }
171

172

173
        public function formatDateTime(DateTimeInterface $value): string
174
        {
175
                $value = DateTimeHelper::convertToTimezone($value, $this->driver->getConnectionTimeZone());
6✔
176
                return strval(((int) $value->format('U')) * 1000 + intdiv((int) $value->format('u'), 1000));
6✔
177
        }
178

179

180
        public function formatLocalDateTime(DateTimeInterface $value): string
181
        {
182
                return "'" . $value->format('Y-m-d H:i:s.u') . "'";
6✔
183
        }
184

185

186
        public function formatLocalDate(DateTimeInterface $value): string
187
        {
NEW
188
                return "'" . $value->format('Y-m-d') . "'";
×
189
        }
190

191

192
        public function formatDateInterval(DateInterval $value): string
193
        {
194
                throw new NotSupportedException();
6✔
195
        }
196

197

198
        public function formatBlob(string $value): string
199
        {
NEW
200
                return "X'" . bin2hex($value) . "'";
×
201
        }
202

203

204
        public function formatLimitOffset(?int $limit, ?int $offset): string
205
        {
206
                if ($limit === null && $offset === null) {
6✔
NEW
207
                        return '';
×
208
                } elseif ($limit === null) {
6✔
209
                        return 'LIMIT -1 OFFSET ' . $offset;
6✔
210
                } elseif ($offset === null) {
6✔
211
                        return "LIMIT $limit";
6✔
212
                } else {
213
                        return "LIMIT $limit OFFSET $offset";
6✔
214
                }
215
        }
216

217

218
        public function createMultiQueryParser(): IMultiQueryParser
219
        {
220
                if (!class_exists(SqliteMultiQueryParser::class)) {
6✔
NEW
221
                        throw new \RuntimeException("Missing nextras/multi-query-parser dependency. Install it first to use IPlatform::createMultiQueryParser().");
×
222
                }
223
                return new SqliteMultiQueryParser();
6✔
224
        }
225

226

227
        public function isSupported(int $feature): bool
228
        {
229
                static $supported = [
6✔
230
                        self::SUPPORT_QUERY_EXPLAIN => true,
3✔
231
                ];
232
                return isset($supported[$feature]);
6✔
233
        }
234
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc