forked from smindel/silverstripe-sqlite3
/
SQLiteDatabaseConfigurationHelper.php
238 lines (214 loc) · 7.29 KB
/
SQLiteDatabaseConfigurationHelper.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<?php
namespace SilverStripe\SQLite;
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
use SilverStripe\Dev\Install\DatabaseConfigurationHelper;
use SQLite3;
use PDO;
use Exception;
/**
* This is a helper class for the SS installer.
*
* It does all the specific checking for SQLiteDatabase
* to ensure that the configuration is setup correctly.
*/
class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
{
/**
* Create a connection of the appropriate type
*
* @skipUpgrade
* @param array $databaseConfig
* @param string $error Error message passed by value
* @return mixed|null Either the connection object, or null if error
*/
protected function createConnection($databaseConfig, &$error)
{
$error = null;
try {
if (!file_exists($databaseConfig['path'])) {
self::create_db_dir($databaseConfig['path']);
self::secure_db_dir($databaseConfig['path']);
}
$file = $databaseConfig['path'] . '/' . $databaseConfig['database'];
$conn = null;
switch ($databaseConfig['type']) {
case 'SQLite3Database':
if (empty($databaseConfig['key'])) {
$conn = @new SQLite3($file, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE);
} else {
$conn = @new SQLite3(
$file,
SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE,
$databaseConfig['key']
);
}
break;
case 'SQLite3PDODatabase':
// May throw a PDOException if fails
$conn = @new PDO("sqlite:$file");
break;
default:
$error = 'Invalid connection type: ' . $databaseConfig['type'];
return null;
}
if ($conn) {
return $conn;
} else {
$error = 'Unknown connection error';
return null;
}
} catch (Exception $ex) {
$error = $ex->getMessage();
return null;
}
}
public function requireDatabaseFunctions($databaseConfig)
{
$data = DatabaseAdapterRegistry::get_adapter($databaseConfig['type']);
return !empty($data['supported']);
}
public function requireDatabaseServer($databaseConfig)
{
$path = $databaseConfig['path'];
$error = '';
$success = false;
if (!$path) {
$error = 'No database path provided';
} elseif (is_writable($path) || (!file_exists($path) && is_writable(dirname($path)))) {
// check if folder is writeable
$success = true;
} else {
$error = "Permission denied";
}
return array(
'success' => $success,
'error' => $error,
'path' => $path
);
}
/**
* Ensure a database connection is possible using credentials provided.
*
* @todo Validate path
*
* @param array $databaseConfig Associative array of db configuration, e.g. "type", "path" etc
* @return array Result - e.g. array('success' => true, 'error' => 'details of error')
*/
public function requireDatabaseConnection($databaseConfig)
{
// Do additional validation around file paths
if (empty($databaseConfig['path'])) {
return array(
'success' => false,
'error' => "Missing directory path"
);
}
if (empty($databaseConfig['database'])) {
return array(
'success' => false,
'error' => "Missing database filename"
);
}
// Create and secure db directory
$path = $databaseConfig['path'];
$dirCreated = self::create_db_dir($path);
if (!$dirCreated) {
return array(
'success' => false,
'error' => sprintf('Cannot create path: "%s"', $path)
);
}
$dirSecured = self::secure_db_dir($path);
if (!$dirSecured) {
return array(
'success' => false,
'error' => sprintf('Cannot secure path through .htaccess: "%s"', $path)
);
}
$conn = $this->createConnection($databaseConfig, $error);
$success = !empty($conn);
return array(
'success' => $success,
'connection' => $conn,
'error' => $error
);
}
public function getDatabaseVersion($databaseConfig)
{
$version = 0;
/** @skipUpgrade */
switch ($databaseConfig['type']) {
case 'SQLite3Database':
$info = SQLite3::version();
$version = trim($info['versionString']);
break;
case 'SQLite3PDODatabase':
// Fallback to using sqlite_version() query
$conn = $this->createConnection($databaseConfig, $error);
if ($conn) {
$version = $conn->getAttribute(PDO::ATTR_SERVER_VERSION);
}
break;
}
return $version;
}
public function requireDatabaseVersion($databaseConfig)
{
$success = false;
$error = '';
$version = $this->getDatabaseVersion($databaseConfig);
if ($version) {
$success = version_compare($version, '3.3', '>=');
if (!$success) {
$error = "Your SQLite3 library version is $version. It's recommended you use at least 3.3.";
}
}
return array(
'success' => $success,
'error' => $error
);
}
public function requireDatabaseOrCreatePermissions($databaseConfig)
{
$conn = $this->createConnection($databaseConfig, $error);
$success = $alreadyExists = !empty($conn);
return array(
'success' => $success,
'alreadyExists' => $alreadyExists,
);
}
/**
* Creates the provided directory and prepares it for
* storing SQLlite. Use {@link secure_db_dir()} to
* secure it against unauthorized access.
*
* @param String $path Absolute path, usually with a hidden folder.
* @return boolean
*/
public static function create_db_dir($path)
{
return file_exists($path) || mkdir($path);
}
/**
* Secure the provided directory via web-access
* by placing a .htaccess file in it.
* This is just required if the database directory
* is placed within a publically accessible webroot (the
* default path is in a hidden folder within assets/).
*
* @param String $path Absolute path, containing a SQLite datatbase
* @return boolean
*/
public static function secure_db_dir($path)
{
return (is_writeable($path)) ? file_put_contents($path . '/.htaccess', 'deny from all') : false;
}
public function requireDatabaseAlterPermissions($databaseConfig)
{
// no concept of table-specific permissions; If you can connect you can alter schema
return array(
'success' => true,
'applies' => false
);
}
}