Letou nhà cái đánh giá

Let’s take a look at how WordPress handles it

Let’s take a look at how WordPress handles it

Want to get more articles like this one.
Join my newsletter WordPress for the adventurous: Entry points.
To master WordPress, you have to be able to look at it from unusual perspectives.
It’s exploring these perspectives that gives you that next level understanding.
You’re able to view problems from another angle.
This can give you unique insights that affect how you approach problems and design solutions for WordPress.
The angle we’re going to explore in this article is the concept of the entry point.
Let’s take a look at how WordPress handles it.
What is an entry point?.
The entry point is the piece of code that controls the entry to a program or a larger piece of code.
It could be a function.
It could be a file that’s read from top to bottom.
Each language has its own take on the concept of the entry point.
In PHP, the entry point is always the first file that the web server accesses.
It reads it from top to bottom and executed in that order.
The web server configuration determines the PHP entry point.
You could limit to a single file or it could be any file accessed directly.
That flexibility is also a weakness when you’re trying to secure a PHP application.
In the context of WordPress, .

An entry point is a PHP file that initiates the loading of WordPress

WordPress has a surprising amount of entry points

A lot of them exist to handle specific tasks which you’ll see later.
It’s all about security.

WordPress entry points are a potential security vulnerability

Some entry point vulnerabilities are with PHP and your web server configuration.
That’s why a lot of WordPress hardening recommendations are about securing the files on your server.

You want to make sure that you limit the access to WordPress files

Some WordPress security issues are deliberate attempts to abuse these entry points and your lack of understanding about them.
Hackers can use them for brute-force attacks like the ones you see on “wp-login.php” or “xmlrpc.php”.
These attacks are attempts to gain access to your admin console.

WordPress entry points are also vulnerable to denial-of-service attacks

Each individual entry point is vulnerable to those attacks.
A lot of these issues come from a simple lack of knowledge.

It’s not that WordPress itself isn’t secure

It’s that you can’t take extra steps to secure what you don’t know about.
Going over entry points.
The goal here isn’t to bore you with an extensive list of entry points.
You can find most of them by searching WordPress for files that require “wp-load.php” or “admin.php”.

Those are the bootstrap files for the frontend and the admin of WordPress

There’s some exceptions to that rule like “xmlrpc.php”.
That said, you’ll get a good overview using those two search parameters.
The default entry points.
The default entry points to WordPress are the “index.php” files.
This isn’t by accident.
The WordPress server configuration says that the default file for PHP to process is “index.php”.
Your server uses that default file when it doesn’t receive a request for a file.
It doesn’t have to be a PHP file.
It could be any file.
So let’s say you’re requesting “/wp-admin”.
Your browser address bar isn’t pointing to a file.
That means you’re using “index.php” in the “wp-admin” folder as the entry point.
Simple as that.
“index.php” files are powerful for that reason.
That’s why it’s always a good idea to put an empty “index.php” file in your plugins.
You can leave it empty or add the well known “Silence is golden.” inside.
Task based entry points.
WordPress uses a lot of entry points for tasks.
That’s where it gets a bit hairy.
There’s a lot of tasks and each has its own entry point.
That’s why listing them isn’t useful to you.
What’s more interesting is the types of tasks that these entry points take care of.
Cron.
Good old WordPress cron… The internet contains countless threads of WordPress cron not running, missed schedule errors and other issues.
The goal was to offer a way to schedule jobs for installations that couldn’t control the cron jobs on their server.
WordPress implements cron using a custom entry point.
Whenever someone loads a page, WordPress will send an asynchronous request to “wp-cron.php”.
“wp-cron.php” checks for scheduled cron jobs and runs them.
The issue is that you need someone to visit your site for WordPress cron to run.
This isn’t consistent.
That’s where understanding that entry point interaction was useful.
The solution to the issue was to create a server level cron job to run “wp-cron.php”.
This guaranteed that WordPress cron jobs would run at a regular interval.
Using the Upgrade API.
Upgrade API you say.

WordPress has an API for dealing with installing and upgrading WordPress

It’s found in the “wp-admin/includes/upgrade.php”.

The API contains functions like “wp_install” for installing a WordPress blog

