12

I have a Wordpress site and a web application that can be used only by the registered (Wordpress) users.

Now I'm loading wp-blog-header.php to check if the user is logged in. Everything is working fine but because on every request (including AJAX) I have to load the Wordpress core also, it slows down my application visibly (more than 70% from the total loading time).

Is there any simple way to use the Wordpress users but without loading the entire Wordpress core?

Update: I need to know which user is logged in and also security is important.

Thank you!

8 Answers 8

10

If I had to do this, I'd use my own cookie to determine login and only load WordPress to check when necessary.

The wordpress_logged_in_{some-hash} cookie can be used to determine the user, and WordPress uses it to determine same. You can't easily reimplement that, but you can use it without loading WordPress on multiple requests.

For example, here's my cookie hash (completely made up data, but realistic):

key: wordpress_logged_in_1234567890abcdef1234567890abcdef
value: admin|1234567890|abcdef1234567890abcdef1234567890

The way WordPress knows how that cookie is valid is irrelevant, all you need to know is whether it's valid one time, then you sign it with a secret.

So, first time, the user isn't proven yet. You load wp-load.php and WP validates the cookie and logs the user in. You now do whatever you do to prove to yourself that the user has been logged in, then you set your own cookie. The key can be anything custom to you, the value you make into a message digest with a secret key using the hash_hmac function.

$key = ... // the key from the WP cookie
$value = ... // the value from the WP cookie
$hash = hash_hmac ( 'md5' , $key.$value , 'some secret key' );

You'll get back gibberish, which you send back to them using setcookie(). On future requests, they'll send this cookie back to you. You can check that first and validate it using the same hash function and secret key.

Only you can generate the hash because only you know the secret key. So if they send back a valid hash that also matches what they send for their WP cookie, then you know they've been validated with WP, through your code, before, and you can get the username right from that value (it's the first part of the cookie, obviously). Then you don't have to load WP.

The secret key, BTW, should be long and random. Not a short password. Not a dictionary word. Just large nonsensical gibberish. Line noise, and lots of it. Example key: 'GHY5hFNqq4Ntdu=3:SUp8#/+_W!- @@^@xslN*L|N+Vn;(1xo8jNyp,au$v9Ki5*'

0
4

Because I'm also using some Wordpress functions beside the users management I decided to continue to load the WP core but I made a custom file that loads only what I need and without loading the plugins. The new loading time is satisfying (it decreased from 1.5s on full WP load to 0.3s)

I've made a file called 'wp-load-minimum.php' and I call this file instead of 'wp-blog-header.php'

This is woking for WP 3.3. Here is the content of the file, If you find it useful:

<?php

//this stops wp-settings from load everything
define ('SHORTINIT',true);

error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR );

/** Define ABSPATH as this files directory */
define( 'ABSPATH', dirname(__FILE__) . '/' );

//WP config file
require ('wp-config.php');

if (SHORTINIT):

// Load the l18n library.
require( ABSPATH . WPINC . '/l10n.php' );

// Run the installer if WordPress is not installed.
wp_not_installed();


