diff --git a/README.md b/README.md index bae0618..0f93e2d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,20 @@ Absolutely! As of version 2.0, this plugin will create a new table for WordPress define( 'WP_SESSION_USE_OPTIONS', true ); ``` +**I get an error saying my PHP version is out of date. Why?** + +PHP 5.6 was designated end-of-life and stopped receiving security patches in December 2018. PHP 7.0 was _also_ marked end-of-life in December 2018. The minimum version of PHP supported by WP Session Manager is now PHP 7.1. + +If your server is running an older version of PHP, the session system _will not work!_ To avoid triggering a PHP error, the plugin will instead output this notice to upgrade and disable itself silently. You won't see a PHP error, but you also won't get session support. + +Reach out to your hosting provider or system administrator to upgrade your server. + +**I get an error saying another plugin is setting up a session. What can I do?** + +WP Session Manager overrides PHP's default session implementation with its own custom handler. Unfortunately, we can't swap in a new handler if a session is already active. This plugin hooks into the `plugins_loaded` hook to set things up as early as possible, but if you have code in _another_ plugin (or your theme) that attempts to invoke `session_start()` before WP Session Manager loads, then the custom handler won't work at all. + +Inspect your other plugins and try to find the one that's interfering. Then, reach out to the developer to explain the conflict and see if they have a fix. + Screenshots ----------- @@ -56,6 +70,11 @@ None Changelog --------- +**4.1.0** +- Fix: Add some defense to ensure end users are running the correct version of PHP before loading the system. +- Fix: Eliminate a race condition where another plugin or the theme created the session first. +- Fix: Schedule a cron to auto-delete expired sessions. + **4.0.0** - New: Add an object cache based handler to leverage Redis or Memcached if available for faster queries. - New: Adopt the Contributor Covenant (v1.4) as the project's official code of conduct. @@ -161,7 +180,7 @@ Additional Information **Requires at least:** 4.7 **Tested up to:** 5.0.2 **Requires PHP:** 7.1 -**Stable tag:** 4.0.0 +**Stable tag:** 4.1.0 **License:** GPLv2 or later **License URI:** http://www.gnu.org/licenses/gpl-2.0.html diff --git a/composer.json b/composer.json index 9fb204a..4fb316c 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name" : "ericmann/wp-session-manager", "description" : "Prototype session management for WordPress.", - "version" : "4.0.0", + "version" : "4.1.0", "type" : "wordpress-plugin", "keywords" : ["session"], "homepage" : "https://github.com/ericmann/wp-session-manager", diff --git a/includes/DatabaseHandler.php b/includes/DatabaseHandler.php index febe394..a1c2d53 100644 --- a/includes/DatabaseHandler.php +++ b/includes/DatabaseHandler.php @@ -252,11 +252,21 @@ protected function directDelete(string $key) * @param int $maxlifetime (Unused). * @param callable $next Next clean operation in the stack. * - * @global \wpdb $wpdb - * * @return mixed */ public function clean($maxlifetime, $next) + { + self::directClean(); + + return $next($maxlifetime); + } + + /** + * Update the database by removing any sessions that are no longer valid + * + * @global \wpdb $wpdb + */ + public static function directClean() { global $wpdb; @@ -269,7 +279,5 @@ public function clean($maxlifetime, $next) ) ); } - - return $next($maxlifetime); } } diff --git a/readme.txt b/readme.txt index 106059f..3e907cf 100644 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Tags: session Requires at least: 4.7 Tested up to: 5.0.2 Requires PHP: 7.1 -Stable tag: 4.0.0 +Stable tag: 4.1.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -54,12 +54,31 @@ Absolutely! As of version 2.0, this plugin will create a new table for WordPress define( 'WP_SESSION_USE_OPTIONS', true ); ` += I get an error saying my PHP version is out of date. Why? = + +PHP 5.6 was designated end-of-life and stopped receiving security patches in December 2018. PHP 7.0 was _also_ marked end-of-life in December 2018. The minimum version of PHP supported by WP Session Manager is now PHP 7.1. + +If your server is running an older version of PHP, the session system _will not work!_ To avoid triggering a PHP error, the plugin will instead output this notice to upgrade and disable itself silently. You won't see a PHP error, but you also won't get session support. + +Reach out to your hosting provider or system administrator to upgrade your server. + += I get an error saying another plugin is setting up a session. What can I do? = + +WP Session Manager overrides PHP's default session implementation with its own custom handler. Unfortunately, we can't swap in a new handler if a session is already active. This plugin hooks into the `plugins_loaded` hook to set things up as early as possible, but if you have code in _another_ plugin (or your theme) that attempts to invoke `session_start()` before WP Session Manager loads, then the custom handler won't work at all. + +Inspect your other plugins and try to find the one that's interfering. Then, reach out to the developer to explain the conflict and see if they have a fix. + == Screenshots == None == Changelog == += 4.1.0 = +* Fix: Add some defense to ensure end users are running the correct version of PHP before loading the system. +* Fix: Eliminate a race condition where another plugin or the theme created the session first. +* Fix: Schedule a cron to auto-delete expired sessions. + = 4.0.0 = * New: Add an object cache based handler to leverage Redis or Memcached if available for faster queries. * New: Adopt the Contributor Covenant (v1.4) as the project's official code of conduct. diff --git a/tests/DatabaseHandlerTest.php b/tests/DatabaseHandlerTest.php new file mode 100644 index 0000000..7841484 --- /dev/null +++ b/tests/DatabaseHandlerTest.php @@ -0,0 +1,27 @@ +assertEquals('path', $path); + $this->assertEquals('name', $name); + + $called = true; + }; + + $handler->create('path', 'name', $callback); + + $this->assertTrue($called); + } + +} \ No newline at end of file diff --git a/wp-session-manager.php b/wp-session-manager.php index 2050bf9..d45de1e 100644 --- a/wp-session-manager.php +++ b/wp-session-manager.php @@ -3,7 +3,7 @@ * Plugin Name: WP Session Manager * Plugin URI: https://paypal.me/eam * Description: Session management for WordPress. - * Version: 4.0.0 + * Version: 4.1.0 * Author: Eric Mann * Author URI: https://eamann.com * License: GPLv2+ @@ -11,57 +11,143 @@ * @package WP Session Manager */ -$wp_session_autoload = __DIR__ . '/vendor/autoload.php'; -if (file_exists($wp_session_autoload)) { - require_once $wp_session_autoload; +if (!defined('WP_SESSION_MINIMUM_PHP_VERSION')) { + define('WP_SESSION_MINIMUM_PHP_VERSION', '7.1.0'); } -if (!class_exists('EAMann\Sessionz\Manager')) { - exit('WP Session Manager requires Composer autoloading, which is not configured'); -} +$wp_session_messages = [ + 'bad_php_version' => sprintf( + __( + 'WP Session Manager requires PHP %s or newer. Please contact your system administrator to upgrade!', + 'wp-session-manager' + ), + WP_SESSION_MINIMUM_PHP_VERSION, + PHP_VERSION + ), + 'multiple_sessions' => __( + 'Another plugin is attempting to start a session with WordPress. WP Session Manager will not work!', + 'wp-session-manager' + ) +]; -// Queue up the session stack. -$wp_session_handler = EAMann\Sessionz\Manager::initialize(); +/** + * Initialize the plugin, bootstrap autoloading, and register default hooks + */ +function wp_session_manager_initialize() +{ + $wp_session_autoload = __DIR__ . '/vendor/autoload.php'; + if (file_exists($wp_session_autoload)) { + require_once $wp_session_autoload; + } -// Fall back to database storage where needed. -if (defined('WP_SESSION_USE_OPTIONS') && WP_SESSION_USE_OPTIONS) { - $wp_session_handler->addHandler(new \EAMann\WPSession\OptionsHandler()); -} else { - $wp_session_handler->addHandler(new \EAMann\WPSession\DatabaseHandler()); -} + if (!class_exists('EAMann\Sessionz\Manager')) { + exit('WP Session Manager requires Composer autoloading, which is not configured'); + } -// If we have an external object cache, let's use it! -if (wp_using_ext_object_cache()) { - $wp_session_handler->addHandler(new EAMann\WPSession\CacheHandler()); -} + if (!isset($_SESSION)) { + // Queue up the session stack. + $wp_session_handler = EAMann\Sessionz\Manager::initialize(); + + // Fall back to database storage where needed. + if (defined('WP_SESSION_USE_OPTIONS') && WP_SESSION_USE_OPTIONS) { + $wp_session_handler->addHandler(new \EAMann\WPSession\OptionsHandler()); + } else { + $wp_session_handler->addHandler(new \EAMann\WPSession\DatabaseHandler()); + + /** + * The database handler can automatically clean up sessions as it goes. By default, + * we'll run the cleanup routine every hour to catch any stale sessions that PHP's + * garbage collector happens to miss. This timeout can be filtered to increase or + * decrease the frequency of the manual purge. + * + * @param string $timeout Interval with which to purge stale sessions + */ + $timeout = apply_filters('wp_session_gc_interval', 'hourly'); + + if (!wp_next_scheduled('wp_session_database_gc')) { + wp_schedule_event(time(), $timeout, 'wp_session_database_gc'); + } + + add_action('wp_session_database_gc', ['EAMann\WPSession\DatabaseHandler', 'directClean']); + } + + // If we have an external object cache, let's use it! + if (wp_using_ext_object_cache()) { + $wp_session_handler->addHandler(new EAMann\WPSession\CacheHandler()); + } + + // Decrypt the data surfacing from external storage. + if (defined('WP_SESSION_ENC_KEY') && WP_SESSION_ENC_KEY) { + $wp_session_handler->addHandler(new \EAMann\Sessionz\Handlers\EncryptionHandler(WP_SESSION_ENC_KEY)); + } + + // Use an in-memory cache for the instance if we can. This will only help in rare cases. + $wp_session_handler->addHandler(new \EAMann\Sessionz\Handlers\MemoryHandler()); + + $_SESSION['wp_session_manager'] = 'active'; + } -// Decrypt the data surfacing from external storage. -if (defined('WP_SESSION_ENC_KEY') && WP_SESSION_ENC_KEY) { - $wp_session_handler->addHandler(new \EAMann\Sessionz\Handlers\EncryptionHandler(WP_SESSION_ENC_KEY)); + if (! isset($_SESSION['wp_session_manager']) || $_SESSION['wp_session_manager'] !== 'active') { + add_action('admin_notices', 'wp_session_manager_multiple_sessions_notice'); + return; + } + + // Create the required table. + \EAMann\WPSession\DatabaseHandler::createTable(); + + register_deactivation_hook(__FILE__, function () { + wp_clear_scheduled_hook('wp_session_database_gc'); + }); } -// Use an in-memory cache for the instance if we can. This will only help in rare cases. -$wp_session_handler->addHandler(new \EAMann\Sessionz\Handlers\MemoryHandler()); +/** + * Print an admin notice if too many plugins are manipulating sessions. + * + * @global array $wp_session_messages + */ +function wp_session_manager_multiple_sessions_notice() +{ + global $wp_session_messages; + ?> +