Skip to content

Commit

Permalink
[Blueprints] Prevent WSOD when autologin is enabled and a plugin logs…
Browse files Browse the repository at this point in the history
… a notice (#2079)

This PR ensures the autologin step won't cause a perpetual white screen
of death when one of the plugins displays a notice.

The autologin flow consists of a bunch of checks, setcookie() call, a
header('Location') call, and an `exit;`. However, when the headers are
already sent, the cookies won't be set, the redirect won't work, but the
script will still. This, effectively, prevents loading any WordPress
page.

This PR adds a `headers_sent()` check to stop the autologin step early
in those scenarios when we can't finish the login.

  ## Testing instructions

* CI has a new E2E test – confirm everything is green
* Try this Blueprint (courtesy of @janw-me). It should open a white
screen with errors. Now change the landing page to "/". It should open
the homepage with a few errors at the top. Before this PR, you'd see a
blank page instead of the homepage.

```json
{
  "plugins": [
    "pronamic-ideal"
  ],
  "preferredVersions": {
    "php": "8.2",
    "wp": "6.7"
  },
  "features": {
    "networking": true
  },
  "login": true,
  "landingPage": "/wp-admin/update-core.php",
  "steps": [
    {
      "step": "defineWpConfigConsts",
      "consts": {
        "WP_DEBUG": true,
        "WP_DEBUG_DISPLAY": true
      }
    },
    {
      "step": "setSiteLanguage",
      "language": "nl_NL"
    }
  ]
}

```
  • Loading branch information
adamziel authored Dec 17, 2024
1 parent b9f5edb commit 1bed672
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
42 changes: 42 additions & 0 deletions packages/playground/website/playwright/e2e/blueprints.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,3 +449,45 @@ test('should correctly redirect to a multisite wp-admin url', async ({
await expect(wordpress.locator('body')).toContainText('Escritorio');
});
});

test('WordPress homepage loads when mu-plugin prints a notice', async ({
wordpress,
website,
page,
}) => {
// Load a blueprint that enables debug mode and adds a mu-plugin that prints a notice
const blueprint = {
landingPage: '/',
preferredVersions: {
wp: '6.7',
php: '8.0',
},
steps: [
{
step: 'defineWpConfigConsts',
consts: {
WP_DEBUG: true,
WP_DEBUG_DISPLAY: true,
},
},
{
step: 'writeFile',
path: '/wordpress/wp-content/mu-plugins/000-print-notice.php',
data: `<?php
echo 'This is a notice printed by an mu-plugin.';
`,
},
],
};

const encodedBlueprint = JSON.stringify(blueprint);
await website.goto(`./#${encodedBlueprint}`);

// Wait for the page to load and verify it contains both WordPress content and the notice
await expect(wordpress.locator('body')).toContainText(
'Welcome to WordPress. This is your first post.'
);

// Verify there's no admin bar
await expect(wordpress.locator('body')).not.toContainText('Dashboard');
});
30 changes: 30 additions & 0 deletions packages/playground/wordpress/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,45 @@ export async function setupPlatformLevelMuPlugins(php: UniversalPHP) {
if (!$user) {
return;
}
/**
* We're about to set cookies and redirect. It will log the user in
* if the headers haven't been sent yet.
*
* However, if they have been sent already – e.g. there a PHP
* notice was printed, we'll exit the script with a bunch of errors
* on the screen and without the user being logged in. This
* will happen on every page load and will effectively make Playground
* unusable.
*
* Therefore, we just won't auto-login if headers have been sent. Maybe
* we'll be able to finish the operation in one of the future requests
* or maybe not, but at least we won't end up with a permanent white screen.
*/
if (headers_sent()) {
_doing_it_wrong('playground_auto_login', 'Headers already sent, the Playground runtime will not auto-login the user', '1.0.0');
return;
}
/**
* This approach is described in a comment on
* https://developer.wordpress.org/reference/functions/wp_set_current_user/
*/
wp_set_current_user( $user->ID, $user->user_login );
wp_set_auth_cookie( $user->ID );
do_action( 'wp_login', $user->user_login, $user );
setcookie('playground_auto_login_already_happened', '1');
/**
* Confirm that nothing in WordPress, plugins, or filters have finalized
* the headers sending phase. See the comment above for more context.
*/
if (headers_sent()) {
_doing_it_wrong('playground_auto_login', 'Headers already sent, the Playground runtime will not auto-login the user', '1.0.0');
return;
}
/**
* Reload page to ensure the user is logged in correctly.
* WordPress uses cookies to determine if the user is logged in,
Expand Down

0 comments on commit 1bed672

Please sign in to comment.