Here is a short post about how I’m dealing with bootstrapping my WordPress plugins. Why use bootstrapping? Typically is used because your plugin requires something – regardless if is PHP or WP… is just better to show „There is missing extension Curl in your PHP installation.“ than meaningless message „Function curl_init is not defined…“ or „This plugin requires WooCommerce plugin installed.“ instead of something else.

I recognize two levels of mistakes that can happen if you are running your plugin on unknown server:

Because of core level I have bootstrap file which check these basic requirements and raise admin notices if something is wrong. This file is always named as the plugin self (e.g. for plugin Debug Log Viewer I have odwp-debug_log folder and odwp-debug_log.php main PHP file).  Here is listing with basic plugin structure:

$cd wp-content/plugins/odwp-debug_log
$ls
...
src/DL_Plugin.php
odwp-debug_log.php
...

Main file odwp-debug_log.php contains all what is necessary to test used environment and print error message about it if needed – the real work starts on line 191 where $odwpdl_errs is defined. As you can see function odwpdl_check_requirements takes array with plugin requirements as an argument and this array can contain definitions of PHP requirements (PHP version, PHP extension) and WordPress requirements (WP version, required WP plugins). This function returns array with errors found in environment check – empty array means no errors so everything is ok:

<?php
/**
* Plugin Name: Debug Log Viewer
* Plugin URI: https://github.com/ondrejd/odwp-debug_log
* Description: Small <a href="https://wordpress.org/" target="blank">WordPress</a> plugin especially for developers that allows better work with <code>debug.log</code> file.
* Version: 1.0.0
* Author: Ondrej Donek
* Author URI: https://ondrejd.com/
* License: GPLv3
* Requires at least: 4.7
* Tested up to: 4.8.1
* Tags: debug,log,development
* Donate link: https://www.paypal.me/ondrejd
*
* Text Domain: odwp-debug_log
* Domain Path: /languages/
*
* @author Ondrej Donek <ondrejd@gmail.com>
* @link https://github.com/ondrejd/odwp-debug_log for the canonical source repository
* @license https://www.gnu.org/licenses/gpl-3.0.en.html GNU General Public License 3.0
* @package odwp-debug_log
* @since 1.0.0
*/
/**
* This file is just a bootstrap. It checks if requirements of plugins are met
* and accordingly either initializes the plugin or halts the process.
*
* Requirements can be specified for PHP and the WordPress self - version
* for both, required extensions for PHP and requireds plugins for WP.
*
* If you are using copy of original file in your plugin you shoud change
* prefix "odwpdl" and name "odwp-debug_log" to your own values.
*
* To set the requirements go down to line 133 and define array that
* is used as a parameter for `odwpdl_check_requirements` function.
*/
if( ! defined( 'ABSPATH' ) ) {
exit;
}
// Some widely used constants
defined( 'DL_SLUG' ) || define( 'DL_SLUG', 'odwpdl' );
defined( 'DL_NAME' ) || define( 'DL_NAME', 'odwp-debug_log' );
defined( 'DL_PATH' ) || define( 'DL_PATH', dirname( __FILE__ ) . '/' );
defined( 'DL_FILE' ) || define( 'DL_FILE', __FILE__ );
defined( 'DL_LOG' ) || define( 'DL_LOG', WP_CONTENT_DIR . '/debug.log' );
if( ! function_exists( 'odwpdl_check_requirements' ) ) :
/**
* Checks requirements of our plugin.
* @param array $requirements
* @return array
* @since 1.0.0
*/
function odwpdl_check_requirements( array $requirements ) {
global $wp_version;
// Initialize locales
load_plugin_textdomain( DL_SLUG, false, dirname( __FILE__ ) . '/languages' );
/**
* @var array Hold requirement errors
*/
$errors = [];
// Check PHP version
if( ! empty( $requirements['php']['version'] ) ) {
if( version_compare( phpversion(), $requirements['php']['version'], '<' ) ) {
$errors[] = sprintf(
__( 'PHP interpreter does not meet version requirements (required at least <b>%s</b>)!', DL_SLUG ),
$requirements['php']['version']
);
}
}
// Check PHP extensions
if( count( $requirements['php']['extensions'] ) > 0 ) {
foreach( $requirements['php']['extensions'] as $req_ext ) {
if( ! extension_loaded( $req_ext ) ) {
$errors[] = sprintf(
__( 'Is required PHP extension <b>%s</b> but it is not installed!', DL_SLUG ),
$req_ext
);
}
}
}
// Check WP version
if( ! empty( $requirements['wp']['version'] ) ) {
if( version_compare( $wp_version, $requirements['wp']['version'], '<' ) ) {
$errors[] = sprintf(
__( 'This plugin requires higher version of <b>WordPress</b> (at least <b>%s</b>)!', DL_SLUG ),
$requirements['wp']['version']
);
}
}
// Check WP plugins
if( count( $requirements['wp']['plugins'] ) > 0 ) {
$active_plugins = (array) get_option( 'active_plugins', [] );
foreach( $requirements['wp']['plugins'] as $req_plugin ) {
if( ! in_array( $req_plugin, $active_plugins ) ) {
$errors[] = sprintf(
__( 'Is required plugin <b>%s</b> but it is not installed!', DL_SLUG ),
$req_plugin
);
}
}
}
return $errors;
}
endif;
if( ! function_exists( 'odwpdl_deactivate_raw' ) ) :
/**
* Deactivate plugin by the raw way.
* @return void
* @since 1.0.0
*/
function odwpdl_deactivate_raw() {
$active_plugins = get_option( 'active_plugins' );
$out = [];
foreach( $active_plugins as $key => $val ) {
if( $val != DL_NAME . '/' . DL_NAME . '.php' ) {
$out[$key] = $val;
}
}
update_option( 'active_plugins', $out );
}
endif;
if( ! function_exists( 'odwpdl_error_log' ) ) :
/**
* @internal Write message to the `wp-content/debug.log` file.
* @param string $message
* @param integer $message_type (Optional.)
* @param string $destination (Optional.)
* @param string $extra_headers (Optional.)
* @return void
* @since 1.0.0
*/
function odwpdl_error_log( string $message, int $message_type = 0, string $destination = null, string $extra_headers = '' ) {
if( ! file_exists( DL_LOG ) || ! is_writable( DL_LOG ) ) {
return;
}
$record = '[' . date( 'd-M-Y H:i:s', time() ) . ' UTC] ' . $message;
file_put_contents( DL_LOG, PHP_EOL . $record, FILE_APPEND );
}
endif;
if( ! function_exists( 'odwpdl_write_log' ) ) :
/**
* Write record to the `wp-content/debug.log` file.
* @param mixed $log
* @return void
* @since 1.0.0
*/
function odwpdl_write_log( $log ) {
if( is_array( $log ) || is_object( $log ) ) {
odwpdl_error_log( print_r( $log, true ) );
} else {
odwpdl_error_log( $log );
}
}
endif;
if( ! function_exists( 'readonly' ) ) :
/**
* Prints HTML readonly attribute. It's an addition to WP original
* functions {@see disabled()} and {@see checked()}.
* @param mixed $value
* @param mixed $current (Optional.) Defaultly TRUE.
* @return string
* @since 1.0.0
*/
function readonly( $current, $value = true ) {
if( $current == $value ) {
echo ' readonly';
}
}
endif;
/**
* Errors from the requirements check
* @var array
*/
$odwpdl_errs = odwpdl_check_requirements( [
'php' => [
// Enter minimum PHP version you needs.
'version' => '5.6',
// Enter extensions that your plugin needs
'extensions' => [
//'gd',
],
],
'wp' => [
// Enter minimum WP version you need
'version' => '4.7',
// Enter WP plugins that your plugin needs
'plugins' => [
//'woocommerce/woocommerce.php',
],
],
] );
// Check if requirements are met or not
if( count( $odwpdl_errs ) > 0 ) {
// Requirements are not met
odwpdl_deactivate_raw();
// In administration print errors
if( is_admin() ) {
$err_head = __( '<b>Debug Log Viewer</b>: ', DL_SLUG );
foreach( $odwpdl_errs as $err ) {
printf( '<div class="error"><p>%s</p></div>', $err_head . $err );
}
}
} else {
// Requirements are met so initialize the plugin.
include( DL_PATH . 'src/DL_Screen_Prototype.php' );
include( DL_PATH . 'src/DL_Plugin.php' );
DL_Plugin::initialize();
}
view raw odwp-debug_log.php hosted with ❤ by GitHub