// Load most of WordPress.
require( ABSPATH . WPINC . '/class-wp-walker.php' );
//require( ABSPATH . WPINC . '/class-wp-ajax-response.php' );
require( ABSPATH . WPINC . '/formatting.php' );
require( ABSPATH . WPINC . '/capabilities.php' );
require( ABSPATH . WPINC . '/query.php' );
require( ABSPATH . WPINC . '/theme.php' );
require( ABSPATH . WPINC . '/user.php' );
require( ABSPATH . WPINC . '/meta.php' );
require( ABSPATH . WPINC . '/general-template.php' );
require( ABSPATH . WPINC . '/link-template.php' );
//require( ABSPATH . WPINC . '/author-template.php' );
require( ABSPATH . WPINC . '/post.php' );
//require( ABSPATH . WPINC . '/post-template.php' );
//require( ABSPATH . WPINC . '/category.php' );
//require( ABSPATH . WPINC . '/category-template.php' );
require( ABSPATH . WPINC . '/comment.php' );
//require( ABSPATH . WPINC . '/comment-template.php' );
require( ABSPATH . WPINC . '/rewrite.php' );
//require( ABSPATH . WPINC . '/feed.php' );
//require( ABSPATH . WPINC . '/bookmark.php' );
//require( ABSPATH . WPINC . '/bookmark-template.php' );
require( ABSPATH . WPINC . '/kses.php' );
require( ABSPATH . WPINC . '/cron.php' );
//require( ABSPATH . WPINC . '/deprecated.php' );
require( ABSPATH . WPINC . '/script-loader.php' );
require( ABSPATH . WPINC . '/taxonomy.php' );
//require( ABSPATH . WPINC . '/update.php' );
//require( ABSPATH . WPINC . '/canonical.php' );
require( ABSPATH . WPINC . '/shortcodes.php' );
require( ABSPATH . WPINC . '/media.php' );
require( ABSPATH . WPINC . '/http.php' );
require( ABSPATH . WPINC . '/class-http.php' );
require( ABSPATH . WPINC . '/widgets.php' );
require( ABSPATH . WPINC . '/nav-menu.php' );
//require( ABSPATH . WPINC . '/nav-menu-template.php' );
//require( ABSPATH . WPINC . '/admin-bar.php' );

// Load multisite-specific files.
if ( is_multisite() ) {
    require( ABSPATH . WPINC . '/ms-functions.php' );
    require( ABSPATH . WPINC . '/ms-default-filters.php' );
    require( ABSPATH . WPINC . '/ms-deprecated.php' );
}

// Define constants that rely on the API to obtain the default value.
// Define must-use plugin directory constants, which may be overridden in the sunrise.php drop-in.
wp_plugin_directory_constants( );

// Load must-use plugins.
/*foreach ( wp_get_mu_plugins() as $mu_plugin ) {
    include_once( $mu_plugin );
}
unset( $mu_plugin );*/

// Load network activated plugins.
if ( is_multisite() ) {
    foreach( wp_get_active_network_plugins() as $network_plugin ) {
        include_once( $network_plugin );
    }
    unset( $network_plugin );
}

do_action( 'muplugins_loaded' );

if ( is_multisite() )
    ms_cookie_constants(  );

// Define constants after multisite is loaded. Cookie-related constants may be overridden in ms_network_cookies().
wp_cookie_constants( );

// Define and enforce our SSL constants
wp_ssl_constants( );

// Create common globals.
require( ABSPATH . WPINC . '/vars.php' );

// Make taxonomies and posts available to plugins and themes.
// @plugin authors: warning: these get registered again on the init hook.
create_initial_taxonomies();
create_initial_post_types();

// Register the default theme directory root
//register_theme_directory( get_theme_root() );

// Load active plugins.
/*foreach ( wp_get_active_and_valid_plugins() as $plugin )
    include_once( $plugin );
unset( $plugin );*/

// Load pluggable functions.
require( ABSPATH . WPINC . '/pluggable.php' );
//require( ABSPATH . WPINC . '/pluggable-deprecated.php' );

// Set internal encoding.
wp_set_internal_encoding();

// Run wp_cache_postload() if object cache is enabled and the function exists.
if ( WP_CACHE && function_exists( 'wp_cache_postload' ) )
    wp_cache_postload();

do_action( 'plugins_loaded' );

// Define constants which affect functionality if not already defined.
wp_functionality_constants( );

// Add magic quotes and set up $_REQUEST ( $_GET + $_POST )
wp_magic_quotes();

do_action( 'sanitize_comment_cookies' );

/**
 * WordPress Query object
 * @global object $wp_the_query
 * @since 2.0.0
 */
$wp_the_query = new WP_Query();

/**
 * Holds the reference to @see $wp_the_query
 * Use this global for WordPress queries
 * @global object $wp_query
 * @since 1.5.0
 */
$wp_query =& $wp_the_query;

/**
 * Holds the WordPress Rewrite object for creating pretty URLs
 * @global object $wp_rewrite
 * @since 1.5.0
 */
$wp_rewrite = new WP_Rewrite();

/**
 * WordPress Object
 * @global object $wp
 * @since 2.0.0
 */
