Opened 6 years ago
Last modified 4 years ago
#45331 new defect (bug)
'rest_url_prefix' filter fails to impact flush_rewrite_rules() on plugin activation
Reported by: | KestutisIT | Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | major | Version: | 4.9.8 |
Component: | Permalinks | Keywords: | needs-patch ux-feedback reporter-feedback |
Focuses: | administration, rest-api | Cc: |
Description
'rest_url_prefix' filter fails to impact flush_rewrite_rules() on plugin activation
=========================================================================
So here is the bug - 'flush_rewrite_rules' is called on plugin activation after 'rest_url_prefix' filter added with new API ENDPOINT,
but if I go to '<MY-DOMAIN>/<NEW-ENDPOINT>/' it gives me 404 error. And if I access '<MY-DOMAIN>/wp-json/' on Firefox Developer Edition in 'Header' section I see:
Link <http://<MY-DOMAIN>/<NEW-ENDPOINT>/>; rel="https://api.w.org/"
So the header is correct here. And it will fix the issue if I go to WP Settings -> Permalinks -> Save.
But that is a bug. As you won't have to instruct that to your plugin user, after his activation he will see that API is not working.
Install Controller class - 'flush_rewrite_rules' is called on plugin activation after 'rest_url_prefix' filter added with new API ENDPOINT:
<?php namespace GreatestEverManager\Controllers\Admin; final class InstallController { <...> public function setCustomWP_RestAPI_Prefix() { // NOTE: Do not forget to do the same on install with flush_rewrite_rules(); after it. add_filter('rest_url_prefix', function() { return ConfigurationInterface::WP_REST_API_PREFIX; }, 10, 1); // NOTE: As there is no custom post types or custom taxonomies registration later, we perform rewrite rules flush right now flush_rewrite_rules(); } <...> }
Main Controller class:
<?php namespace GreatestEverManager\Controllers; use SoftwareLicenseManager\Models\Configuration\ConfigurationInterface; <...> final class MainController { <...> public function __construct(ConfigurationInterface $paramConfWithoutRouting) { <...> if(!is_null($this->confWithoutRouting)) { register_activation_hook($this->confWithoutRouting->getPluginPathWithFilename(), array(&$this, 'networkOrSingleActivate')); register_deactivation_hook($this->confWithoutRouting->getPluginPathWithFilename(), array(&$this, 'networkDeactivate')); <...> } } /** * Activate (enable+install or enable only) plugin for across the whole network * @note - 'get_sites' function requires WordPress 4.6 or newer! */ public function networkOrSingleActivate() { if(is_multisite()) { // A workaround until WP will get fixed // SHOULD be 'networkActivate' but WordPress does not yet support that feature, // so this means as long as the 'MULTISITE' constant is defined in wp-config, we use that method $this->multisiteActivate(); } else { // A workaround until WP will get fixed $this->activate(); } } public function activate() { try { <...> // Install plugin for single site $objInstaller = new \GreatestEverManager\Controllers\Admin\InstallController($conf, $lang, $conf->getBlogId()); // Install <...> $objInstaller->setCustomWP_RestAPI_Prefix(); <...> } catch (\Exception $e) { if(StaticValidator::inWPDebug()) { // In WP activation we can kill the install only via 'trigger_error' with 'E_USER_ERROR' param $error = sprintf(static::LANG_ERROR_IN_METHOD_TEXT, __FUNCTION__, $e->getMessage()); trigger_error($error, E_USER_ERROR); } } } public function run() { if($this->canProcess) { <...> add_filter('rest_url_prefix', function() { return ConfigurationInterface::WP_REST_API_PREFIX; }, 10, 1); add_action('rest_api_init', array(&$this, 'frontEndAPI_Callback'), 0); <...> } } <...> }
Plugin main file (wp-content/plugins/GreatestEverManager/GreatestEverManager.php):
<?php /** * Plugin Name: Greatest Ever Manager * <...> */ namespace GreatestEverManager; require_once 'Models/Configuration/ConfigurationInterface.php'; require_once 'Models/Configuration/Configuration.php'; require_once 'Controllers/MainController.php'; <...> use GreatestEverManager\Models\Configuration\Configuration; use GreatestEverManager\Controllers\MainController; if(!class_exists('GreatestEverManager\GreatestEverManager')) { final class GreatestEverManager { // Configuration const REQUIRED_PHP_VERSION = '5.4.0'; const REQUIRED_WP_VERSION = 4.6; const OLDEST_COMPATIBLE_PLUGIN_VERSION = 6.0; const PLUGIN_VERSION = 6.0; // Settings private static $params = array( 'plugin_id' => 0, 'plugin_prefix' => 'greatest_ever_manager_', 'plugin_api_namespace' => 'gem/v1', 'plugin_handle_prefix' => 'greatest-ever-manager-', <...> ); private static $objConfiguration = NULL; private static $objMainController = NULL; <...> /** * @return Configuration */ public static function getConfiguration() { if(is_null(static::$objConfiguration) || !(static::$objConfiguration instanceof Configuration)) { // Create an instance of plugin configuration model static::$objConfiguration = new Configuration( $GLOBALS['wpdb'], get_current_blog_id(), static::REQUIRED_PHP_VERSION, phpversion(), static::REQUIRED_WP_VERSION, $GLOBALS['wp_version'], static::OLDEST_COMPATIBLE_PLUGIN_VERSION, static::PLUGIN_VERSION, __FILE__, static::$params ); } return static::$objConfiguration; } /** * Creates new or returns existing instance of plugin main controller * @return MainController */ public static function getMainController() { if(is_null(static::$objMainController) || !(static::$objMainController instanceof MainController)) { // NOTE: This is not passing by reference! static::$objMainController = new MainController(static::getConfiguration()); } return static::$objMainController; } <...> } <...> // Run the plugin GreatestEverManager::getMainController()->run(); }
The coding pattern is S.O.L.I.D. MVC, Version 6, based on PSR-4 Autoloaders and PSR-2 Coding Standards.
To deeply inspect load process (without the REST_API part), you can inspect 'Expadandable FAQ' - SolidMVC boiler-plate plugin:
https://wordpress.org/plugins/expandable-faq/
AFAICT this happens because the rewrite rules have already been registered when a plugin is activated for the first time. This type of timing issue is common in WordPress. I would recommend running
flush_rewrite_rules
the next page load after your plugin is installed.If that is incorrect, please provide a minimal, standalone plugin that demonstrates the issue.