In a custom plugin, I am creating a custom menu page in WP with an admin_menu hook in the main Asset loader class. In a separate file, I have another class called Leads_UI that houses the public static function leads_html() to output the markup to show on this custom admin page.
If you see in the code below, in the register_leads_dashboard function, if I am calling the
leads_html function via the same class rather than the Leads_UI class, it works as intended and outputs the html only when you are on that Leads page.
But if I try to use the public static function leads_html() from the separate Leads_UI class, Leads_UI::leads_html(), it is causing 2 errors:
The html is outputted on every page in the dashboard and shows at the top left corner behind the main content,
When I click on the 'Leads' page in dashboard, it throws the 404 page not found error.
What might be the issue? I am new to OOP.
namespace MWS_PLUGIN\Inc;
use MWS_PLUGIN\Inc\Traits\Singleton;
class Assets {
use Singleton;
protected function __construct() {
// Load all critical functions
$this->setup_hooks();
}
protected function setup_hooks() {
//Add a new screen for 'Leads' CPT
add_action( 'admin_menu', [$this, 'register_leads_dashboard'] );
}
public function register_leads_dashboard() {
// This causes unexpected behaviour
add_menu_page( __( 'Leads', 'my-plugin' ), __( 'Leads', 'my-plugin' ), 'manage_options', 'leads-list', Leads_UI::leads_html(), 'dashicons-admin-users', 50 );
// This works perfectly!
//add_menu_page( __( 'Leads', 'my-plugin' ), __( 'Leads', 'my-plugin' ), 'manage_options', 'leads-list', [ $this, 'leads_html' ], 'dashicons-admin-users', 50 );
}
public function leads_html() {
esc_html_e( 'Leads page test', 'my-plugin' );
}
}
Lead_UI class that's in a separate file:
namespace MWS_PLUGIN\Inc;
class Leads_UI {
private function __construct(){
}
public static function leads_html() {
esc_html_e( 'Leads page test', 'my-plugin' );
}
}
The add_menu_page() function's fifth parameter has the callable type.
But, your code doesn't pass the callable. Rather, it calls your void Leads_UI::leads_html() function, then passes the missing return value of that function to add_menu_page(). So the function is getting invoked at the time you call add_menu_page() which is not what you want.
add_menu_page( __( 'Leads', 'my-plugin' ), __( 'Leads', 'my-plugin' ),
'manage_options', 'leads-list',
Leads_UI::leads_html(), /* NOT a callable! */
'dashicons-admin-users', 50 );
You want to specify that static function as a callable like this:
[ Leads_UI::class, 'leads_html' ]
giving code like this.
add_menu_page( __( 'Leads', 'my-plugin' ), __( 'Leads', 'my-plugin' ),
'manage_options', 'leads-list',
[ Leads_UI::class, 'leads_html' ], /* Callable! */
'dashicons-admin-users', 50 );
Edit: More explanation:
Here's the sequence of operations.
Somewhere in your code you say something like new Assets().
That calls the __construct member function you showed us.
In turn that calls your setup_hooks() member function.
That registers an admin_menu action that will call your register_leads_dashboard() member function later on.
When you return from __construct(), WordPress does a whole lot of other stuff. If it's rendering any dashboard page, eventually it invokes your admin_menu action. If it's rendering a front-end page it never invokes it.
Everything that appears in the big dashboard menu on the left side of the dashboard, everything, is the result of some admin_menu action handler. There are many admin_menu handlers in WordPress. Yours is one of them.
WordPress calls your admin_menu handler which invokes your register_leads_dashboard() method.
You call add_menu_page() to put your Leads page on the menu.
That in turn registers another callback (the callable we've been talking about here) to be called when your user clicks on your menu item. In your case you want it to call that static member function Leads_UI::leads_html().
When the user clicks on your menu item, WordPress calls that static member function.
WordPress lives, and sometimes dies (:-) by all these callbacks. And it happens that php has peculiar ways of declaring callables, the functions to be called when invoking callbacks.
For a global function, a callable is simply a text string containing its name.
For a non-static member function, the callable is
array( $instance, 'function_name' )
For a static member function like yours, the callable is
array( ClassName::class, 'function_name' )
Why not forget about all those class definitions and just make all callback functions global? Doing that would make it easier to add WordPress action and filter handlers. These reasons.
Global name space pollution. If you and I both define the same global function namespace\name combination, the php program (WordPress) will crash. Using a static member function (what you did) is a way to take those functions out of the global namespace.
Context. While an ordinary member function is running, it has access via $this to all the class instance's properties and functions. So one callback can save information in properties, and another callback can use that information.
Don't overthink this OOP stuff.
Related
In many instances, I see a function followed by the add_action command.
But in terms of the visual flow, I always move the add_action underneath the function, and it still works.
I would like to know firstly, if it matters which order they appear in, and why most instructions seems to include the function last?
add_action( $hook, 'foo', $priority, $accepted_args );
function foo() {
// Function code
}
or
function foo() {
// Function code
}
add_action( $hook, 'foo', $priority, $accepted_args );
Wouldn't it make more sense to use the latter as the function has already been read?
The order doesn't matter. The PHP interpreter uses a 2-pass approach. The functions are defined in the first pass, and your function is only run later when it's called by do_action( $hook ).
A little more about this here: Does PHP interpret code in the same way that JavaScript does?
So, I finally found a way to affect the desired function.
The function I was trying to modify is contained in a class so, I did something like this:
class my_Check extends Appointments {
function __construct() {
$this->unregister_parent_hook();
add_action( 'wp_ajax_post_confirmation', array( $this, 'post_confirmation' ) );
add_action( 'wp_ajax_nopriv_post_confirmation', array( $this, 'post_confirmation' ) );
}
function unregister_parent_hook() {
global $appointments; //this was the object created with the parent class
remove_action( 'wp_ajax_post_confirmation', array( $appointments, 'post_confirmation' ) );
remove_action( 'wp_ajax_nopriv_post_confirmation', array( $appointments, 'post_confirmation' ) );
}
function post_confirmation() {
...do the stuff with my mods...
}
}
$new_Check = new my_Check();
Only, I have a new problem now. The parent class does a lot more in the __construct() (many add_action()'s, etc. And $this is populated with a lot of data). The problem is, these other things and data do not seem to be carrying over into the child class. I tried adding a parent::__construct() in the child's __construct() function, but that did not seem to work.
The code with my mods works except for things that need more data in the $this carried over from the parent class.
How can I maintain all the parent class's variable, functions, hooks & filters, etc. into the child class?
And, I can't really change the file with the parent class because that is in a plugin core file which I don't want to modify directly.
I had seen many examples where people were using parent::__construct() to pass the parent's variables, etc. and they were placing it in the child's class __construct() function. That wasn't working for me.
However, I was finally able to get it to work by calling the parent constructor within the new function I was adding to the parent class. Like this:
function post_confirmation() {
parent::__construct();
...do the stuff with my mods...
}
Got the solution from this post > https://stackoverflow.com/a/32232406/1848815
Can someone help me explain what is going on here? I am going through a Wordpress plugin. It has the following constructor function
protected function __construct() {
do_action_ref_array( 'plugin_specific_action_name', array( $this ) );
add_action( 'init', array( $this, 'init' ) );
}
My question is on the first line of the constructor. The action 'plugin_specific_action_name' didn't have any function associated anywhere. The action hook is plugin specific. Since it didn't have any function associated with it, what does it helps the plugin with.
The author has commented "Announce that the class is ready, and pass the object (for advanced use)". Can someone help me what exactly will that be used for? Why do I have to use this in the constructor function? What will be the advanced use cases I can use that for? Any help in clearing this for me will be appreciated
Ok. do_action or do_action_ref_array is just a placeholder. Any custom behavior can be introduced here with help of add_action. If nothing is specified the action will be muted. So it is a place holder. The eye opener for me here is do_action can exist without add_action
I am learning about OOP, and I think I am getting the hang of it. My question is why the author of a wordpress boilerplate plugin wrote the add action function like this
add_action('admin_init', array(&$this, 'admin_init'));
According to the codex I understand the add_action hook, the paramaters to be passed are $tag "The name of the action to which $function_to_add is hooked", and the function that you want hooked $function_to_add.
Well I understand the functions in OOP are methods, and I can see why that may change the syntax, but that is just a vague representation, I want a clear answer why the author uses an array, then uses &$this. I understand why one would use $this->property but not so sure about &$this.
Is it just how you refer to the method? If so I still dont understand the array, why wouldnt it look something like $this->admin_init.
By the way the structure looks kind of like this
class my_plugin_settings {
public function __construct() {
// register actions
add_action('admin_init', array(&$this, 'admin_init'));
}
public function admin_init() {
//Settings here
}
}
&this provides context to the PHP functions under the hood that hook it up to your admin_init function. Check out the declaration of add_action or take a look at http://www.php.net/manual/en/function.call-user-func-array.php.
That's how PHP knows to use the function in that object, not a global-level function
I am trying to create a wordpress plugin, I found one plugin which use oops concepts, my question is why the second parameter in the add_action function is an array instead of a function name
add_action('admin_menu', array(&$this,
'my_menu'));
my_menu is a function in the same class, please help me
Thanks
Because the second argument needs to be a callback. (and add_action internally uses call_user_func_array).
For functions we can just pass its name as a string but we can't do that with object methods, can we?
So an array is passed with 2 elements, first the object and second the method to call:-
array( $object, 'method' )
Oh and you can safely remove that useless '&', PHP4 days are gone now.
#Thomas John, you are correct about second argument in add_action also in wordpress org not mentioned anything about this so now, let me know you, we can pass array as second argument array($this,'method').
Description:
when object creates of class then constructor automatically calls and your action performs.
WHY IT REQUIRES
in wordpress how to create or initialize the class in add_action method in short add_action referencing a class check below example
class Myclass{
public function __construct() {
add_action( 'plugins_loaded', array( $this, 'load_plugin_textdomain' ) );
}
}
Referencing a class using add_action().