After this file come main plugin’s PHP – src/DL_Plugin.php – in this case. This file holds all main functionality and (if are required) also environment checks that shows admin notices if needed conditions are not met.

<?php
/**
* @author Ondrej Donek <ondrejd@gmail.com>
* @link https://github.com/ondrejd/odwp-debug_log for the canonical source repository
* @license https://www.gnu.org/licenses/gpl-3.0.en.html GNU General Public License 3.0
* @package odwp-debug_log
* @since 1.0.0
*/
if( ! defined( 'ABSPATH' ) ) {
exit;
}
if( ! class_exists( 'DL_Plugin' ) ) :
/**
* Main class.
* @since 1.0.0
*/
class DL_Plugin {
// ...
/**
* Hook for "admin_init" action.
* @return void
* @since 1.0.0
*/
public static function admin_init() {
// ...
// Check environment
self::check_environment();
// ...
}
// ...
/**
* Checks environment we're running and prints admin messages if needed.
* @return void
* @since 1.0.0
*/
public static function check_environment() {
if( ! file_exists( DL_LOG ) || ! is_writable( DL_LOG ) ) {
add_action( 'admin_notices', function() {
$msg = sprintf(
__( '<strong>Debug Log Viewer</strong>: Soubor (<code>%s</code>) k zapisu ladicich informaci neni vytvoren nebo neni zapisovatelny. Pro vice informací prejdete na <a href="%s">nastaveni tohoto pluginu</a>.', DL_SLUG ),
DL_LOG,
admin_url( 'options-general.php?page=' . DL_SLUG . '-plugin_options' )
);
DL_Plugin::print_admin_notice( $msg );
} );
}
/**
* @var string $err_msg Error message about setting WP_DEBUG and WP_DEBUG_LOG constants.
*/
$err_msg = sprintf(
__( 'Pro umozneni zapisu ladicich informaci do logovaciho souboru (<code>%s</code>) musi být konstanty <code>%s</code> a <code>%s</code> nastaveny na hodnotu <code>TRUE</code>. Pro vice informaci prejdete na <a href="%s">nastaveni tohoto pluginu</a>.', DL_SLUG ),
DL_LOG,
'WP_DEBUG',
'WP_DEBUG_LOG',
admin_url( 'options-general.php?page=' . DL_SLUG . '-plugin_options' )
);
if( ! defined( 'WP_DEBUG' ) || ! defined( 'WP_DEBUG_LOG' ) ) {
add_action( 'admin_notices', function() use ( $err_msg ) {
self::print_admin_notice( $err_msg, 'error' );
} );
}
if( ! defined( 'WP_DEBUG' ) || ! defined( 'WP_DEBUG_LOG' ) ) {
add_action( 'admin_notices', function() use ( $err_msg ) {
self::print_admin_notice( $err_msg, 'error' );
} );
}
}
// ...
/**
* @internal Prints error message in correct WP amin style.
* @param string $msg Error message.
* @param string $type (Optional.) One of ['error','info','success','warning'].
* @param boolean $dismissible (Optional.) Is notice dismissible?
* @return void
* @since 1.0.0
*/
public static function print_admin_notice( $msg, $type = 'info', $dismissible = true ) {
$class = 'notice';
if( in_array( $type, ['error','info','success','warning'] ) ) {
$class .= ' notice-' . $type;
} else {
$class .= ' notice-info';
}
if( $dismissible === true) {
$class .= ' s-dismissible';
}
printf( '<div class="%s"><p>%s</p></div>', $class, $msg );
}
// ...
}
endif;
view raw DL_Plugin.php hosted with ❤ by GitHub

You can see this solution in several my WordPress plugins – for example here:

# #

18.8.2017

Napsat komentář