WordPress Version testen

Momentan habe ich es mir zur Aufgabe gemacht ein veraltetes Plugin auf eine neue Codebasis zu setzen (Refactoring). Um welches Plugin es sich dabei handelt, werde ich verraten wenn es in einem vorzeigbaren Zustand ist. Derzeit braucht es noch recht viel Arbeit.
Dabei fallen aber immer wieder ein paar Sachen ab die ich für erwähnenswert halte. Mein letzter Artikel war übrigens auch das Ergebnis dieser Arbeit. Diesmal ist es eine Klasse mit der man die minimale WordPress- und PHP-Version testen kann (MySQL könnte man auch testen, ich denke aber das wird kaum jemand machen da es eher selten der Fall ist das man eine bestimmte MySQL-Version voraussetzt). Minimale Version bedeutet hierbei, man testet ob WordPress bzw. PHP mindestens Version X hat.
Bei dem Tempo das Automattic bei der Entwickelung von WordPress an den Tag legt (alleine für dieses Jahr sind 3 Versionen geplant), wird es immer wichtiger zumindest zu  prüfen unter welcher WP-Version ein Plugin oder Theme aktiviert wird. Kann man natürlich auch sein lassen, wenn man den Anwender lieber mit Fehlermeldungen bzw. nicht funktionierenden Funktionen beglücken möchte. Ich persönlich halte es aber für empfehlenswert zu prüfen und ggf. auf eine zu niedrige Version hinzuweisen.

Die Klasse lässt sich vielfältig konfigurieren und flexibel einsetzen. Mit der statischen Methode is_WP() lässt sich z.B. prüfen ob WordPress bereits gestartet wurde. Ist dies nicht der Fall, werden 403-Header (forbidden) gesendet und das Script beendet. Dies ist z.B. nützlich um ein Script gegen direktes Aufrufen zu schützen.
Bsp.:

// beendet das Script falls WordPress nicht zuvor gestartet wurde
WP_Environment_Check::is_WP();

Kern der Klasse sind allerdings die drei Methoden check_wp(), check_php() und run_all_tests() (die Methode check_mysql() ist, wie erwähnt, nur der Vollständigkeit halber dabei und dürfte eher selten Anwendung finden). Konfiguriert man ein Array oder ein Objekt mit den entsprechenden Werten und übergibt das Array bzw. Object der Methode run_all_tests(), so werden die entsprechenden Versionen geprüft und ggf. das Script beendet. Natürlich kann man auch nur einzelne Komponenten testen. Im Gist ist noch eine Datei mit einigen Beispielen, ich denke dadurch wird klar wie man die Klasse verwenden kann.
Im Normalfall ruft man die Klasse beim Aktivieren des Plugins auf, so dass das Plugin erst gar nicht aktiviert wird.

Nun möchte man vielleicht nicht gleich mit dem Hammer zuschlagen und das Script beenden weil es sich um ein Theme und nicht um ein Plugin handelt. Dafür ist die Methode set_die_on_fail() nützlich. Übergibt man ihr den Wert TRUE, so wird das Script bei einen fehlerhaften Test nicht beendet, sondern FALSE als Rückgabewert zurück gegeben. Die Rückgabewerte kann man dann entsprechend auswerten und Meldungen ausgeben.

Falls man mit den Standardmeldungen nicht glücklich ist, kann man diese ebenfalls recht einfach anpassen. Einfach der Klasse einen neuen String übergeben. Das macht z.B. Übersetzungen der Fehlermeldungen recht einfach. Auch hierzu gibt es ein Beispiel im Gist.

Vielleicht findet der eine oder andere die Klasse ganz nützlich oder erweitert sie sogar. In beiden Fällen würde ich mich über Feedback natürlich freuen. Und hier noch der Gist zur Klasse und den Beispielen:

