Skip to content

Commit 4218487

Browse files
authored
[Blueprints v2] Add support for "enableMultisite" step (#136)
Resolves #130. Closes #131.
1 parent 07d0dcf commit 4218487

File tree

8 files changed

+541
-75
lines changed

8 files changed

+541
-75
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

components/Blueprints/Steps/DefineConstantsStep.php

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -314,25 +314,72 @@ function rewrite_wp_config_to_define_constants( $content, $constants = array() )
314314
315315
// Add any constants that weren't found in the file
316316
if ( count( $constants ) ) {
317-
$prepend = array(
318-
"<?php \n",
319-
);
320-
foreach ( $constants as $name => $value ) {
321-
$prepend = array_merge(
322-
$prepend,
323-
array(
324-
'define(',
325-
var_export( $name, true ),
326-
',',
327-
var_export( $value, true ),
328-
");\n",
329-
)
330-
);
317+
// First try to find the "That's all, stop editing!" comment.
318+
$anchor = find_first_token_index( $output, T_COMMENT, "That's all, stop editing!" );
319+
320+
// If not found, try the "Absolute path to the WordPress directory." doc comment.
321+
if ( null === $anchor ) {
322+
$anchor = find_first_token_index( $output, T_DOC_COMMENT, "Absolute path to the WordPress directory." );
323+
}
324+
325+
// If not found, try the "Sets up WordPress vars and included files." doc comment.
326+
if ( null === $anchor ) {
327+
$anchor = find_first_token_index( $output, T_DOC_COMMENT, "Sets up WordPress vars and included files." );
328+
}
329+
330+
// If not found, try "require_once ABSPATH . 'wp-settings.php';".
331+
if ( null === $anchor ) {
332+
$require_anchor = find_first_token_index( $output, T_REQUIRE_ONCE );
333+
if ( null !== $require_anchor ) {
334+
$abspath = $output[$require_anchor + 2] ?? null;
335+
$path = $output[$require_anchor + 6] ?? null;
336+
if (
337+
( is_array( $abspath ) && $abspath[1] === 'ABSPATH' )
338+
&& ( is_array( $path ) && $path[1] === "'wp-settings.php'" )
339+
) {
340+
$anchor = $require_anchor;
341+
}
342+
}
343+
}
344+
345+
// If not found, fall back to the PHP opening tag.
346+
if ( null === $anchor ) {
347+
$open_tag_anchor = find_first_token_index( $output, T_OPEN_TAG );
348+
if ( null !== $open_tag_anchor ) {
349+
$anchor = $open_tag_anchor + 1;
350+
}
351+
}
352+
353+
// If we still don't have an anchor, the file is not a valid PHP file.
354+
if ( null === $anchor ) {
355+
error_log( "Blueprint Error: wp-config.php file is not a valid PHP file." );
356+
exit( 1 );
331357
}
332-
$prepend[] = '?>';
333-
$output = array_merge(
334-
$prepend,
335-
$output
358+
359+
// Ensure surrounding newlines when not already present.
360+
$prev = $output[ $anchor - 1 ] ?? null;
361+
$prev = is_array( $prev ) ? $prev[1] : $prev;
362+
$next = $output[ $anchor ] ?? null;
363+
$next = is_array( $next ) ? $next[1] : $next;
364+
365+
$no_prefix = $prev && "\n\n" === substr( $prev, -2 );
366+
$no_suffix = $next && "\n\n" === substr( $next, 0, 2 );
367+
368+
// Add the new constants.
369+
$new_constants = array( "\n" );
370+
foreach ( $constants as $name => $path ) {
371+
$new_constants[] = 'define( ';
372+
$new_constants[] = var_export( $name, true );
373+
$new_constants[] = ', ';
374+
$new_constants[] = var_export( $path, true );
375+
$new_constants[] = " );\n";
376+
}
377+
$new_constants[] = "\n";
378+
379+
$output = array_merge(
380+
array_slice( $output, 0, $anchor ),
381+
$new_constants,
382+
array_slice( $output, $anchor )
336383
);
337384
}
338385
@@ -365,6 +412,23 @@ function skip_whitespace( $tokens ) {
365412
return $output;
366413
}
367414
415+
function find_first_token_index( $tokens, $type, $search = null ) {
416+
foreach ( $tokens as $i => $token ) {
417+
if ( ! is_array( $token ) ) {
418+
continue;
419+
}
420+
421+
if ( $type !== $token[0] ) {
422+
continue;
423+
}
424+
425+
if ( null === $search || false !== strpos( $token[1], $search ) ) {
426+
return $i;
427+
}
428+
}
429+
return null;
430+
}
431+
368432
$wp_config_path = getenv( "DOCROOT" ) . "/wp-config.php";
369433
370434
if ( ! file_exists( $wp_config_path ) ) {
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+
}

0 commit comments

Comments
 (0)