(dev/core#1615) Import civicrm-setup code
[civicrm-core.git] / setup / src / Setup.php
1 <?php
2 namespace Civi;
3
4 use Civi\Setup\Event\CheckAuthorizedEvent;
5 use Civi\Setup\Event\CheckRequirementsEvent;
6 use Civi\Setup\Event\CheckInstalledEvent;
7 use Civi\Setup\UI\Event\UIConstructEvent;
8 use Civi\Setup\Event\InitEvent;
9 use Civi\Setup\Event\InstallDatabaseEvent;
10 use Civi\Setup\Event\InstallFilesEvent;
11 use Civi\Setup\Event\UninstallDatabaseEvent;
12 use Civi\Setup\Event\UninstallFilesEvent;
13 use Civi\Setup\Exception\InitException;
14 use Psr\Log\NullLogger;
15 use Symfony\Component\EventDispatcher\EventDispatcher;
16
17 class Setup {
18
19 const PROTOCOL = '1.0';
20
21 const PRIORITY_START = 2000;
22 const PRIORITY_PREPARE = 1000;
23 const PRIORITY_MAIN = 0;
24 const PRIORITY_LATE = -1000;
25 const PRIORITY_END = -2000;
26
27 private static $instance;
28
29 /**
30 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
31 */
32 protected $dispatcher;
33
34 /**
35 * @var \Civi\Setup\Model
36 */
37 protected $model;
38
39 /**
40 * @var \Psr\Log\LoggerInterface
41 */
42 protected $log;
43
44 // ----- Static initialization -----
45
46 /**
47 * The initialization process loads any `*.civi-setup.php` files and
48 * fires the `civi.setup.init` event.
49 *
50 * @param array $modelValues
51 * List of default configuration options.
52 * Recommended fields: 'srcPath', 'cms'
53 * @param callable $pluginCallback
54 * Function which manipulates the list of plugin files.
55 * Use this to add, remove, or re-order callbacks.
56 * function(array $files) => array
57 * Ex: ['hello' => '/var/www/plugins/hello.civi-setup.php']
58 * @param \Psr\Log\LoggerInterface $log
59 */
60 public static function init($modelValues = array(), $pluginCallback = NULL, $log = NULL) {
61 if (!defined('CIVI_SETUP')) {
62 define('CIVI_SETUP', 1);
63 }
64
65 self::$instance = new Setup();
66 self::$instance->model = new \Civi\Setup\Model();
67 self::$instance->model->setValues($modelValues);
68 self::$instance->dispatcher = new EventDispatcher();
69 self::$instance->log = $log ? $log : new NullLogger();
70
71 $pluginDir = dirname(__DIR__) . '/plugins';
72 $pluginFiles = array();
73 foreach (['*.civi-setup.php', '*/*.civi-setup.php'] as $pattern) {
74 foreach ((array) glob("$pluginDir/$pattern") as $file) {
75 $key = substr($file, strlen($pluginDir) + 1);
76 $key = preg_replace('/\.civi-setup\.php$/', '', $key);
77 $pluginFiles[$key] = $file;
78 }
79 }
80 ksort($pluginFiles);
81
82 if ($pluginCallback) {
83 $pluginFiles = $pluginCallback($pluginFiles);
84 }
85
86 foreach ($pluginFiles as $pluginFile) {
87 self::$instance->log->debug('[Setup.php] Load plugin {file}', array(
88 'file' => $pluginFile,
89 ));
90 require $pluginFile;
91 }
92
93 $event = new InitEvent(self::$instance->getModel());
94 self::$instance->getDispatcher()->dispatch('civi.setup.init', $event);
95 // return $event; ...or... return self::$instance;
96 }
97
98 /**
99 * Assert that this copy of civicrm-setup is compatible with the client.
100 *
101 * @param string $expectedVersion
102 * @throws \Exception
103 */
104 public static function assertProtocolCompatibility($expectedVersion) {
105 if (version_compare(self::PROTOCOL, $expectedVersion, '<')) {
106 throw new InitException(sprintf("civicrm-setup is running protocol v%s. This application expects civicrm-setup to support protocol v%s.", self::PROTOCOL, $expectedVersion));
107 }
108 list ($actualFirst) = explode('.', self::PROTOCOL);
109 list ($expectedFirst) = explode('.', $expectedVersion);
110 if ($actualFirst > $expectedFirst) {
111 throw new InitException(sprintf("civicrm-setup is running protocol v%s. This application expects civicrm-setup to support protocol v%s.", self::PROTOCOL, $expectedVersion));
112 }
113 }
114
115 /**
116 * Assert that the "Setup" subsystem is running.
117 *
118 * This function is mostly just a placeholder -- in practice, if
119 * someone makes a failed call to `assertRunning()`, it will probably
120 * manifest as an unknown class/function. But this gives us a pretty,
121 * one-line, syntactically-valid way to make the assertion.
122 */
123 public static function assertRunning() {
124 if (!defined('CIVI_SETUP')) {
125 exit("Installation plugins must only be loaded by the installer.\n");
126 }
127 }
128
129 /**
130 * @return Setup
131 */
132 public static function instance() {
133 if (self::$instance === NULL) {
134 throw new InitException('\Civi\Setup has not been initialized.');
135 }
136 return self::$instance;
137 }
138
139 /**
140 * @return \Psr\Log\LoggerInterface
141 */
142 public static function log() {
143 return self::instance()->getLog();
144 }
145
146 /**
147 * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
148 */
149 public static function dispatcher() {
150 return self::instance()->getDispatcher();
151 }
152
153 // ----- Logic ----
154
155 /**
156 * Determine whether the current CMS user is authorized to perform
157 * installation.
158 *
159 * @return \Civi\Setup\Event\CheckAuthorizedEvent
160 */
161 public function checkAuthorized() {
162 $event = new CheckAuthorizedEvent($this->getModel());
163 return $this->getDispatcher()->dispatch('civi.setup.checkAuthorized', $event);
164 }
165
166 /**
167 * Determine whether the local environment meets system requirements.
168 *
169 * @return \Civi\Setup\Event\CheckRequirementsEvent
170 */
171 public function checkRequirements() {
172 $event = new CheckRequirementsEvent($this->getModel());
173 return $this->getDispatcher()->dispatch('civi.setup.checkRequirements', $event);
174 }
175
176 /**
177 * Determine whether the setting and/or schema are already installed.
178 *
179 * @return \Civi\Setup\Event\CheckInstalledEvent
180 */
181 public function checkInstalled() {
182 $event = new CheckInstalledEvent($this->getModel());
183 return $this->getDispatcher()->dispatch('civi.setup.checkInstalled', $event);
184 }
185
186 /**
187 * Create the settings file.
188 *
189 * @return \Civi\Setup\Event\InstallFilesEvent
190 */
191 public function installFiles() {
192 $event = new InstallFilesEvent($this->getModel());
193 return $this->getDispatcher()->dispatch('civi.setup.installFiles', $event);
194 }
195
196 /**
197 * Create the database schema.
198 *
199 * @return \Civi\Setup\Event\InstallDatabaseEvent
200 */
201 public function installDatabase() {
202 $event = new InstallDatabaseEvent($this->getModel());
203 return $this->getDispatcher()->dispatch('civi.setup.installDatabase', $event);
204 }
205
206 /**
207 * Remove the settings file.
208 *
209 * @return \Civi\Setup\Event\UninstallFilesEvent
210 */
211 public function uninstallFiles() {
212 $event = new UninstallFilesEvent($this->getModel());
213 return $this->getDispatcher()->dispatch('civi.setup.uninstallFiles', $event);
214 }
215
216 /**
217 * Remove the database schema.
218 *
219 * @return \Civi\Setup\Event\UninstallDatabaseEvent
220 */
221 public function uninstallDatabase() {
222 $event = new UninstallDatabaseEvent($this->getModel());
223 return $this->getDispatcher()->dispatch('civi.setup.uninstallDatabase', $event);
224 }
225
226 /**
227 * Create a page-controller for a web-based installation form.
228 *
229 * @return \Civi\Setup\UI\Event\UIConstructEvent
230 */
231 public function createController() {
232 $event = new UIConstructEvent($this->getModel());
233 return $this->getDispatcher()->dispatch('civi.setupui.construct', $event);
234 }
235
236 // ----- Accessors -----
237
238 /**
239 * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
240 */
241 public function getDispatcher() {
242 return $this->dispatcher;
243 }
244
245 /**
246 * @return \Civi\Setup\Model
247 */
248 public function getModel() {
249 return $this->model;
250 }
251
252 /**
253 * @return \Psr\Log\LoggerInterface
254 */
255 public function getLog() {
256 return $this->log;
257 }
258
259 }