<?php
/**
*
* Class to check the environment (WordPress-, PHP and MySQL-version)
* Test only on minimum or equal version
*
* @author Ralf Albert
* @version 1.0
*
* @var array|object $versions (optional) Array with key=>val or object $version->wp|php|mysql; what to test => minimum version
*
*/
class WP_Environment_Check
{
/**
*
* WP version
* @access public
* @var string minimum or equal version of WordPress
*/
public $wp = '3.2';

/**
*
* PHP version
* @access public
* @var string minimum or equal version of PHP
*/
public $php = '5.2';

/**
*
* MySQL version
* @access public
* @var string minimum or equal version of MySQL
*/
public $mysql = '5.0';

/**
*
* Exit message if WordPress test failed
* @access public
* @var string
*/
public $exit_msg_wp = '';

/**
*
* Exit message if PHP test failed
* @access public
* @var string
*/
public $exit_msg_php = '';

/**
*
* Exit message if MySQL test failed
* @access public
* @var string
*/
public $exit_msg_mysql = '';

/**
*
* If set to true, the class will die with a message if a WP|PHP|MySQL test fail.
* Does not affect is_WP() or if forbidden_headers() is called withot a message
* @access public static
* @var bool true (default)|false
*/
public static $die_on_fail = TRUE;

/**
*
* Constructor
* Run all test that are defined in $version
* @access public
* @param array|object $versions
*/
public function __construct( $versions = NULL ){
self::is_WP();

if( ! empty( $versions ) || ( is_array( $versions ) || is_object( $versions ) ) )
$this->run_all_tests( $versions );
}

/**
*
* Set $die_on_fail
* @param bool $status True exits the script with a message
*/
public function set_die_on_fail( $status = TRUE ){
if( ! is_bool( $status ) )
$status = (bool) $status;

self::$die_on_fail = $status;
}

/**
*
* Check if WordPress is active (if $wp is an object of class wp() )
* @access public static
* @return bool true|die with message and send forbidden-headers if WP is not active
*/
public static function is_WP(){
global $wp;

if( ! $wp instanceof WP )
self::forbidden_header();
else
return TRUE;
}

/**
*
* Run all tests
* @access public
* @param array|object $versions
* @return bool true if all tests passed successfully
*/
public function run_all_tests( $versions = NULL ){
if( empty( $versions ) || ( ! is_array( $versions ) && ! is_object( $versions ) ) )
return FALSE;

$tests = array( 'wp', 'php', 'mysql' );

foreach( $versions as $test => $version ){
// check if the wanted test is available (means: is the test x a method 'check_x')
if( in_array( strtolower( $test ), $tests ) ){
$method = strtolower( $test );
$func = 'check_' . $test; // create the method (check_wp|check_php|check_mysql)
$this->$method = $version; // set $this->wp|php|mysql to version x

if( ! call_user_func( array( &$this, $func ) ) )
die( 'Test ' . __CLASS__ . '::' . $func . ' failed!' ); // this should never happen...
}
}

return TRUE;
}

/**
*
* Check WordPress version
* @access public
* @return bool true returns true if the test passed successfully. Die with a message if not.
*/
public function check_wp(){
if( empty( $this->wp ) )
return FALSE;

if( empty( $this->exit_msg_wp ) )
$this->exit_msg_wp = 'This plugin requires WordPress ' . $this->wp . ' or newer. <a href="http://codex.wordpress.org/Upgrading_WordPress">Please update WordPress</a> or delete the plugin.';

global $wp_version;
if( ! version_compare( $wp_version, $this->wp, '>=' ) ){
return self::forbidden_header( $this->exit_msg_wp );
}

return TRUE;
}

/**
*
* Check PHP version
* @access public
* @return bool true|die with message
*/
public function check_php(){
if( empty( $this->php ) )
return FALSE;

if( empty( $this->exit_msg_php ) )
$this->exit_msg_php = 'This plugin requires at least PHP version <strong>' . $this->php . '</strong>';

if( ! version_compare( PHP_VERSION, $this->php, '>=' ) ){
return self::forbidden_header( $this->exit_msg_php );
}

return TRUE;
}

/**
*
* Check MYSQL version
* @access public
* @return bool true|die with message
*/
public function check_mysql(){
if( empty( $this->mysql ) )
return FALSE;

if( empty( $this->exit_msg_mysql ) )
$this->exit_msg_mysql = 'This plugin requires at least MySQL version <strong>' . $this->mysql . '</strong>';

global $wpdb;
if( ! version_compare( $wpdb->db_version(), $this->mysql, '>=' ) ){
return self::forbidden_header( $this->exit_msg_mysql );
}

return TRUE;
}

/**
*
* Send forbidden-headers (403) if no message is set. Only dies if a message is set
* @access public static
* @param string (optional) $exit_msg
*/
public static function forbidden_header( $exit_msg = '' ){

if( empty( $exit_msg ) ){
header( 'Status: 403 Forbidden' );
header( 'HTTP/1.1 403 Forbidden' );
die( "I'm sorry Dave, I'm afraid I can't do that." );
} else {
if( FALSE === self::$die_on_fail )
return FALSE;
else
die( $exit_msg );
}
}
}

<?php
/*
* Mocking
*/
class WP{}
$wp = new WP;

class WPDB
{
public function db_version(){
return '5.1';
}
}
$wpdb = new WPDB();

$wp_version = '3.3.1';
/* end mocking */

/*
* Creating an instance ov WP_Environment_Check
* Setup the minimum versions
* Setup an exit-message if the WordPress test fail
* Performs every single test one by one
*/

$a = new WP_Environment_Check();
$a->wp = '3.3.1';
$a->php = '5.2';
$a->mysql = '5.1';
$a->exit_msg_wp = 'The plugin <em><a href="http://example.com/my_plugin/">Acme Plugin</a></em> requires WordPress ' . $a->wp . ' or newer. <a href="http://codex.wordpress.org/Upgrading_WordPress">Please update WordPress</a> or delete the plugin.';
$a->check_wp();
$a->check_php();
$a->check_mysql();

/*
* Creating an object with the minimum versions
* Create an instance of WP_Environment_Check
* Performs all tests at once
*/

$v = new stdClass();
$v->wp = '3.3.1';
$v->php = '5.2';
$v->mysql = '5.0';
$a = new WP_Environment_Check();

$a->run_all_tests( $v );


/*
* Setup an array with the minimum versions
* Performs all test by creating an instance of WP_Environment_Check
*/

$v = array( 'wp' => '3.0', 'php' => '5.2', 'MySQL' => '5.1' );
$a = new WP_Environment_Check( $v );

/*
* Store testresult in an object. Disable dying if tests failed
*/

$a = new WP_Environment_Check();
$a->set_die_on_fail( FALSE );
$a->wp = '3.3.1';
$a->php = '5.2';
$a->mysql = '7.0';

$r = new stdClass();

$r->wp = $a->check_wp();
$r->php = $a->check_php();
$r->mysql = $a->check_mysql(); // fail on MYSQL -> 'mysql' => false

var_dump( $r );