Is there a way to use the WordPress users but without loading the entire WordPress core?

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).

Read More

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!

Related posts

Leave a Reply

8 comments

  1. 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*'

  2. 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' );
    
  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.