1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @package install |
4
|
|
|
*/ |
5
|
|
|
namespace SymphonyCms\Installer\Lib; |
6
|
|
|
|
7
|
|
|
use DatabaseException; |
8
|
|
|
use DirectoryIterator; |
9
|
|
|
use Exception; |
10
|
|
|
use General; |
11
|
|
|
use Lang; |
12
|
|
|
use Symphony; |
13
|
|
|
|
14
|
|
|
class Updater extends Installer |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* This function returns an instance of the Updater class. It is the only way to |
18
|
|
|
* create a new Updater, as it implements the Singleton interface |
19
|
|
|
* |
20
|
|
|
* @return Updater |
21
|
|
|
*/ |
22
|
|
|
public static function instance() |
23
|
|
|
{ |
24
|
|
|
if (!(self::$_instance instanceof Updater)) { |
25
|
|
|
self::$_instance = new Updater; |
26
|
|
|
} |
27
|
|
|
|
28
|
|
|
return self::$_instance; |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Initialises the language by looking at the existing configuration |
33
|
|
|
*/ |
34
|
|
|
public static function initialiseLang() |
35
|
|
|
{ |
36
|
|
|
Lang::set(Symphony::Configuration()->get('lang', 'symphony'), false); |
|
|
|
|
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Initialises the configuration object by loading the existing |
41
|
|
|
* website config file |
42
|
|
|
* |
43
|
|
|
* @param array $data |
44
|
|
|
*/ |
45
|
|
|
public static function initialiseConfiguration(array $data = array()) |
46
|
|
|
{ |
47
|
|
|
parent::initialiseConfiguration(); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Overrides the `initialiseLog()` method and writes logs to `manifest/logs/update` |
52
|
|
|
* |
53
|
|
|
* @param string $filename |
54
|
|
|
* @return bool|void |
55
|
|
|
* @throws Exception |
56
|
|
|
*/ |
57
|
|
View Code Duplication |
public static function initialiseLog($filename = null) |
58
|
|
|
{ |
59
|
|
|
if (is_dir(INSTALL_LOGS) || General::realiseDirectory( |
60
|
|
|
INSTALL_LOGS, |
61
|
|
|
self::Configuration()->get('write_mode', 'directory') |
|
|
|
|
62
|
|
|
) |
63
|
|
|
) { |
64
|
|
|
parent::initialiseLog(INSTALL_LOGS . '/update'); |
65
|
|
|
} |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Overrides the default `initialiseDatabase()` method |
70
|
|
|
* This allows us to still use the normal accessor |
71
|
|
|
*/ |
72
|
|
|
public static function initialiseDatabase() |
73
|
|
|
{ |
74
|
|
|
self::setDatabase(); |
75
|
|
|
|
76
|
|
|
$details = Symphony::Configuration()->get('database'); |
77
|
|
|
|
78
|
|
|
try { |
79
|
|
|
Symphony::Database()->connect( |
80
|
|
|
$details['host'], |
81
|
|
|
$details['user'], |
82
|
|
|
$details['password'], |
83
|
|
|
$details['port'], |
84
|
|
|
$details['db'] |
85
|
|
|
); |
86
|
|
|
} catch (DatabaseException $e) { |
87
|
|
|
self::__abort( |
88
|
|
|
'There was a problem while trying to establish a connection to the MySQL server. Please check your settings.', |
89
|
|
|
time() |
90
|
|
|
); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
// MySQL: Setting prefix & character encoding |
94
|
|
|
Symphony::Database()->setPrefix($details['tbl_prefix']); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
public function run() |
|
|
|
|
98
|
|
|
{ |
99
|
|
|
// Initialize log |
100
|
|
|
if (is_null(Symphony::Log())) { |
101
|
|
|
self::__render(new UpdaterPage('missing-log')); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
// Get available migrations. This will only contain the migrations |
105
|
|
|
// that are applicable to the current install. |
106
|
|
|
$migrations = array(); |
107
|
|
|
|
108
|
|
|
foreach (new DirectoryIterator(INSTALL . '/migrations') as $m) { |
109
|
|
|
if ($m->isDot() || $m->isDir() || General::getExtension($m->getFilename()) !== 'php') { |
110
|
|
|
continue; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
$version = str_replace('.php', '', $m->getFilename()); |
114
|
|
|
|
115
|
|
|
// Include migration so we can see what the version is |
116
|
|
|
include_once($m->getPathname()); |
117
|
|
|
$classname = 'SymphonyCms\\Installer\\Migrations\\migration_' . str_replace('.', '', $version); |
118
|
|
|
|
119
|
|
|
$m = new $classname(); |
120
|
|
|
|
121
|
|
|
if (version_compare( |
122
|
|
|
Symphony::Configuration()->get('version', 'symphony'), |
123
|
|
|
call_user_func(array($m, 'getVersion')), |
124
|
|
|
'<' |
125
|
|
|
)) { |
126
|
|
|
$migrations[call_user_func(array($m, 'getVersion'))] = $m; |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
// The DirectoryIterator may return files in a sporadic order |
131
|
|
|
// on different servers. This will ensure the array is sorted |
132
|
|
|
// correctly using `version_compare` |
133
|
|
|
uksort($migrations, 'version_compare'); |
134
|
|
|
|
135
|
|
|
// If there are no applicable migrations then this is up to date |
136
|
|
|
if (empty($migrations)) { |
137
|
|
|
Symphony::Log()->info('Updater - Already up-to-date'); |
138
|
|
|
|
139
|
|
|
self::__render(new UpdaterPage('uptodate')); |
140
|
|
|
} // Show start page |
141
|
|
|
elseif (!isset($_POST['action']['update'])) { |
142
|
|
|
$notes = array(); |
143
|
|
|
|
144
|
|
|
// Loop over all available migrations showing there |
145
|
|
|
// pre update notes. |
146
|
|
|
foreach ($migrations as $version => $m) { |
147
|
|
|
$n = call_user_func(array($m, 'preUpdateNotes')); |
148
|
|
|
if (!empty($n)) { |
149
|
|
|
$notes[$version] = $n; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
// Show the update ready page, which will display the |
154
|
|
|
// version and release notes of the most recent migration |
155
|
|
|
self::__render(new UpdaterPage('ready', array( |
156
|
|
|
'pre-notes' => $notes, |
157
|
|
|
'version' => call_user_func(array($m, 'getVersion')), |
|
|
|
|
158
|
|
|
'release-notes' => call_user_func(array($m, 'getReleaseNotes')) |
159
|
|
|
))); |
160
|
|
|
} // Upgrade Symphony |
161
|
|
|
else { |
162
|
|
|
$notes = array(); |
163
|
|
|
$canProceed = true; |
164
|
|
|
|
165
|
|
|
// Loop over all the available migrations incrementally applying |
166
|
|
|
// the upgrades. If any upgrade throws an uncaught exception or |
167
|
|
|
// returns false, this will break and the failure page shown |
168
|
|
|
foreach ($migrations as $version => $m) { |
169
|
|
|
$n = call_user_func(array($m, 'postUpdateNotes')); |
170
|
|
|
if (!empty($n)) { |
171
|
|
|
$notes[$version] = $n; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
$canProceed = call_user_func( |
175
|
|
|
array($m, 'run'), |
176
|
|
|
'upgrade', |
177
|
|
|
Symphony::Configuration()->get('version', 'symphony') |
178
|
|
|
); |
179
|
|
|
|
180
|
|
|
Symphony::Log()->info( |
181
|
|
|
sprintf( |
182
|
|
|
'Updater - Migration to %s was %s', |
183
|
|
|
$version, |
184
|
|
|
$canProceed ? 'successful' : 'unsuccessful' |
185
|
|
|
) |
186
|
|
|
); |
187
|
|
|
|
188
|
|
|
if (!$canProceed) { |
189
|
|
|
break; |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
if (!$canProceed) { |
194
|
|
|
self::__render(new UpdaterPage('failure')); |
195
|
|
|
} else { |
196
|
|
|
self::__render(new UpdaterPage('success', array( |
197
|
|
|
'post-notes' => $notes, |
198
|
|
|
'version' => call_user_func(array($m, 'getVersion')), |
199
|
|
|
'release-notes' => call_user_func(array($m, 'getReleaseNotes')) |
200
|
|
|
))); |
201
|
|
|
} |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.