SHORTINIT константа: WordPress середовище з мінімальним навантаженням
Мене хвилювало питання: як можна використовувати об’єкт $wpdb та Базу Даних сайту з якою я працюю, але щоб мінімально підвантажувати середовище WP. Іноді буває потрібно, при використанні Ajax отримати або записати дані в БД і нічого більше: не потрібні фільтри, не потрібна перевірка на авторизацію користувача, не потрібні функції WоrdPress, не потрібні будь-які перевірки та купа функцій, що підвантажуються. Загалом, не потрібно нічого крім можливості спілкуватися з Базою даних звичними для WordPress методами.
Вирішити таке завдання можна, вважаючи дані підключення до БД із файлу wp-config.php та окремо підключиться до БД. Але це не дуже зручно і вимагатиме зайвого коду, який по суті вже є у файлах WordPress. А то вийдуть чергові милиці.
З версії 3.4 розробники WordPress подбали про це і додали константу SHORTINIT у wp-settings.php:
// Зупиняємо основне завантаження WordPress, якщо нам потрібна тільки база. if ( SHORTINIT ) return false;
Працює вона так:
// вказуємо, що нам потрібний мінімум від WP define('SHORTINIT', true); // підвантажуємо середовище WordPress // WP робить деякі перевірки та підвантажує тільки найнеобхідніше для підключення до БД require_once( $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php' ); // Тут ми можемо спілкуватися з БД. Але ніякі функції WP працювати не будуть. // Глобальні змінні $wp, $wp_query, $wp_the_query не встановлено... Global $wpdb; $result = $wpdb->get_results("SELECT post_title FROM $wpdb->posts WHERE post_type='post'"); if( $result ){ foreach( $result as $post ){ echo "$post->post_title <br>"; } }
Конкретні числа
Щоб подивитися, чим відрізняються ініціалізації з SHORTINIT і без. Я заміряв: кількість SQL запитів, час виконання коду і пам’ять. Ось що вийшло:
require_once( $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php' ); // 5 SQL за 0.1 сек., пам'ять: 14.92 mb define( 'SHORTINIT', true ); require_once( $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php' ); // 0 SQL за 0.02 сек., пам'ять: 2.35 mb
Таким чином, SHORTINIT, грубо, по всіх параметрах знижує навантаження в 5 разів. Не погано!
Що працює при SHORTINIT
Під час використання SHORTINIT система фільтрів: apply_filters() do_action() вже працює. Фільтри прописані основні (файл: /wp-includes/default-filters.php) Ті, що ви вказували у functions.php вашої теми і багато інших працювати не будуть.
Зі звичних функцій: esc_attr() , is_single() , the_content() , get_permalink() і т.д. не працює жодна. Ось всі функції, які підключаються – див. /wp-settings.php :
Включаються файли, необхідні для ініціалізації.
- /wp-includes/class-wp-paused-extensions-storage.php
- /wp-includes/class-wp-fatal-error-handler.php
- /wp-includes/class-wp-recovery-mode-cookie-service.php
- /wp-includes/class-wp-recovery-mode-key-service.php
- /wp-includes/class-wp-recovery-mode-link-service.php
- /wp-includes/class-wp-recovery-mode-email-service.php
- /wp-includes/class-wp-recovery-mode.php
- /wp-includes/error-protection.php
- /wp-includes/default-constants.php
- /wp-includes/plugin.php
Load early WordPress files.
- /wp-includes/compat.php
- /wp-includes/class-wp-list-util.php
- /wp-includes/formatting.php
- /wp-includes/meta.php
- /wp-includes/functions.php
- /wp-includes/class-wp-meta-query.php
- /wp-includes/class-wp-matchesmapregex.php
- /wp-includes/class-wp.php
- /wp-includes/class-wp-error.php
- /wp-includes/default-filters.php
Авторизація при SHORTINIT
Приклад перевірки авторизації та отримання всіх прав користувача з SHORTINIT
У коментарях Олександр поділився кодом, який справно працює, за що йому окреме спасибі. Далі код би доопрацьований Михайлом (обернуть у функцію) і ось що вийшло в результаті:
<?php // specify that we need a minimum from WP define( 'SHORTINIT', true ); // Loading the WordPress environment require_once( $_SERVER['DOCUMENT_ROOT'] . '/wp/wp-load.php' ); // A shortened version of wp_hash from pluggable.php function wp_hash( $data ){ $salt = LOGGED_IN_KEY . LOGGED_IN_SALT; return hash_hmac( 'md5', $data, $salt ); } function hash_token( $token ){ if( function_exists( 'hash' ) ){ return hash('sha256', $token); } else { return sha1($token); } } function curr_user_can( $capability ){ Global $all_caps; return isset( $all_caps[ $capability ] ) && $all_caps[ $capability ]; } function get_curr_user_can(){ Global $wpdb; $_options = $wpdb->get_results( " SELECT `option_name`, `option_value` FROM $wpdb->options WHERE `option_name` IN ('siteurl', '{$wpdb->prefix}user_roles') ", 'OBJECT_K'); if( ! $_options ){ return ['error', 'Options not found']; } $_c_hash = md5( $_options['siteurl']->option_value ); if( ! isset( $_COOKIE[ "wordpress_logged_in_$_c_hash" ] ) ){ return [ 'error', 'No cookies.' ]; } $cookie = $_COOKIE["wordpress_logged_in_$_c_hash"]; $cookie_elements = explode('|', $cookie); if( count( $cookie_elements ) !== 4 ){ return [ 'error', 'Cookie is broken' ]; } $username = $cookie_elements[0]; $expiration = $cookie_elements[1]; $token = $cookie_elements[2]; $hmac = $cookie_elements[3]; if( $expiration < time() ){ return [ 'error', 'Session time has expired' ]; } $user = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->users WHERE `user_login`=%s", $username ), 'OBJECT' ); if( ! $user ){ return [ 'error', 'There is no such user' ]; } $ pass_frag = substr ($ user-> user_pass, 8, 4); $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token ); $algo = function_exists( 'hash' ) ? 'sha256': 'sha1'; $hash = hash_hmac( $algo, $username . '|' . $expiration . '|' . $token, $key ); if( ! hash_equals( $hash, $hmac ) ){ return [ 'error', 'Hash is not equivalent to' ]; } $user_options = $wpdb->get_results( " SELECT `meta_key` , `meta_value` FROM $wpdb->usermeta WHERE `user_id` = $user->ID AND `meta_key` IN ( 'session_tokens', '{$wpdb->prefix}capabilities' ) ", OBJECT_K); if( ! $user_options ){ return [ 'error', 'User options are not set' ]; } $sessions = unserialize( $user_options['session_tokens']->meta_value ); $verifier = hash_token($token); if( ! isset( $sessions[ $verifier ] ) )){ return [ 'error', 'No authorization token' ]; } if( $sessions[ $verifier ]['expiration'] < time() ){ return [ 'error', 'Session time has expired' ]; } $role_caps = unserialize( $_options[ $wpdb->prefix . 'user_roles' ]->option_value ); $user_caps = unserialize( $user_options[ $wpdb->prefix . 'capabilities' ]->meta_value ); $all_caps = []; foreach( $user_caps as $key => $value ){ if( isset( $role_caps[ $key ] ) && $value ){ $all_caps = array_merge( $all_caps, $role_caps[ $key ]['capabilities'], true ); } else { $all_caps[ $key ] = $value; } } return ['success', $all_caps]; }
Перший протестований варіант коду
Код тестувався на WP 4.5. У ранніх чи пізніх версіях можливо потрібно буде змінити перевірку.