It also has useful helper functions for interacting with the MySQL database.
One of them is “dbDelta”.
You use it when you want to maintain a database table with your plugin.
The API isn’t loaded by default.
Only special entry points that deal with installing and upgrading WordPress use it.
That includes the network install as well.
Admin pages.
Each WordPress admin page is an entry point.
There’s over 70 of admin pages in WordPress if you include all the network ones.
That makes for a ridiculous amount of entry points.
Why are each of them an entry point.
That’s a question I wasn’t able to find an answer to.
There’s an API for administration menus.
WordPress core doesn’t seem to use it though.
Regardless, this is why it’s important to take extra care to secure “/wp-admin”.
Compromising any of these files would compromise your WordPress installation.
External interactions.
There’s entry points dedicated to communicating with WordPress.
These include systems like: XML-RPC.
Post via Email.
Trackbacks.
AJAX.
Commenting.
The noteworthy one is XML-RPC.
It’s used by almost every app or service that interacts with WordPress.
The unique issue with it is that you need to log into WordPress for each call made to the XML-RPC API.
There’s no cookie that WordPress can use to keep you authenticated.
So for every command, your username and password is sent in plain text to your WordPress site.
That makes you vulnerable to man-in-the-middle attacks.

That’s unless you use a SSL certificate on your WordPress site

Authentication layer.
WordPress uses a few entry points for its authentication layer.
The most known is “wp-login.php” which is the WordPress login page.
There’s also “wp-activate.php” and “wp-signup.php” when you enable user registration.
These entry points (“wp-login.php” usually) are often the target of brute-force attacks.
That’s why there’s a lot of plugins to deal with this issue.
You can also protect “wp-login.php” at the web server level to help with the issue.
Another way to look at WordPress security.
It’s often misunderstood how WordPress security works.
You follow a list of recommendations and don’t twice about it.
Looking at WordPress entry points can help you make sense of some of these recommendations.
It’s all about that other angle.
Sometimes looking at things from another angle can give you the understanding you need.
Tweet.
#Entry Point # #WordPress for the Adventurous Designing a class: WordPress AJAX handler Spying on WordPress with the proxy pattern.


Designing a system: WordPress admin pages

