Skip to content

Commit 1de041a

Browse files
committed
[Blueprints v2] Add support for "enableMultisite" step
1 parent f4af6b0 commit 1de041a

File tree

5 files changed

+310
-19
lines changed

5 files changed

+310
-19
lines changed

components/Blueprints/Runner.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use WordPress\Blueprints\Steps\ActivateThemeStep;
2525
use WordPress\Blueprints\Steps\CpStep;
2626
use WordPress\Blueprints\Steps\DefineConstantsStep;
27+
use WordPress\Blueprints\Steps\EnableMultisiteStep;
2728
use WordPress\Blueprints\Steps\Exception;
2829
use WordPress\Blueprints\Steps\ImportContentStep;
2930
use WordPress\Blueprints\Steps\ImportMediaStep;
@@ -673,6 +674,8 @@ private function createStepObject( string $stepType, array $data ) {
673674
return new CpStep( $data['fromPath'], $data['toPath'] );
674675
case 'defineConstants':
675676
return new DefineConstantsStep( $data['constants'] );
677+
case 'enableMultisite':
678+
return new EnableMultisiteStep();
676679
case 'importContent':
677680
/**
678681
* Flatten the content declaration from
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace WordPress\Blueprints\Steps;
4+
5+
use Symfony\Component\Process\Exception\ProcessFailedException;
6+
use WordPress\Blueprints\Exception\BlueprintExecutionException;
7+
use WordPress\Blueprints\Progress\Tracker;
8+
use WordPress\Blueprints\Runtime;
9+
10+
/**
11+
* Represents the 'enableMultisite' step.
12+
*/
13+
class EnableMultisiteStep implements StepInterface {
14+
public function run( Runtime $runtime, Tracker $tracker ) {
15+
$tracker->setCaption( 'Enabling multisite' );
16+
17+
$code =
18+
<<<'PHP'
19+
<?php
20+
/*
21+
* This code is mirroring the "wp core multisite-convert" command behavior.
22+
* See: https://github.com/wp-cli/core-command/blob/f157fb37dae1d13fe7318452f932917161e83e53/src/Core_Command.php#L505
23+
*/
24+
25+
require_once getenv( 'DOCROOT' ) . '/wp-load.php';
26+
require_once getenv( 'DOCROOT' ) . '/wp-admin/includes/upgrade.php';
27+
28+
// need to register the multisite tables manually for some reason
29+
foreach ( $wpdb->tables( 'ms_global' ) as $table => $prefixed_table ) {
30+
$wpdb->$table = $prefixed_table;
31+
}
32+
33+
install_network();
34+
35+
// Get multisite arguments
36+
$site_id = 1;
37+
$base = '/';
38+
$title = sprintf( '%s Sites', get_option( 'blogname' ) );
39+
$admin_email = get_option( 'admin_email' );
40+
$subdomains = false;
41+
42+
// Get the base domain
43+
$siteurl = get_option( 'siteurl' );
44+
$domain = (string) preg_replace( '|https?://|', '', $siteurl );
45+
$slash = strpos( $domain, '/' );
46+
if ( false !== $slash ) {
47+
$domain = substr( $domain, 0, $slash );
48+
}
49+
50+
// Eagerly check for custom ports
51+
if ( strpos( $domain, ':' ) !== false ) {
52+
throw new Exception(
53+
sprintf(
54+
'The current host is "%s", but WordPress multisites do not support custom ports.',
55+
$domain
56+
)
57+
);
58+
}
59+
60+
$result = populate_network(
61+
$site_id,
62+
$domain,
63+
$admin_email,
64+
$title,
65+
$base,
66+
$subdomains
67+
);
68+
69+
$site_id = $wpdb->get_var( "SELECT id FROM $wpdb->site" );
70+
$site_id = ( null === $site_id ) ? 1 : (int) $site_id;
71+
72+
if ( $result instanceof WP_Error ) {
73+
throw new Exception(
74+
sprintf(
75+
'Error: [%s] %s',
76+
$result->get_error_code(),
77+
$result->get_error_message()
78+
)
79+
);
80+
}
81+
82+
// delete_site_option() cleans the alloptions cache to prevent dupe option
83+
delete_site_option( 'upload_space_check_disabled' );
84+
update_site_option( 'upload_space_check_disabled', 1 );
85+
86+
$wp_config_constants = array(
87+
'WP_ALLOW_MULTISITE' => true,
88+
'MULTISITE' => true,
89+
'SUBDOMAIN_INSTALL' => $subdomains,
90+
'DOMAIN_CURRENT_SITE' => $domain,
91+
'PATH_CURRENT_SITE' => $base,
92+
'SITE_ID_CURRENT_SITE' => $site_id,
93+
'BLOG_ID_CURRENT_SITE' => 1,
94+
);
95+
96+
append_output( json_encode( $wp_config_constants ) );
97+
PHP;
98+
99+
try {
100+
$result = $runtime->evalPhpCodeInSubProcess( $code );
101+
} catch ( ProcessFailedException $e ) {
102+
throw new BlueprintExecutionException( $e->getMessage() );
103+
}
104+
105+
if ( '' === $result->outputFileContent ) {
106+
throw new BlueprintExecutionException( 'Failed to enable multisite' );
107+
}
108+
109+
// Reuse DefineConstantsStep to set the multisite constants.
110+
$wpConfigConstants = json_decode( $result->outputFileContent, true );
111+
$defineConstantsStep = new DefineConstantsStep( $wpConfigConstants );
112+
$defineConstantsStep->run( $runtime, $tracker );
113+
}
114+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
namespace WordPress\Blueprints\Tests\Unit\Steps;
4+
5+
use Exception;
6+
use WordPress\Blueprints\Exception\BlueprintExecutionException;
7+
use WordPress\Blueprints\Progress\Tracker;
8+
use WordPress\Blueprints\Steps\EnableMultisiteStep;
9+
10+
class EnableMultisiteStepTest extends StepTestCase {
11+
public function setUp(): void {
12+
parent::setUp();
13+
14+
// Set site URL.
15+
$this->runtime->evalPhpCodeInSubProcess(
16+
<<<'PHP'
17+
<?php
18+
require_once getenv('DOCROOT') . '/wp-load.php';
19+
update_option( 'siteurl', 'http://localhost' );
20+
update_option( 'home', 'http://localhost' );
21+
PHP
22+
);
23+
}
24+
25+
public function testEnableMultisite() {
26+
$step = new EnableMultisiteStep();
27+
$tracker = new Tracker();
28+
$step->run( $this->runtime, $tracker );
29+
30+
// Verify that multisite is set up and enabled.
31+
$result = $this->runtime->evalPhpCodeInSubProcess(
32+
<<<'PHP'
33+
<?php
34+
35+
// Load WordPress environment
36+
require_once getenv('DOCROOT') . '/wp-load.php';
37+
38+
// Verify multisite setup
39+
append_output(
40+
json_encode( [
41+
'is_multisite' => is_multisite(),
42+
'name' => get_bloginfo( 'name' ),
43+
'wpurl' => get_bloginfo( 'wpurl' ),
44+
'url' => get_bloginfo( 'url' ),
45+
'network' => get_network(),
46+
'constants' => [
47+
'WP_ALLOW_MULTISITE' => defined( 'WP_ALLOW_MULTISITE' ) ? WP_ALLOW_MULTISITE : null,
48+
'MULTISITE' => defined( 'MULTISITE' ) ? MULTISITE : null,
49+
'SUBDOMAIN_INSTALL' => defined( 'SUBDOMAIN_INSTALL' ) ? SUBDOMAIN_INSTALL : null,
50+
'DOMAIN_CURRENT_SITE' => defined( 'DOMAIN_CURRENT_SITE' ) ? DOMAIN_CURRENT_SITE : null,
51+
'PATH_CURRENT_SITE' => defined( 'PATH_CURRENT_SITE' ) ? PATH_CURRENT_SITE : null,
52+
'SITE_ID_CURRENT_SITE' => defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : null,
53+
'BLOG_ID_CURRENT_SITE' => defined( 'BLOG_ID_CURRENT_SITE' ) ? BLOG_ID_CURRENT_SITE : null,
54+
],
55+
] )
56+
);
57+
58+
PHP
59+
);
60+
61+
$output = json_decode( $result->outputFileContent, true );
62+
$this->assertSame( true, $output['is_multisite'] );
63+
$this->assertSame( 'WordPress Site', $output['name'] );
64+
$this->assertSame( 'http://localhost', $output['wpurl'] );
65+
$this->assertSame( 'http://localhost', $output['url'] );
66+
67+
$network = $output['network'];
68+
$this->assertSame( 'localhost', $network['domain'] );
69+
$this->assertSame( 'localhost', $network['cookie_domain'] );
70+
$this->assertSame( '/', $network['path'] );
71+
$this->assertSame( 'WordPress Site Sites', $network['site_name'] );
72+
73+
$constants = $output['constants'];
74+
$this->assertSame( true, $constants['WP_ALLOW_MULTISITE'] );
75+
$this->assertSame( true, $constants['MULTISITE'] );
76+
$this->assertSame( false, $constants['SUBDOMAIN_INSTALL'] );
77+
$this->assertSame( 'localhost', $constants['DOMAIN_CURRENT_SITE'] );
78+
$this->assertSame( '/', $constants['PATH_CURRENT_SITE'] );
79+
$this->assertSame( 1, $constants['SITE_ID_CURRENT_SITE'] );
80+
$this->assertSame( 1, $constants['BLOG_ID_CURRENT_SITE'] );
81+
}
82+
83+
84+
public function testEnableMultisiteRedirectsWhenSiteNotFound() {
85+
$step = new EnableMultisiteStep();
86+
$tracker = new Tracker();
87+
$step->run( $this->runtime, $tracker );
88+
89+
// Verify that multisite is set up and enabled.
90+
$result = $this->runtime->evalPhpCodeInSubProcess(
91+
<<<'PHP'
92+
<?php
93+
94+
register_shutdown_function( function() {
95+
if ( did_action( 'ms_site_not_found' ) ) {
96+
append_output( 'redirected' );
97+
}
98+
});
99+
100+
$_SERVER['HTTP_HOST'] = 'http://unknown';
101+
$_SERVER['REQUEST_URI'] = '/';
102+
103+
// Load WordPress environment
104+
require_once getenv('DOCROOT') . '/wp-load.php';
105+
append_output( 'not_redirected' );
106+
107+
PHP
108+
);
109+
110+
// In the CLI SAPI, the "header()" function is a no-op. We can only test
111+
// that "ms_site_not_found" was called and that the process exited early.
112+
$this->assertSame( 'redirected', $result->outputFileContent );
113+
}
114+
115+
public function testEnableMultisiteFailsOnNon80Port() {
116+
$this->runtime->evalPhpCodeInSubProcess(
117+
<<<'PHP'
118+
<?php
119+
require_once getenv('DOCROOT') . '/wp-load.php';
120+
update_option( 'siteurl', 'http://localhost:8080' );
121+
PHP
122+
);
123+
124+
$this->expectException( BlueprintExecutionException::class );
125+
$this->expectExceptionMessage( 'The current host is "localhost:8080", but WordPress multisites do not support custom ports.' );
126+
$step = new EnableMultisiteStep();
127+
$tracker = new Tracker();
128+
$step->run( $this->runtime, $tracker );
129+
}
130+
131+
public function testEnableMultisiteFailsWhenAlreadyEnabled() {
132+
$step = new EnableMultisiteStep();
133+
$tracker = new Tracker();
134+
$step->run( $this->runtime, $tracker );
135+
136+
$this->expectException( BlueprintExecutionException::class );
137+
$this->expectExceptionMessage( '[siteid_exists] The network already exists.' );
138+
$step->run( $this->runtime, $tracker );
139+
}
140+
141+
public function testEnableMultisiteFailsWhenConfigInvalid() {
142+
$this->runtime->evalPhpCodeInSubProcess(
143+
<<<'PHP'
144+
<?php
145+
require_once getenv('DOCROOT') . '/wp-load.php';
146+
delete_option( 'siteurl' );
147+
PHP
148+
);
149+
150+
$this->expectException( BlueprintExecutionException::class );
151+
$this->expectExceptionMessage( 'Failed to enable multisite' );
152+
$step = new EnableMultisiteStep();
153+
$tracker = new Tracker();
154+
$step->run( $this->runtime, $tracker );
155+
}
156+
}

0 commit comments

Comments
 (0)