MediaWiki master
installPreConfigured.php
Go to the documentation of this file.
1<?php
2
14use Symfony\Component\Yaml\Yaml;
15
16require_once __DIR__ . '/Maintenance.php';
17
24 private $taskContext;
25
26 public function __construct() {
27 parent::__construct();
28 $this->addDescription( 'Create the database and tables for a new wiki, ' .
29 'using a pre-existing LocalSettings.php' );
30 $this->addOption( 'task',
31 'Execute only the specified task', false, true );
32 $this->addOption( 'skip',
33 'Skip the specified task', false, true, false, true );
34 $this->addOption( 'override-config',
35 'Specify a configuration variable with name=value. The value is in YAML format.',
36 false, true, 'c', true );
37 $this->addOption( 'override-option',
38 'Specify an installer option with name=value. The value is in YAML format.',
39 false, true, 'o', true );
40 $this->addOption( 'show-tasks',
41 'Show the list of tasks to be executed, do not actually install' );
42 }
43
45 public function getDbType() {
46 return Maintenance::DB_ADMIN;
47 }
48
49 public function finalSetup( SettingsBuilder $settingsBuilder ) {
50 parent::finalSetup( $settingsBuilder );
51
52 // Apply override-config options. Doing this here instead of in
53 // AddWikiTaskContext::setConfigVar() allows the options to be available
54 // globally for use in LoadExtensionSchemaUpdates.
55 foreach ( $this->getOption( 'override-config' ) ?? [] as $str ) {
56 [ $name, $value ] = $this->parseKeyValue( $str );
57 if ( str_starts_with( $name, 'wg' ) ) {
58 $name = substr( $name, 2 );
59 }
60 $settingsBuilder->putConfigValue( $name, $value );
61 }
62 }
63
65 public function execute() {
66 $context = $this->getTaskContext();
67 $taskFactory = $this->createTaskFactory( $context );
68 $taskList = $this->createTaskList( $taskFactory );
69 $taskRunner = $this->createTaskRunner( $taskList, $taskFactory );
70
71 Installer::disableStorage( $this->getConfig(), 'en' );
72
73 if ( $this->hasOption( 'show-tasks' ) ) {
74 $taskRunner->loadExtensions();
75 echo $taskRunner->dumpTaskList();
76 return false;
77 }
78
79 if ( $this->hasOption( 'task' ) ) {
80 $status = $taskRunner->runNamedTask( $this->getOption( 'task' ) );
81 } else {
82 $status = $taskRunner->execute();
83 }
84
85 if ( $status->isOK() ) {
86 $this->output( "Installation complete.\n" );
87 return true;
88 } else {
89 $this->error( "Installation failed at task \"" .
90 $taskRunner->getCurrentTaskName() . '"' );
91 return false;
92 }
93 }
94
102 private function parseKeyValue( string $str ) {
103 $parts = explode( '=', $str, 2 );
104 if ( count( $parts ) !== 2 ) {
105 $this->fatalError( "Invalid configuration variable \"$str\"" );
106 }
107 return [ $parts[0], Yaml::parse( $parts[1], Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE ) ];
108 }
109
113 protected function getTaskContext() {
114 if ( !$this->taskContext ) {
115 $this->taskContext = $this->createTaskContext();
116 }
117 return $this->taskContext;
118 }
119
126 private function createTaskContext() {
127 $context = new AddWikiTaskContext(
128 $this->getConfig(),
129 $this->getServiceContainer()->getDBLoadBalancerFactory()
130 );
131
132 // Make sure ExtensionTablesTask isn't skipped
133 $context->setOption( 'Extensions',
134 array_keys( ExtensionRegistry::getInstance()->getAllThings() ) );
135
136 foreach ( $this->getOption( 'override-option' ) ?? [] as $str ) {
137 [ $name, $value ] = $this->parseKeyValue( $str );
138 $context->setOption( $name, $value );
139 }
140 foreach ( $this->getSubclassDefaultOptions() as $name => $value ) {
141 $context->setOption( $name, $value );
142 }
143
144 return $context;
145 }
146
153 protected function getSubclassDefaultOptions() {
154 return [];
155 }
156
163 private function createTaskList( TaskFactory $taskFactory ) {
164 $taskList = new TaskList;
165 $taskFactory->registerMainTasks( $taskList, TaskFactory::PROFILE_ADD_WIKI );
166 $reg = ExtensionRegistry::getInstance();
167 $taskList->add( $taskFactory->create(
168 [
169 'class' => CannedProvider::class,
170 'args' => [
171 'extensions',
172 [
173 'HookContainer' => $this->getHookContainer(),
174 'VirtualDomains' => $reg->getAttribute( 'DatabaseVirtualDomains' ),
175 'ExtensionTaskSpecs' => $reg->getAttribute( 'InstallerTasks' ),
176 ]
177 ]
178 ]
179 ) );
180 foreach ( $this->getExtraTaskSpecs() as $spec ) {
181 $taskList->add( $taskFactory->create( $spec ) );
182 }
183 return $taskList;
184 }
185
195 protected function getExtraTaskSpecs() {
196 return [];
197 }
198
206 private function createTaskRunner( TaskList $taskList, TaskFactory $taskFactory ) {
207 $taskRunner = new TaskRunner( $taskList, $taskFactory, TaskFactory::PROFILE_ADD_WIKI );
208 $taskRunner->setSkippedTasks( $this->getOption( 'skip' ) ?? [] );
209
210 $taskRunner->addTaskStartListener( function ( Task $task ) {
211 $name = $task->getName();
212 $desc = $task->getDescriptionMessage()->plain();
213 $this->output( "[$name] $desc... " );
214 } );
215
216 $taskRunner->addTaskEndListener( function ( $task, StatusValue $status ) {
217 if ( $status->isOK() ) {
218 $this->output( "done\n" );
219 } else {
220 $this->output( "\n" );
221 }
222 if ( !$status->isGood() ) {
223 try {
224 $this->error( $status );
225 } catch ( InvalidArgumentException $e ) {
226 $this->error( (string)$status );
227 }
228 }
229 } );
230
231 return $taskRunner;
232 }
233
240 private function createTaskFactory( ITaskContext $context ) {
241 return new TaskFactory(
242 $this->getServiceContainer()->getObjectFactory(),
243 $context
244 );
245 }
246}
247
248$maintClass = InstallPreConfigured::class;
249require_once RUN_MAINTENANCE_IF_MAIN;
getDbType()
Does the script need different DB access? By default, we give Maintenance scripts normal rights to th...
finalSetup(SettingsBuilder $settingsBuilder)
Handle some last-minute setup here.
execute()
Do the actual work.All child classes will need to implement thisbool|null|void True for success,...
__construct()
Default constructor.
getSubclassDefaultOptions()
Get installer options overridden by a subclass.
getExtraTaskSpecs()
Subclasses can override this to provide specification arrays for extra tasks to run during install.
Base installer class.
Definition Installer.php:84
A task context for use in installPreConfigured.php.
A scheduled provider which simply provides data given to it through its constructor.
Factory for installer tasks.
registerMainTasks(TaskList $list, string $profile)
Populate the task list with core installer tasks which are shared by the various installation methods...
create(array $spec)
Create a task object.
A container for tasks, with sorting of tasks by their declared dependencies.
Definition TaskList.php:13
add(Task $task)
Add a task to the list.
Definition TaskList.php:23
Base class for installer tasks.
Definition Task.php:24
getName()
Get the symbolic name of the task.
getDescriptionMessage()
Get the description as a Message object.
Definition Task.php:81
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
output( $out, $channel=null)
Throw some output to the user.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
hasOption( $name)
Checks to see if a particular option was set.
getOption( $name, $default=null)
Get an option, or return the default.
error( $err, $die=0)
Throw an error to the user.
getServiceContainer()
Returns the main service container.
addDescription( $text)
Set the description text.
Load JSON files, and uses a Processor to extract information.
Builder class for constructing a Config object from a set of sources during bootstrap.
putConfigValue(string $key, $value)
Puts a value into a config variable.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
isOK()
Returns whether the operation completed.
isGood()
Returns whether the operation completed and didn't have any error or warnings.
Dependency bundle and execution context for installer tasks.