$wp = new WP();

/**
 * WordPress Widget Factory Object
 * @global object $wp_widget_factory
 * @since 2.8.0
 */
$GLOBALS['wp_widget_factory'] = new WP_Widget_Factory();

do_action( 'setup_theme' );

// Define the template related constants.
wp_templating_constants(  );

// Load the default text localization domain.
load_default_textdomain();

// Find the blog locale.
$locale = get_locale();
$locale_file = WP_LANG_DIR . "/$locale.php";
if ( ( 0 === validate_file( $locale ) ) && is_readable( $locale_file ) )
    require( $locale_file );
unset($locale_file);

// Pull in locale data after loading text domain.
require( ABSPATH . WPINC . '/locale.php' );

/**
 * WordPress Locale object for loading locale domain date and various strings.
 * @global object $wp_locale
 * @since 2.1.0
 */
$GLOBALS['wp_locale'] = new WP_Locale();

// Load the functions for the active theme, for both parent and child theme if applicable.
/*if ( ! defined( 'WP_INSTALLING' ) || 'wp-activate.php' === $pagenow ) {
    if ( TEMPLATEPATH !== STYLESHEETPATH && file_exists( STYLESHEETPATH . '/functions.php' ) )
        include( STYLESHEETPATH . '/functions.php' );
    if ( file_exists( TEMPLATEPATH . '/functions.php' ) )
        include( TEMPLATEPATH . '/functions.php' );
}*/

do_action( 'after_setup_theme' );

// Load any template functions the theme supports.
//require_if_theme_supports( 'post-thumbnails', ABSPATH . WPINC . '/post-thumbnail-template.php' );

// Set up current user.
$wp->init();

/**
 * Most of WP is loaded at this stage, and the user is authenticated. WP continues
 * to load on the init hook that follows (e.g. widgets), and many plugins instantiate
 * themselves on it for all sorts of reasons (e.g. they need a user, a taxonomy, etc.).
 *
 * If you wish to plug an action once WP is loaded, use the wp_loaded hook below.
 */
do_action( 'init' );

// Check site status
if ( is_multisite() ) {
    if ( true !== ( $file = ms_site_check() ) ) {
        require( $file );
        die();
    }
    unset($file);
}

/**
 * This hook is fired once WP, all plugins, and the theme are fully loaded and instantiated.
 *
 * AJAX requests should use wp-admin/admin-ajax.php. admin-ajax.php can handle requests for
 * users not logged in.
 *
 * @link http://codex.wordpress.org/AJAX_in_Plugins
 *
 * @since 3.0.0
 */
do_action('wp_loaded');

endif;

//require( ABSPATH . WPINC . '/pluggable.php' );
1
  • 1
    This is a good idea. One suggestion: You can probably ditch plugin loading and query set up (depending on your use case, of course). Mar 9, 2012 at 14:33
4

For Wordpress 4.9: As I cant comment (new user). The final soultion (single WP install) I use for making is_user_logged_in() and current_user_can() work, is as follow below. We require('wp-load.php') first (to skip wp() in load-blog-header.php), and get ABSPATH constant then, manually includes exactly all the stuff needed.

Using define('SHORTINIT', true) + require('wp-load.php') + manually includes:

Pageload: 1.05 sek - included files: 43 files

Comparing: Using ONLY require('wp-load.php'):

Pageload: 1.35 sek - included files: 419 files

The time difference (0.3 sek) might differ from installs and PHP engines, but while validating many requests on one pageload -things adds up!

Remember to use relative call to WP installed dir. From a Wordpress custom plugin dir, inside one subdir level, normal install, a path should be like:

$wordpress = '../../../../wp-load.php';

Then:

define('SHORTINIT', true);
include_once $wordpress;