Want to learn object-oriented programmming.
Get my free course Designing a system: WordPress admin pages.
In a previous article, we saw how to design a class to represent a WordPress admin page.
The article was an excellent resource for anyone looking to design a class to solve that specific type of problem.
But it was only a starting point.
For example, we used the settings API to handle forms.
But the settings API doesn’t quite do the job for every use cases.
For those other cases, you’re going to need to manage the submission of a form yourself.
But to handle these different use cases, we’re going to need a more solid foundation.
So we’re going to take the work that we did to design our admin page class, and we’re going to take it one step further.
We’re going to design a whole system for WordPress admin pages.
Reviewing our admin page class.
But first, let’s review some of what we had at the end of the previous article.
Instead of focusing on creating a system like we’re going to do now, that article focused on creating a class.
That class was the MyPlugin_AdminPage class.
class MyPlugin_AdminPage { /** * Path to the admin page templates.
* * @var string */ private $template_path; /** * Constructor.
* * @param string $template_path */ public function __construct($template_path) { $this->template_path = rtrim($template_path, ”/”); } /** * Configure the admin page using the Settings API.
*/ public function configure() { /// Register settings register_setting($this->get_slug(), ”myplugin_option”); // Register section and field add_settings_section( $this->get_slug().
”-section”, __(”Section Title”, ”myplugin”), array($this, ”render_section”), $this->get_slug() ); add_settings_field( $this->get_slug().
”-api-status”, __(”My option”, ”myplugin”), array($this, ”render_option_field”), $this->get_slug(), $this->get_slug().
”-section” ); } /** * Get the capability required to view the admin page.
* * @return string */ public function get_capability() { return ”install_plugins”; } /** * Get the title of the admin page in the WordPress admin menu.
* * @return string */ public function get_menu_title() { return ”My Plugin”; } /** * Get the title of the admin page.
* * @return string */ public function get_page_title() { return ”My Plugin Admin Page”; } /** * Get the parent slug of the admin page.
* * @return string */ public function get_parent_slug() { return ”options-general.php”; } /** * Get the slug used by the admin page.
* * @return string */ public function get_slug() { return ”myplugin”; } /** * Renders the option field.
*/ public function render_option_field() { $this->render_template(”option_field”); } /** * Render the plugin”s admin page.
*/ public function render_page() { $this->render_template(”page”); } /** * Render the top section of the plugin”s admin page.
*/ public function render_section() { $this->render_template(”section”); } /** * Renders the given template if it”s readable.
* * @param string $template */ private function render_template($template) { $template_path = $this->template_path.
”/”.
$template.
”.php”; if (!is_readable($template_path)) { return; } include $template_path; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 MyPlugin_AdminPage      * Path to the admin page templates.
$template_path      * @param string $template_path $template_path template_path rtrim $template_path ”/”      * Configure the admin page using the Settings API.
/// Register settings get_slug ”myplugin_option” // Register section and field get_slug.
”-section” ”Section Title” ”myplugin” get_slug get_slug.
”-api-status” ”My option” ”myplugin” ”render_option_field” get_slug get_slug.
”-section”      * Get the capability required to view the admin page.

Get_capability      * Get the title of the admin page in the WordPress admin menu

get_menu_title ”My Plugin”      * Get the title of the admin page.
”My Plugin Admin Page”      * Get the parent slug of the admin page.
get_parent_slug ”options-general.php”      * Get the slug used by the admin page.
get_slug ”myplugin”      * Renders the option field.
render_option_field render_template ”option_field”      * Render the plugin”s admin page.
render_page render_template ”page”      * Render the top section of the plugin”s admin page.
render_template ”section”      * Renders the given template if it”s readable.
* @param string $template render_template $template $template_path template_path.
”/”.
$template.
”.php”.
is_readable $template_path include $template_path As you can see above, the MyPlugin_AdminPage class is pretty big.
That’s because it has a method for each argument of the add_submenu_page function.
This is a design decision that allows the class to map to the add_submenu_page function in a simple way.

The MyPlugin_AdminPage class also made use of the settings API

We used it to create the different fields of the admin page.
We’d register them with the settings API, and it would put everything together for us.
The last few methods of the MyPlugin_AdminPage class had to do with HTML generation.
The settings API doesn’t generate any HTML for us, so we needed a way to do that.
The solution was to reuse a method to generate HTML from PHP template that we’d seen in a previous article.
function myplugin_add_admin_page() { $admin_page = new MyPlugin_AdminPage(); add_submenu_page( $admin_page->get_parent_slug(), $admin_page->get_page_title(), $admin_page->get_menu_title(), $admin_page->get_capability(), $admin_page->get_slug(), array($admin_page, ”render_page”) ); } add_action(”admin_menu”, ”myplugin_add_admin_page”); 1 2 3 4 5 6 7 8 9 10 11 12 13 myplugin_add_admin_page $admin_page MyPlugin_AdminPage add_submenu_page $admin_page get_parent_slug $admin_page $admin_page get_menu_title $admin_page get_capability $admin_page get_slug $admin_page ”render_page” ”myplugin_add_admin_page” Finally, the last little bit of code from the previous article was the myplugin_add_admin_page function.

This is the function that we used to register the MyPlugin_AdminPage class with WordPress

All that it did was call the add_submenu_page using the methods from the MyPlugin_AdminPage class for the function arguments.
The myplugin_add_admin_page function gets called by the plugin API.
We register it to the admin_menu hook using the add_action function.
The admin_menu hook that you should always use whenever you want to add admin pages.
Creating an interface.
An essential aspect of designing an object-oriented system is to have it rely on interfaces and not concrete classes.
This is an important element the dependency inversion principle.
This means that we need to extract an interface from our MyPlugin_AdminPage class.
/** * A WordPress admin page.
*/ interface MyPlugin_AdminPageInterface { /** * Get the capability required to view the admin page.
* * @return string */ public function get_capability(); /** * Get the title of the admin page in the WordPress admin menu.
* * @return string */ public function get_menu_title(); /** * Get the title of the admin page.
* * @return string */ public function get_page_title(); /** * Get the parent slug of the options page.
* * @return string */ public function get_parent_slug(); /** * Get the slug used by the admin page.
* * @return string */ public function get_slug(); /** * Renders the admin page.
*/ public function render_page(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 * A WordPress admin page.
interface MyPlugin_AdminPageInterface      * Get the capability required to view the admin page.

Get_capability        * Get the title of the admin page in the WordPress admin menu

get_menu_title        * Get the title of the admin page.
* Get the parent slug of the options page.
get_parent_slug        * Get the slug used by the admin page.
get_slug        * Renders the admin page.

Render_page Above is the MyPlugin_AdminPageInterface interface

It’s the result of this extraction process.
It contains the relevant methods from MyPlugin_AdminPage class.
It’s worth taking a moment to discuss why we chose these methods and not the others.
All these methods have one thing in common.
They’re all methods that we use to map our admin page class to the add_submenu_page function.
We omitted every other method.
(At least for now.) The reason for doing this is that we want to start with the smallest interface possible.
This will allow our system to be much more flexible.
So for now, .

MyPlugin_AdminPageInterface interface only has those methods

There’s no support for the settings API like the original MyPlugin_AdminPage class has

We’ll see how to handle the settings API using an interface a bit later.
Adding our admin pages.
Next, we want to look at how we’ll register our admin pages with WordPress.
This is the task that the myplugin_add_admin_page function did in our previous article.
If you want you can continue using it, you can with this small change: function myplugin_add_admin_pages() { $admin_pages = array( // Put admin page objects here ); foreach ($admin_pages as $admin_page) { add_submenu_page( $admin_page->get_parent_slug(), $admin_page->get_page_title(), $admin_page->get_menu_title(), $admin_page->get_capability(), $admin_page->get_slug(), array($admin_page, ”render_page”) ); } } add_action(”admin_menu”, ”myplugin_add_admin_pages”); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 myplugin_add_admin_pages $admin_pages // Put admin page objects here foreach $admin_pages as $admin_page add_submenu_page $admin_page get_parent_slug $admin_page $admin_page get_menu_title $admin_page get_capability $admin_page get_slug $admin_page ”render_page” ”myplugin_add_admin_pages” Here’s the modified myplugin_add_admin_page function.
First, we changed the name and made it plural.
This is to represent the fact that it can add more than one admin page object.
We also changed the admin_page variable and also made it plural.
Instead of containing a single admin page object, it’s now an array of admin page objects.
We use a foreach loop to add them all using the add_submenu_page function.
Adding back the rendering system.
Now that we have a foundation for our admin page system, we can start fleshing it out.
The first thing we can look at doing is adding back the rendering system that we had in the MyPlugin_AdminPage class.
To do that, we’re going to use an abstract class.
/** * A WordPress admin page rendered using a PHP template.
*/ abstract class MyPlugin_AbstractRenderedAdminPage implements MyPlugin_AdminPageInterface { /** * Path to the admin page”s templates.
* * @var string */ protected $template_path; /** * Constructor.
* * @param string $template_path */ public function __construct($template_path) { $this->template_path = rtrim($template_path, ”/”); } /** * Render the admin page.
*/ public function render_page() { $this->render_template(”page”); } /** * Renders the given template if it”s readable.
* * @param string $template */ protected function render_template($template) { $template_path = $this->template_path.
”/”.
$template.
”.php”; if (!is_readable($template_path)) { return; } include $template_path; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 * A WordPress admin page rendered using a PHP template.
MyPlugin_AbstractRenderedAdminPage implements MyPlugin_AdminPageInterface      * Path to the admin page”s templates.
$template_path        * @param string $template_path $template_path template_path rtrim $template_path ”/”        * Render the admin page.
render_page render_template ”page”        * Renders the given template if it”s readable.
* @param string $template render_template $template $template_path template_path.
”/”.
$template.
”.php”.
is_readable $template_path include $template_path Here is our MyPlugin_AbstractRenderedAdminPage abstract class.
This class implements our MyPlugin_AdminPageInterface interface.
That said, we’ve only implemented the render_page method.
(This is also why the class needs to be abstract.) The reason why we’ve only implemented the render_page method is that we care about.
The goal of the MyPlugin_AbstractRenderedAdminPage class is to be a base class for any class that wants to use PHP templates.
And this happens in the render_page method.
The MyPlugin_AbstractRenderedAdminPage class also has the render_template helper method.
We’ve changed its visibility from private to protected.
This will allow any class that extends our MyPlugin_AbstractRenderedAdminPage class to use it.
Adding back support for the settings API.
Next, we’re going to look into adding back the support for the settings API.
This is going to be a bit different from what we did with the rendering system.
Instead of using an abstract class, we’re going to use inheritance.

/** * A WordPress admin page configured using the settings API

*/ interface MyPlugin_ConfigurableAdminPageInterface extends MyPlugin_AdminPageInterface { /** * Configure the admin page using the Settings API.
*/ public function configure(); } 1 2 3 4 5 6 7 8 9 10 * A WordPress admin page configured using the settings API.
interface MyPlugin_ConfigurableAdminPageInterface MyPlugin_AdminPageInterface      * Configure the admin page using the Settings API.
Here’s what we mean by using inheritance.
We created a new interface called MyPlugin_ConfigurableAdminPageInterface.
This new interface extends our original MyPlugin_AdminPageInterface.
Why extend?.
A valid question would be, “Why do we need to extend the MyPlugin_AdminPageInterface?” After all, we could just have the MyPlugin_ConfigurableAdminPageInterface with just the configure method.
And any admin page who wants to use the settings API would have to implement both interfaces instead of just the one.
(This would be a type of multiple inheritance.) The reason not to go this route is subjective.
From a design perspective, a standalone MyPlugin_ConfigurableAdminPageInterface interface doesn’t make much sense.
There aren’t really any scenarios where you’d use the MyPlugin_ConfigurableAdminPageInterface interface without also using the MyPlugin_AdminPageInterface interface as well.
That said, if you had a standalone system for the settings API, it would make sense for that system to have an interface like the MyPlugin_ConfigurableAdminPageInterface interface.
But such a system is out of the scope of this article.
So, for now, the MyPlugin_ConfigurableAdminPageInterface interface will extend the MyPlugin_AdminPageInterface interface.
Configuring the settings API.
At this point, we have an interface that we can use to configure the settings API, but we still don’t have code to use that interface.
This is what we have to do next.
We have to create a function that calls the configure method at the right moment in the WordPress loading process.
function myplugin_configure_admin_pages() { $admin_pages = array( // Put admin page objects here ); foreach ($admin_pages as $admin_page) { if ($admin_page instanceof MyPlugin_ConfigurableAdminPageInterface) { $admin_page->configure(); } } } add_action(”admin_init”, ”myplugin_configure_admin_pages”); 1 2 3 4 5 6 7 8 9 10 11 12 myplugin_configure_admin_pages $admin_pages // Put admin page objects here foreach $admin_pages as $admin_page $admin_page instanceof MyPlugin_ConfigurableAdminPageInterface $admin_page ”myplugin_configure_admin_pages” Above is the myplugin_configure_admin_pages function used to configure our admin pages that want to use the settings API.
It’s very similar to our earlier myplugin_add_admin_pages function.
It’s basically a foreach loop through all our admin page objects.
The difference is that inside the loop we do use a conditional to check if the admin page object implements the MyPlugin_ConfigurableAdminPageInterface interface.
We use the instanceof operator to do it.
If the given admin_page implements the MyPlugin_ConfigurableAdminPageInterface interface, we call the configure method.
It’s also worth mentioning that the myplugin_configure_admin_pages function also gets called using the plugin API.
We don’t use the admin_menu hook this time.
Instead, the hook that you should use with the settings API is the admin_init hook.
How do you manage your admin page objects?.
Now, there’s a problem with the myplugin_configure_admin_pages function that we haven’t discussed.
It’s that it has a admin_pages variable which contains our admin page objects.
The problem with that is that our myplugin_add_admin_pages function also had that same variable.
So what do we do.
We can’t have two admin_pages variables that initialize our admin page objects.
One solution is to move the initialization of our admin page objects to its function like this: function myplugin_get_admin_pages() { return array( // Put admin page objects here ); } 1 2 3 4 5 myplugin_get_admin_pages // Put admin page objects here The myplugin_get_admin_pages function does exactly that.
It just returns our array of admin page objects.
We can then replace the admin_pages array in our functions with this: $admin_pages = myplugin_get_admin_pages(); 1 $admin_pages myplugin_get_admin_pages We use the myplugin_get_admin_pages function to fetch our admin page objects whenever we need them.
In the case of our two earlier functions, we use it to populate our admin_pages array.
This solves the issue of which of these functions creates our admin page objects.
Using the event management system instead.
That said, this is far from an ideal way to solve the problem.
For one, it’s a procedural way of solving the problem.
This isn’t bad per se.
But it isn’t helpful when the goal of this article is to show you how to create an object-oriented system.
Second, it doesn’t really show you how to combine various systems together.
(And that’s one of the goals of the goals of these articles.) So we’re going to take a moment and do that.
We’re going to combine this system with the event management system.

/** * Subscriber that registers the plugin”s admin pages with WordPress

*/ class MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface { } 1 2 3 4 5 6 7 * Subscriber that registers the plugin”s admin pages with WordPress.
MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface   We’re going to start with just an empty MyPlugin_AdminPagesSubscriber class.
It implements the MyPlugin_SubscriberInterface from the event management system.
This interface has a contract that requires any class that implements it to have a get_subscribed_events static method.
Storing our admin page objects.

Now that we have the foundation of our MyPlugin_AdminPagesSubscriber class

let’s flesh it out some more.
The first thing that it needs to have is a way to store all our admin page objects.
We can do that using a private property.
/** * Subscriber that registers the plugin”s admin pages with WordPress.
*/ class MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface { /** * The admin pages that the subscriber manages.
* * @var MyPlugin_AdminPageInterface[] */ private $admin_pages; /** * Constructor.
* * @param MyPlugin_AdminPageInterface[] $admin_pages */ public function __construct(array $admin_pages) { $this->admin_pages = $admin_pages; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 * Subscriber that registers the plugin”s admin pages with WordPress.
MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface      * The admin pages that the subscriber manages.
* @var MyPlugin_AdminPageInterface[] $admin_pages        * @param MyPlugin_AdminPageInterface[] $admin_pages $admin_pages admin_pages $admin_pages Above is the modified MyPlugin_AdminPagesSubscriber class.
We added the admin_pages private property.
We also added a constructor that has an array of admin pages as an argument.
But just assigning the admin_pages variable to the admin_pages property like that isn’t great.
That’s because we say that the admin_pages array should only contain objects implementing the MyPlugin_AdminPageInterface interface.
But the reality is that we have no way of guaranteeing that the way that we coded our constructor.
/** * Subscriber that registers the plugin”s admin pages with WordPress.
*/ class MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface { /** * The admin pages that the subscriber manages.
* * @var MyPlugin_AdminPageInterface[] */ private $admin_pages; /** * Constructor.
* * @param MyPlugin_AdminPageInterface[] $admin_pages */ public function __construct(array $admin_pages) { $this->admin_pages = array(); foreach ($admin_pages as $admin_page) { $this->add_admin_page($admin_page); } } /** * Add a new admin page to the subscriber.
* * @param MyPlugin_AdminPageInterface $admin_page */ private function add_admin_page(MyPlugin_AdminPageInterface $admin_page) { $this->admin_pages[] = $admin_page; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 * Subscriber that registers the plugin”s admin pages with WordPress.
MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface      * The admin pages that the subscriber manages.
* @var MyPlugin_AdminPageInterface[] $admin_pages        * @param MyPlugin_AdminPageInterface[] $admin_pages $admin_pages admin_pages   foreach $admin_pages as $admin_page $admin_page        * Add a new admin page to the subscriber.
* @param MyPlugin_AdminPageInterface $admin_page MyPlugin_AdminPageInterface $admin_page admin_pages $admin_page Here’s an improved MyPlugin_AdminPagesSubscriber class with the added validation of the admin_pages array.
There’s a new private method called add_admin_page.
It’s now the method in charge of adding admin page objects to the admin_pages array.
The add_admin_page method validates the admin page object using type hinting.
Using our new add_admin_page method, we can then rework the constructor.
We remove the old assignment of the admin_pages variable to the admin_pages property.
We replace it with just assigning an empty array to the admin_pages property.
We then use a foreach loop to loop through the admin_pages variable.
We then pass each admin page object to our new add_admin_page method.
This is what ensures that every admin page object in our admin_pages array implements our MyPlugin_AdminPageInterface interface.
Moving our functions.
At this point, all that we’ve done is move everything related to the myplugin_get_admin_pages function to our MyPlugin_AdminPagesSubscriber class.
We haven’t touched either function that used the myplugin_get_admin_pages function.
Those were the myplugin_add_admin_pages and myplugin_configure_admin_pages functions.
Moving these two functions to our MyPlugin_AdminPagesSubscriber class is quite straightforward.
To begin, the MyPlugin_SubscriberInterface interface requires that we create the get_subscribed_events public static method.
This method is where we’ll put the hooks that we registered with the add_action function before.
/** * Subscriber that registers the plugin”s admin pages with WordPress.

*/ class MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface { //

/** * Returns an array of events that this subscriber wants to listen to.
* * The array key is the event name.
The value can be: * * * The method name * * An array with the method name and priority * * An array with the method name, priority and number of accepted arguments * * For instance: * * * array(”event_name” => ”method_name”) * * array(”event_name” => array(”method_name”, $priority)) * * array(”event_name” => array(”method_name”, $priority, $accepted_args)) * * @return array */ public static function get_subscribed_events() { return array(); } //.
} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 * Subscriber that registers the plugin”s admin pages with WordPress.
MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface        * Returns an array of events that this subscriber wants to listen to.
* The array key is the event name.
The value can be:      *  * The method name      *  * An array with the method name and priority      *  * An array with the method name, priority and number of accepted arguments      * For instance:      *  * array(”event_name” => ”method_name”)      *  * array(”event_name” => array(”method_name”, $priority))      *  * array(”event_name” => array(”method_name”, $priority, $accepted_args)) get_subscribed_events   Above you can see the get_subscribed_events method.
For now, it returns an empty array.
But the array that it returns needs to follow the convention of the MyPlugin_SubscriberInterface interface.
This convention is written in the PHPdoc of the method.
Next, .

We have to move our two functions to the MyPlugin_AdminPagesSubscriber class

Once that’s done, we also need to add the hooks and method names to the array that the get_subscribed_events method returns.
You can see this all below.
/** * Subscriber that registers the plugin”s admin pages with WordPress.
*/ class MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface { //.
/** * Returns an array of events that this subscriber wants to listen to.
* * The array key is the event name.
The value can be: * * * The method name * * An array with the method name and priority * * An array with the method name, priority and number of accepted arguments * * For instance: * * * array(”event_name” => ”method_name”) * * array(”event_name” => array(”method_name”, $priority)) * * array(”event_name” => array(”method_name”, $priority, $accepted_args)) * * @return array */ public static function get_subscribed_events() { return array( ”admin_init” => ”configure_admin_pages”, ”admin_menu” => ”add_admin_pages”, ); } /** * Adds the plugin”s admin pages to the WordPress admin.
*/ public function add_admin_pages() { foreach ($this->admin_pages as $admin_page) { add_submenu_page( $admin_page->get_parent_slug(), $admin_page->get_page_title(), $admin_page->get_menu_title(), $admin_page->get_capability(), $admin_page->get_slug(), array($admin_page, ”render_page”) ); } } /** * Configure the plugin”s admin pages using the Settings API.
*/ public function configure_admin_pages() { foreach ($this->admin_pages as $admin_page) { if ($admin_page instanceof MyPlugin_ConfigurableAdminPageInterface) { $admin_page->configure(); } } } //.
} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 * Subscriber that registers the plugin”s admin pages with WordPress.
MyPlugin_AdminPagesSubscriber implements MyPlugin_SubscriberInterface        * Returns an array of events that this subscriber wants to listen to.
* The array key is the event name.
The value can be:      *  * The method name      *  * An array with the method name and priority      *  * An array with the method name, priority and number of accepted arguments      * For instance:      *  * array(”event_name” => ”method_name”)      *  * array(”event_name” => array(”method_name”, $priority))      *  * array(”event_name” => array(”method_name”, $priority, $accepted_args)) get_subscribed_events ”configure_admin_pages” ”add_admin_pages”        * Adds the plugin”s admin pages to the WordPress admin.
add_admin_pages foreach admin_pages as $admin_page add_submenu_page $admin_page get_parent_slug $admin_page $admin_page get_menu_title $admin_page get_capability $admin_page get_slug $admin_page ”render_page”        * Configure the plugin”s admin pages using the Settings API.
configure_admin_pages foreach admin_pages as $admin_page $admin_page instanceof MyPlugin_ConfigurableAdminPageInterface $admin_page   We added two new methods: add_admin_pages and configure_admin_pages.
The two methods kept the same name as our earlier functions.
The only difference is that we removed the myplugin_ prefix since it’s not useful inside a class.
The code inside the methods hasn’t changed from the earlier version with the functions with one exception.
We replaced the use of the admin_pages variable with the admin_pages class property.
That class property was the main reason why we created the MyPlugin_AdminPagesSubscriber class in the first place.
A solid foundation.
So this is a good place to wrap things up.
With what we’ve seen in this article, you have all that you need to create admin pages like in the previous article.
But now, you’ve broken things down more and built a system around it.
The great thing with this system is that it lets you handle different use cases.
So far, we’ve only covered the use cases from the previous article like rendering templates and using the settings API.
But, we’ll look at some more in the future.
You can find a gist with all the complete code sample here.
Photo Credit: Hal Gatewood.
Tweet.
#Designing a system #Object-oriented Design #Object-oriented Programming #Settings API # #WordPress admin 2018 in review: A challenging year How to approach object-oriented programming with WordPress.