require_once ( ABSPATH . WPINC . '/class-wp-user.php' );
require_once ( ABSPATH . WPINC . '/class-wp-roles.php' );
require_once ( ABSPATH . WPINC . '/class-wp-role.php' );
require_once ( ABSPATH . WPINC . '/class-wp-session-tokens.php' );
require_once ( ABSPATH . WPINC . '/class-wp-user-meta-session-tokens.php' );
require_once ( ABSPATH . WPINC . '/formatting.php' );
require_once ( ABSPATH . WPINC . '/capabilities.php' );
//require_once ( ABSPATH . WPINC . '/query.php' ); // - might be useful
require_once ( ABSPATH . WPINC . '/user.php' );
require_once ( ABSPATH . WPINC . '/meta.php' );

wp_cookie_constants();

require_once ( ABSPATH . WPINC . '/vars.php' );
require_once ( ABSPATH . WPINC . '/kses.php' );
require_once ( ABSPATH . WPINC . '/rest-api.php' );
require_once ( ABSPATH . WPINC . '/pluggable.php' );

After this, user validation is accessable. For other task, running on one or two requests, tracking down other needed files might not be not worth 0.3 sek. Skip the SHORTINIT constant and manually clutter.

1
  • +1 to use the first call as relative, Things could be really messy if lending wp core from absolute urls. Oct 25, 2018 at 9:22
2

Wordpress itself only is on or off. Sometimes, but that's only by chance and not by design, you can work around that. But in your case, I'm not really sure if it's possible.

Instead of wp-blog-header.php you can try to only load the WP functions, include wp-load.php instead. Maybe that helps.

6
  • wp-blog-header.php basically loads wp-load.php so there is no diffrence...
    – Victor
    Sep 12, 2011 at 20:11
  • 2
    @Victor: There is a difference. It spares the fire-up of wp(); which is actually pretty expensive.
    – hakre
    Sep 12, 2011 at 20:14
  • OK, now I'm trying to figure out what exactly wp() does.
    – Victor
    Sep 12, 2011 at 20:21
  • I've made some test with wp-load.php instead of wp-blog-header.php, everything seems to work fine but the loading time is the same.
    – Victor
    Sep 12, 2011 at 20:36
  • @Victor: Are you using your watch while pressing F5 or how do you actually measure? :) Anyway, don't use WordPress if you actually need a framework. You can try to only load the functions you actually require instead. But you need to look for them bit by bit. Just include the files you actually need, like for user functions and the database access probably.
    – hakre
    Sep 12, 2011 at 20:50
1

You could try to access the table directly. If you know the salt of the password files you could have them log in via your own system, salt the password yourself (look at how wordpress does it) and keep track of them yourself. If you want the ability to traverse between your own system and wordpress without re-authentication, you could make a plugin to wordpress that passes the current users session to your system.

0

The fastest you can get with WP is making custom wrapper that will define SHORTINIT and then load core. This will make core load stop right after database is connected and before most of APIs and extensions (theme and plugins) are processed.

From there you can try to get by database alone or selectively load parts of core you need.

This is quite a messy approach, but it is as close to lighter core load as things get in WP.

2
  • SHORTINIT is a good approach, but this means that all the functions for checking users and the hashes and such won't get loaded. You could reimplement that, but messy, as you said.
    – Otto
    Sep 12, 2011 at 23:22
  • @Otto Probably not reimplement, but rather load those parts of core by hand. And if there are any modifications to users are being done by plugins load those by hand as well. Yeah, this is quite involved approach. But next alternative for better performance is ditching WP entirely and working with database directly, which is even more messy.
    – Rarst
    Sep 13, 2011 at 8:45
0

Seems there was already discussion about that. So, check updates at: https://core.trac.wordpress.org/ticket/37584

-1

If you just want to allow all Wordpress users to use the web app, you can use the Wordpress user management system and just check whether the user is logged in or not.

To check this you will need to check whether the cookie named wordpress_logged_in_{some-hash} is present. If not, redirect the user to the Wordpress login page. The {some-hash} part of the cookie name is just a series of letters and digits.

2
  • 1
    I need to know which user is logged in and also security is important.
    – Victor
    Sep 12, 2011 at 19:50
  • This is a security-nightmare. Anyone can send a request with a cookie that is structured like this. As you're not checking the hash but just checking if there is anything this equates to a login form where you can enter anything for user and password as long as the fields aren't empty.
    – kraftner
    Sep 15, 2016 at 11:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.