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.


it got accepted for WordCamp Miami

Want to get more articles like this one.
Join my newsletter In Marketing How I marketed and published a niche WordPress book.
A few weeks ago, .

I released my first book “Discover object-oriented programming using WordPress“

The book had $11,040 in sales during its launch week.
This went above and beyond the expectations that I had for how well the book would do.
(I would’ve been ecstatic if it’d made even half of that.) But before I go any further, I want to do a small preface.
What I am going to talk about isn’t anything that I really figured out myself.
All that I did was read a lot of what others had done and then tried to do it myself in my own authentic way.
(That last part is super important though!) I’m especially thankful to Nathan Barry and his book “Authority“.
(Sadly, he doesn’t sell just the book anymore.) I’m also super grateful to Paul Jarvis who’s inspired me to have my own quirky voice through his newsletter.
He and Justin Jackson (not the basketball player!) taught me that marketing didn’t have to be this slimy thing if you were authentic about it.
(Justin has a course on marketing for developers.) That’s also why I’m taking the time to write down all my thoughts about this experience while everything is still fresh.
(Much like my year in reviews.) I was only successful because other people shared how they approached marketing and self-publishing a book.
I want to do the same by sharing as much information as I can for anyone interested in marketing and publishing their own book.
Having a newsletter.
The centrepiece of marketing a book (or any other product) is having a newsletter.
This is something that you’ll hear over and over from a lot of people selling things on the internet.
The newsletter is how you build an audience that trusts you and will want to hear what you’re creating.
(And maybe even pay for it!) Now, there’s no right way to do a newsletter.
(Paul Jarvis has a pretty good take on what makes for a good newsletter and how to start one.) What I did was just start teaching everything I knew to the WordPress community.
I then put up a newsletter signup page to try to stay in touch with anyone who liked my writing.
That said, it’s worth pointing out that creating a newsletter signup page isn’t some magical thing.
It doesn’t mean that you’ll end up with thousands of subscribers overnight.
It does happen for some people, but it didn’t for me.
The main reason why that didn’t happen was that I was pretty much writing for a niche within a niche.
(And even maybe within another niche!) So to try to increase the number of people signing up to the newsletter, I added a book landing page in August 2014.
I never tracked how effective it was for me to do this, but it didn’t really matter either.
I was just trying to find another incentive for someone to subscribe to my newsletter.
I was willing to try things that I didn’t consider dishonest or annoying like exit popups.
(I did do that little slide down below the menu, but I haven’t tracked how well it works either.
I know.
I’m bad at this.) I wasn’t against the idea of writing a book if there was enough interest in it so the book landing page felt ok to me.
But what was enough interest for me.
Well, enough interest was about a thousand newsletter subscribers.
This seemed to be the sweet spot based on my research.
Less than that and the odds were good that I wouldn’t make any money.
Anything above that seemed to lead to pretty good launch outcomes.
(I’d read people making a few thousand dollars to $50,000 or more.) And then nothing.
Once I had a landing page for the book, I did nothing else related to it for a few years.

My main focus was just writing articles and speaking at WordCamps

Over time, my newsletter grew to a point where I felt I’d have over a thousand subscribers when I’d be ready to launch it.
That was in 2016.
I spent most of 2017 writing it slowly.
(As I do with everything I write.) 2018 was all about editing it and working on the extra material for the more expensive packages.
I won’t talk too much about all that in this article since there’s a lot to write about there as well.
But as 2018 was wrapping up, I was far enough along that I’d started thinking about ways to market the book.
Normally, you want to do all these guest posts and try to get the word out as much as possible.
I already wrote a lot.
I didn’t want to write guest posts.
Picking my launch date.
But one content marketing idea I had was to do a WordCamp talk where I could promote the book at the end.

That talk was “How to approach object-oriented programming with WordPress“

I started submitting it to WordCamps at the end of 2018

I also told myself that, when a WordCamp selected it, I’d set a deadline for launching the book.

It wasn’t picked up by any WordCamps in 2018

But in January, it got accepted for WordCamp Miami.
This meant that I now had to set a deadline.

I needed the book to be out by the time I spoke at WordCamp Miami

The date I chose was Wednesday Match 6th which was just one week before the camp.
I had a few reasons for picking that date.
I have no idea whether they were good or not since I’ve only launched one book.
But they made sense in my head at the time.
(The best selection criteria obviously.
lol) First, a Wednesday seemed like the optimal day to launch a programming book.
There are almost no major holidays on a Wednesday.
It’s also mid-week.
I’m catching everyone near the peak of where they’re thinking about programming.

I also chose that week because it was a week before WordCamp Miami

I felt this would give me enough time to focus on the launch and finish preparing my talk.
In practice, it was enough time because I started preparing my talk a month before.
But it was still stressful because I was still in book launch mode the following week.
I’d bump that to two weeks if I did it again.
Building excitement for the launch.
With my launch date selected, I started promoting the launch of the book.
I didn’t really know what to do for that either.
I ended up mostly following the launch plan from Justin Jackson’s marketing for developers book.
Four weeks prior.
The first promotional message that I did was four weeks before the launch.
I tweeted that the book would come out next month.
I also sent an email to my newsletter which you can see below.
I didn’t put a specific date because I wasn’t sure if I’d hit my deadline or not.
I still had a lot left to do.
The largest one was that the screencasts which I couldn’t record because I was in Mexico.
(Though life I know!) That tweet ended up doing very well.
It got 37 retweets and a lot of likes.
But more importantly, I added 44 new subscribers that week which is more than I add in a month usually.
Foreword update.
A week before launch, I sent a final email update about the book.
(You can see it below.) That update focused on announcing that the book would have a foreword by Tom McFarlin.
I was so happy with the foreword that Tom wrote so it felt great sharing it with everyone.
The truth is that I hadn’t planned on having a foreword for the book.
Tom was just super nice and offered to do one.
I feel really fortunate for having had that happen.
But it was also a unique marketing opportunity.

Tom is a pretty well-known WordPress developer

I figured I could test if announcing a foreword by him did anything to create excitement for the book.
So I tweeted about the upcoming foreword email a few days before I sent it out.
The tweet wasn’t even close to as successful as the one I’d done announcing that the book’s release.
I only got six newsletter subscribers during the period between when I tweeted about the foreword and when I sent the email for it.
Day before.
The next promotional message was the day before the book launch.
I sent to different emails to my newsletter.
The one below was if you hadn’t shown any interest in the book.
While I didn’t want to spam people that hadn’t asked for book updates, I also didn’t want to ignore them either.
I figured the day before was a good day to bring up the fact that I had a book coming out.
I also gave them a link to the sample chapter if they wanted to get one.
The link had a 14% click rate which is quite good.
Above is the email I sent to the people who were getting book updates.
It’s very different from the other one.
It follows Justin’s recommendation in his marketing for developers book I also tweeted that the book was launching the day before.
This tweet did really well much like the one announcing the book launch a month before.
It got 26 retweets and reached a lot of people.
I also got a lot of newsletter subscribers from it as well.
I don’t know how many exactly because the launch messed with the numbers.
(I’d say at least as many as the tweet I sent a month before.) The reason why I think the tweet did really well was because it had an incentive.
It said that’d they’d get launch discount if they signed up.
Launch day.
So this brings us to the launch day.
I started the day by switching the book landing page to the book sales page.
(I’ll talk about the sales page in the next section.) In terms of communication, I followed the same formula as the other days.
I sent an email announcing the book launch to my newsletter which you can see below.
I didn’t really have a plan when I wrote that email.
I just wanted to talk about why I wrote the book and why I thought it was important for the WordPress community.
It ended up being quite a long email so I put a TL;DR at the top with the launch discount.
I also tweeted about the book launch.
I wanted to have this tweet pinned at the top of my Twitter profile so I wrote it in a more timeless way.
It also linked to the new book sales page without an attached image.
(I’ll talk about the book sales page later in the article.) I didn’t attach an image to the tweet because I made sure that the book sales page had a great link preview.
I don’t usually do anything special for social media or SEO with my articles.
But I figured it was probably a good idea to do it here since I have no idea what the long tail sales are going to be.

Sharing in the Advanced WordPress group

It was a good thing that I did the work making sure that the book sales page had a great link preview.
That’s because the book sales page was also shared in the Advanced WordPress group on Facebook.
This wasn’t something that I expected.
For me, it felt like too much self-promotion to post the book in the Facebook group.
I didn’t want to do it.
(It’s also why I never posted it on Reddit either.) But my friend Maxime Jobin insisted to do it so I let him make a post.
Even if Maxime created a post for me, .

It didn’t mean that it would make it on the Advanced WordPress group

It still needed the group administrators to approve it.
And I wasn’t sure if they would since it was about a product that you had to buy.
But they did.
And the reception was actually quite good which was a relief.
Maxime also offered to share the launch discount code if anyone wanted it.
A lot of people took him up on it.
I think not having a discount code for the Facebook group was a mistake on my part.
(Maxime even suggested I have one.) If I did it again, I would make one.
I could at least know if the group drove a lot of sales or not.
Last day of the launch sale.
While doing my research on how to market the book, I found plenty of information on what to do leading up to the launch.
But after that, there wasn’t much written about what to do.
So I decided to just go with what made sense to me based on my research.
The first thing that seemed to make sense was that I should send a reminder that the sale was ending.
People are busy.
They might have had the intention to buy the book, but life got in the way and they forgot.
So on the last of the sale, I sent the email that you see above to everyone who hadn’t bought the book.
I didn’t really know what to write in the email either so I mentioned that the book had been doing well.
I also put a screenshot of this tweet by Kevin Hoffman reviewing the complete package as a form of social proof.

The graph above from Gumroad shows my sales around the launch of the book

As you can see, I made almost as much money on that final day of the sale that I did on launch day.
It does feel like sending that email was probably a really good idea.
As with a lot of what I discuss in this article, it’s hard to know if it actually did do a difference.
I can’t see what would have happened if I hadn’t.
But I would probably send that email again if I launched another product.
Sales page.
Like I mentioned earlier, with the book launch, I replaced my old book landing page with a sales page.
There’s plenty of material out there on how to design and build a sales page, but I wasn’t too sure what I wanted to do anyway.
I just ended up piecing one together using a few sources as inspiration.
For the copywriting, I kept a lot of what I’d written for the book landing page and just polished it.
The copywriting for the original landing page was the result of following Amy Hoy’s teaching.
She talks a lot about her formula of talking about the reader’s pains and dreams, and how to bridge them.
The big copywriting addition to the new sales page was the hero image and text.
I ended up basing my text on the one Adam Wathan had for his “Refactoring to Collections” book.
Since the book did “quite well”, I figured it was probably an ok text to base myself on.
In fact, I based a lot of the rest of the sales page on the one for “Refactoring to Collections”.
I didn’t feel like reinventing the wheel and that one seems to have worked well for Adam.
I did leave some things out like the sample screencast and table of contents.
Communicating after buying the book.
Another thing that wasn’t discussed at all during my research was what to do once someone bought the book.
I wasn’t too sure what I wanted to do either.
That said, I made sure that I had an automation rule to add anyone that bought the book to a sequence.
Asking for Gumroad review.
At the end of the book, I ask if you can tweet or write a review for the book if it helped you.
That’s because I’m not doing anything else to market the book outside what we’ve been discussing in this article.
That means that I’m depending on word of mouth for marketing from this point on.
However, a month before the book launch, Gumroad came out with a product rating system.
Gumroad uses the rating system with its product recommendation system.
I haven’t made any sales with it yet and I don’t expect to if I’m being honest.
That said, it’s still another type of social proof.
Whenever someone clicks to buy one of the packages, they’ll see the rating for the product.
I felt that was probably valuable enough to ask people who bought the book to rate it.
Above you can see the email that I send.
The title of the email and the first paragraph bring up the favour that I asked in the book.
After that, I explain that Gumroad has a rating system and how they can rate the product.
I think that sending that email was a good call.
If you look at my Gumroad profile, you can see that the three main packages all have 5 stars.
That’s all I could ask for really.
Upselling the team package.
I also had another post-purchase email idea that I didn’t end up implementing.
It was about buying the team package.
The idea was to refund someone’s purchase if they got their work to buy it.
My idea was to talk about how getting the book into as many as possible was important.
That’s how we’ll get the WordPress community to learn object-oriented programming.
So, if they felt that the book would be helpful to their coworkers, they should get their work to buy it.
So why haven’t I sent this email.
Well, the truth is kinda chickened out.
The book did super well already and I felt it was kinda greedy to push for more.
I’m curious about your opinion on it though.
So feel free to let me know in the comments if you feel like I should have done this.
Maybe I’ll end up doing it.
Post-launch discount requests.
I took down the launch discount code at the end of the third day as I’d said I would.
But even with the emails, I still had people reach out a few days saying they missed the launch discount.
My policy for those requests was to create a custom one-time use discount code for them.
Why do that.
Well, because this book launch is far from being the most important going on in your life.
So you end up missing the emails and forgetting about the launch even if you had the intention to buy the book.
On top of that, if you reached out about it, you were probably really keen on buying it.
It didn’t make sense to me to be greedy.
I’d rather just build goodwill by extending you the discount.
Offering a discount for a WordCamp talk.

So what happened with my WordCamp Miami talk where I was going to promote the book

Well, due to technical difficulties, I wasn’t able to show the slide with the discount code.
So I wasn’t able to promote the book as I’d planned when I picked my launch date.
All wasn’t lost though.
While I wasn’t able to see if promoting a book at WordCamp talk generated any sales, I did do something else.
I also offered the discount code to my newsletter when I sent my email about the companion article to the talk.
This is actually the only mention of the discount code that I ended up doing.
What was interesting is that I did get 7 sales from mentioning it in the email.
So it wasn’t a bad idea to mention it in the email.
I just don’t know how many sales I would have made if I’d mentioned it at the talk.
(I’ll get a chance to test that again soon.) Sales breakdown.
So that pretty much covers everything that I did for the book launch.
Let’s wrap this up by going some of the sales numbers.
Launch sales.
First, let’s look at the sales during the 3-day launch.
I made $9,349.50 during that period.
If we break it down per day, it looks like this: Day 1: $3,826.50.
Day 2: $2,225.25.
Day 3: $3,297.75.
As I said, I think sending the email about the last day of the sale was a really good idea.
I think that’s part of the reason why the last day almost did as well as the first one.
Packages.
There’s one recommendation that I saw over and over researching how to market this book.
It was to not just sell the book.
There’s a limit to how much people are willing to spend on a book even if that’s where most of the value is.
Instead, the book should be one of several different packages.
Each package has a different value proposition at a different price point.
This allows you to increase the average revenue per customer.
So from the start, I was planning on doing more than one package for the book.
And was worthwhile to spend the time to create these different packages.
Yes, it was.
You can see it clearly when you look at the sales breakdown per package.
Book package: $2,496 (81 sales).
Premium package: $3,118.50 (42 sales).
Complete package: $3,735 (19 sales).
The book package had the most sales but generated the least amount of money.
Each package made progressively more money on fewer sales.
The complete ended up generating the most money on only 19 sales which is amazing.
This puts my average revenue at $65.84 per customer.
This is more than twice the revenue that I was making on the book during the sale.
(The book was $29.25 with the discount.) So needless to say, I’m happy that I did packages.
It was the difference of over $5,000 in revenue for me.
Refunds.
I suffered from a lot of impostor syndrome writing this book.
My biggest fear with the book was that people would hate it and ask for a refund.
The opening of the book was specifically written to deal with that fear.
First, I specifically explained what the book would be about and what it wouldn’t cover.
I think this is a good thing to do even if you don’t have any fear of refunds.
It ensures that the reader really knows what to expect from the book.
For me, this had the secondary benefit that I felt less guilty about a refund if that’s why they wanted one.
But I also said that they could ask for a refund for any reason and I’d be happy to give it.
Overall, doing this made me feel better about my fear of refunds.
But like most fears with impostor syndrome, it ended up being unfounded.
I had a single refund request since the book launched.
And it was a completely legitimate refund request based on what I’d written at the beginning of the book.
Where are we now?.
Over a month has passed since the launch, I’m now at $12,952.80 in sales.
So that means that I made about $2,000 in sales in the last month.
That’s pretty amazing.
I’m not sure where this number will be in a few months, but it doesn’t really matter.
This is more money than I ever expected to make with this book.
I’m really just grateful for everyone’s support.
I also hope that this will help anyone who’s thinking of launching a product of their own.
The whole thing was scary and daunting to me even with all the research.
So know that you’re not alone feeling that way, but that there’s always a potential for success.
Even for something as crazy as an object-oriented programming book for WordPress developers.
Tweet.
#Self-publishing #Transparency # How to approach object-oriented programming with WordPress Improving a system: Different types of WordPress admin pages.


we’re sure that post implements WP_Post_Interface

Want to learn object-oriented programmming.
Get my free course Using inheritance with WordPress.
As a WordPress developer, you’re always looking for ways to better reuse your code between projects.
Your time is valuable and you don’t want to reinvent the wheel each time you start a new project.
Object-oriented programming can help you with that.
In a previous post, I covered why you should learn it.
Now it’s time to take things further by going over the main feature for code reuse.
You’ve probably heard about it before.
It’s called “inheritance“.
Inheritance, what’s that?.
Inheritance is about maximizing the code reuse between your classes.
It allows you to create a hierarchy between your classes.
That hierarchy creates a “is-a” relationship between your classes.
That’s why a lot of examples follow the formula: class Car extends Vehicle (Car is a Vehicle).
class Dog extends Animal (Dog is an Animal).
They aren’t practical examples, but they highlight that relationship.
The take away from those examples is that you need to learn to see the relationship between your classes.
Once that happens, you’ll be able to leverage inheritance to create reusable code for your projects.
Visibility revisited.
When I covered encapsulation, I mentioned three possible access levels for properties and methods.
Public.
Private.
Protected.
“Public” meant that anyone could access the property or method.
Meanwhile, “Private” meant the opposite.
You could only access it internally.
It’s time to flesh out those definitions a bit more.
When using inheritance, defining a property or method as private restricts it to the class that defined it.
Its children can’t access it.
Meanwhile, protected grants access to the entire class hierarchy.
This means that child classes can access it.
The inheritance toolkit.
Before going ahead with a practical example, let’s go over some of the tools that you can use with inheritance.
A good understanding of that toolkit will allow you to leverage inheritance to its full potential.
Interfaces.
Interfaces are the most basic tool in your inventory.
You should see an interface as a contract.
By implementing an interface, your class agrees to implement specific methods.
You can only define public methods in an interface.
By only offering a contract, you let others worry about the implementation.
All that you care about is that the methods in your contract exist.
Let’s look at a small example.
interface Post_Interface { /** * Get the CSS class.
* * @return string */ public function get_class(); /** * Get the post type.
* * @return string */ public function get_type(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface Post_Interface      * Get the CSS class.
get_class        * Get the post type.
get_type The Post_Interface represents a small contract for WordPress posts.
It has two methods: get_class and get_type.
Any class that implements Post_Interface will need to have these methods.
Built-in PHP interfaces.
PHP is full of useful interfaces that you can use to make your life easier.
For example, Countable allows you to pass your object to the built-in count function.

Class Posts implements Countable

{ /** * @var Post_Interface[] */ protected $posts; //.
public function count() { return count($this->posts); } } $posts = new Posts(); $num_posts = count($posts); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Posts implements Countable      * @var Post_Interface[] $posts     count count posts   $posts Posts   $num_posts count $posts The Posts class above contains multiple instances of Post_Interface objects.

Because Posts implements the Countable interface, we can pass Posts to the count function

This gives us an easy way to get the total number of  Post_Interface objects in Posts

Interface inheritance.
Interfaces can also inherit from other interfaces.
That’s the case with the built-in interface Traversable.
Traversable allows you to use your object in a foreach loop, but you can’t use it directly.
You need to implement either Iterator or IteratorAggregate which are its child interfaces.
class Posts implements IteratorAggregate { /** * @var array */ protected $posts; //.
public function getIterator() { return new ArrayIterator($this->posts); } } $posts = new Posts(); foreach ($posts as $post) { // Do stuff } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Posts implements IteratorAggregate $posts     getIterator ArrayIterator posts   $posts Posts   foreach $posts as // Do stuff This example shows our Posts  class implementing the IteratorAggregate.
There’s just a small problem.
We can’t have our getIterator return our posts array as is.
That’s because of the IteratorAggregate contract.
It says that getIterator needs to return an object implementing a Traversable interface.
Lucky for us, there’s a PHP class designed to turn an array into a Traversable object.
That’s why getIterator returns an instance of ArrayIterator.
It’s a class that turns an array into Traversable object.
Now, .

We can loop through the posts in our Posts class

Implementing multiple interfaces.
While a class can only have one parent class, it can implement as many interfaces as it wants.
This is a powerful feature of interfaces.
You can have a single class fulfill as many contracts as it wants.
class Posts implements Countable, IteratorAggregate { /** * @var array */ protected $posts; //.
Public function count() { return count($this->posts); } public function getIterator() { return new ArrayIterator($this->posts); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Posts implements Countable IteratorAggregate $posts     count count posts   getIterator ArrayIterator posts For example, .

Our Posts class now implements both Countable and IteratorAggregate

This lets you pass a Posts object inside the count function and in a foreach loop. Both will work.
Abstract classes.
Let’s say you want to reuse your code in your classes, but each class has just a tiny difference between how they behave.
You could implement it partially, but the other classes would need to do the rest of the work. That’s what abstract classes do.
They partially implement the logic in a class, but leave some details for the child classes.
Abstract methods are the methods that a child class needs to implement.
They function the same way as a method defined in an interface.
The one difference is that you can define an abstract method as protected.
You can view an abstract class as an interface where you already coded some of the logic.
You should know that, because they are incomplete, you can’t instantiate abstract classes.
abstract class Abstract_Post implements Post_Interface { /** * @var int */ protected $id; /** * Get the CSS class.
* * @return type */ public function get_class() { return $this->get_type().
”-”.
$this->id; } abstract public function get_type(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Abstract_Post implements Post_Interface      * @var int        * Get the CSS class.
* @return type get_class get_type.
”-”.
get_type In the case of the Abstract_Post class shown above, we know some common details for all classes.
Every post will have a post ID.
It will also output its CSS class the same way.
get_class concatenates the post type and its ID together.
But we don’t know its post type yet.
That’s why get_type uses the abstract keyword.
It lets us use get_type in get_class without the need to code it.
We’ll leave that to the concrete classes.
Concrete classes.
Concrete classes are the classes that your code will end up using.
They might implement one or more interfaces and/or extend an abstract class.
The important thing is that you can instantiate these classes.

Class Attachment extends Abstract_Post { /** * Get the post type

* * @return string */ public function get_type() { return ”attachment”; } } $attachment = new Attachment(); $attachment->get_class(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Attachment Abstract_Post      * Get the post type.
get_type ”attachment”   $attachment Attachment   $attachment get_class Now, it’s time to code the get_type method.
For our Attachment class, we make it return attachment.
It’s the same value as the existing WordPress post type.
Because we’ve completed our class, we can instantiate it and use it.
Overriding methods.
Here is a scenario: You defined a method in your parent class.
It works for 90% of the use cases, but not in that one child class.
What can you do.
Well, you can override the method.
You override a method by defining that method again in a child class.
Doing so replaces the old one and allows you to add that custom logic you wanted.
class Video extends Attachment { /** * Get the post type.
* * @return string */ public function get_type() { return ”video”; } } 1 2 3 4 5 6 7 8 9 10 11 12 Video Attachment      * Get the post type.
get_type ”video” In our Video class, we overrode our get_type method.
Instead of returning attachment, it’ll return video.
The parent method is still available if you want to use it.
All you need to do is use the parent keyword.
This is useful when you only want to alter the output of the parent method.
class Picture extends Attachment { /** * Get the post type.
* * @return string */ public function get_type() { return ”picture-”.
parent::get_type(); } } 1 2 3 4 5 6 7 8 9 10 11 12 Picture Attachment      * Get the post type.
get_type ”picture-”.
parent :: get_type In this final example, the get_type method for the Picture class won’t return attachment.
Instead, it’ll return picture-attachment.
attachment comes from our call to parent::get_type().
Final keyword.
There might be some cases where you don’t want others to extend your class or override your method. That’s where the final keyword comes in.
It prevents someone from extending a class or overriding a method.
There’s rarely a case for a class to be final, but you might see final methods in some cases.
You generally use a final method when the method is crucial to the functioning of a class.
In that case, overriding the method might have dire consequences.
Making sure you get the right type of object.
How can you make sure that you are receiving the correct type of object in your code.
You don’t want errors because someone didn’t give you the correct object.
Let’s look at two options available to you so you don’t get errors in your code.
Type hinting.
Type hinting is a useful tool when using inheritance.
It allows you to force a function or method to accept only a specific type as a parameter.
This saves you the need to do the validation in your code.
That said, receiving an invalid parameter will cause a fatal error in PHP.
/** * Save post to the database.
* * @param WP_Post_Interface $post */ function save_post(WP_Post_Interface $post) { // You are sure $post implements WP_Post_Interface } 1 2 3 4 5 6 7 8 9 * Save post to the database.
* @param WP_Post_Interface $post save_post WP_Post_Interface // You are sure $post implements WP_Post_Interface Validating in your code.
You can also check the object type in your code as well.
PHP has two ways for you to do it.
You can use either the is_a function or the instanceof operator.
This is how WordPress likes to do it since it allows for defensive coding.
Using this form of validation prevents PHP fatal errors.
/** * Save post to the database.
* * @param mixed $post */ function save_post($post) { // You are not sure $post implements WP_Post_Interface if (!$post instanceof WP_Post_Interface) { return; } // You are sure $post implements WP_Post_Interface } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 * Save post to the database.

* @param mixed $post save_post // You are not sure $post implements WP_Post_Interface

instanceof WP_Post_Interface // You are sure $post implements WP_Post_Interface The point of the above example is to show the before and after state within our save_post method.
Before our instanceof check, our post variable could be anything.
But after it, we’re sure that post implements WP_Post_Interface.
What should you check?.
That’s the better question to ask.
Is it the correct class or just an interface.
While it’s up to you, using interfaces for validation allows for the most flexibility.
When you’re validating a class, you’re telling them how to interact with your code.
You’re asserting more control over it as a result.
They have to use your class or they can’t do anything.
Validating using an interface allows others to build what they want.
You’re not forcing them to use your classes.
You just want them to use the contract that you created.
A practical example with WP_Widget.
WordPress widgets are an excellent showcase on how to use inheritance.
There is a clear relationship between the WP_Widget class and all its children.
That distinct relationship allows for a large amount of code reuse.
You can see the impact of it by the small size of the child classes.
How it currently works.
To develop your own custom widget, you need to extend the WP_Widget class and add your own logic.
WordPress expects you to override the widget method.
Otherwise, it will throw a fatal error.
class WP_Test_Widget extends WP_Widget { public function widget($args, $instance) { echo ”test”; } } 1 2 3 4 5 6 7 WP_Test_Widget WP_Widget widget $args $instance ”test” You can find more information about making widgets in the codex.
Leveraging inheritance.
As an exercise, let’s rewrite parts of the code to use inheritance.
This should give you a good idea of how to use the tools described earlier.
Defining an interface.

Let’s start by defining the interface WP_Widget_Interface

You want to create a baseline contract for all widgets in WordPress.
By doing so, we establish which methods WordPress deems necessary for a widget class.

Interface WP_Widget_Interface { /** * Register the widget

*/ public function register(); /** * Echo the widget content.
* * @param array $args * @param array $instance */ public function widget($args, $instance); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface WP_Widget_Interface      * Register the widget.
* Echo the widget content.
* @param array $args      * @param array $instance widget $args $instance You’ll notice that the interface has only two methods.
This isn’t a mistake.
You want to limit the interface to required methods only.
The only public methods that WordPress uses are register and widget.
All other methods are internal.
It is worth noting that some methods need to be public due to the nature of the callback system.
That said, they should not be part of the interface.
WordPress does not need to be aware of them for you to fulfill your contract.
Taking a look at WP_Widget.
Now that we have defined an interface for widgets, let’s take a better look at the WP_Widget class.
Let’s rework it while maintaining the behaviour expected by WordPress.
Managing property and method visibility.
WP_Widget uses only public methods and properties.
The documentation defines private methods as methods meant for internal use.
We’re going to rework the visibility of the methods and properties.
That’s because we want them to follow encapsulation principles.
This means that only a few functions are public.
There’s the register and widget methods.
We defined those in the interface.
Also, you have the three callback methods which should also be public.
All other methods are for internal use only.
This means that you should define those methods as “protected”.
This goes for the properties as well.
Using an abstract class.
The WP_Widget class is an ideal candidate for an abstract class.
WordPress expects each child object to implement its own widget method.
By using an abstract class, we’re deferring the need to implement the method to the child class.
No need to use a “die”.
Using the final keyword.
WP_Widget documentation highlights methods that shouldn’t be overridden but leaves it at that.
We’re going to use the final keyword to lock down those methods.
We are, again, formalizing a behaviour that WordPress expects.
The result.
You can see the result below.
This isn’t an exact copy of all the methods.
It highlights the changes discussed earlier.
abstract class Abstract_WP_Widget implements WP_Widget_Interface { /** * @var array */ protected $widget_options; /** * @var string */ protected $id_base; /** * @var string */ protected $name; /** * @var array */ protected $control_options; /** * @var bool */ protected $number = false; /** * @var bool */ protected $id = false; /** * @var bool */ protected $updated = false; /** * Constructor * * @param type $id_base * @param type $name * @param type $widget_options * @param type $control_options */ public function __construct( $id_base, $name, $widget_options = array(), $control_options = array() ) { } /** * {@inheritdoc} */ final public function register() { } /** * Generate the actual widget content.
*/ final public function display_callback($args, $widget_args = 1) { } /** * Generate the control form.
*/ final public function form_callback($widget_args = 1) { } /** * Deal with changed settings.
* * @param mixed $deprecated Not used.
*/ final public function update_callback($deprecated = 1) { } /** * Echo the settings update form.
* * @param array $instance Current settings */ protected function form($instance) { } /** * Constructs name attributes for use in form() fields.
* * @param string $field_name * * @return string */ protected function get_field_name($field_name) { } /** * Constructs id attributes for use in form() fields.
* * @param string $field_name * * @return string */ protected function get_field_id($field_name) { } /** * Update a widget instance.
* * @param array $new_instance * @param array $old_instance * * @return array */ protected function update($new_instance, $old_instance) { } } 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 Abstract_WP_Widget implements WP_Widget_Interface      * @var array $widget_options      * @var string $id_base      * @var string      * @var array $control_options      * @var bool $number false      * @var bool false      * @var bool $updated false        * Constructor      * @param type $id_base      * @param type $name      * @param type $widget_options      * @param type $control_options $id_base $widget_options $control_options        * {@inheritdoc} final        * Generate the actual widget content.
final display_callback $args $widget_args 1        * Generate the control form.
final form_callback $widget_args 1        * Deal with changed settings.
* @param mixed $deprecated Not used.
final update_callback $deprecated 1        * Echo the settings update form.
* @param array $instance Current settings $instance        * Constructs name attributes for use in form() fields.
* @param string $field_name      * get_field_name $field_name        * Constructs id attributes for use in form() fields.
* @param string $field_name      * get_field_id $field_name        * Update a widget instance.
* @param array $new_instance      * @param array $old_instance update $new_instance $old_instance Changing the validation in WordPress.
We have a proper interface for widgets.
We can make changes to the_widget function.
Instead of validating based on the WP_Widget class, we will use the interface.
Function the_widget($widget, $instance = array(), $args = array()) { global $wp_widget_factory; $widget_obj = $wp_widget_factory->widgets[$widget]; if ( !is_a($widget_obj, .

”WP_Widget_Interface”) ) return; //

$widget_obj->widget($args, $instance); } 1 2 3 4 5 6 7 8 9 10 11 the_widget $widget $instance $args global $wp_widget_factory   $widget_obj $wp_widget_factory widgets $widget.
Is_a $widget_obj ”WP_Widget_Interface” $widget_obj widget $args $instance Meanwhile, .

WP_Widget_Factory should also validate an object to see if it’s a WP_Widget

It doesn’t.
That’s a bug I submitted to Trac.
Here is a possible way we could handle it.
class WP_Widget_Factory { var $widgets = array(); function register($widget_class) { $widget_obj = new $widget_class(); if ( !is_a($widget_obj, ”WP_Widget_Interface”) ) return; $this->widgets[$widget_class] = $widget_obj; } //.
} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 WP_Widget_Factory var $widgets   $widget_class $widget_obj $widget_class.
is_a $widget_obj ”WP_Widget_Interface”   widgets $widget_class $widget_obj   It comes down to practice.
Inheritance is one of the most powerful features of object-oriented programming.
The amount of time you save with the code reuse is incredible.
That said, it can also be the hardest thing to grasp for newcomers.
Being able to see the relationship between your classes takes time and practice.
So keep at it.
Tweet.
#Abstract class #Inheritance #Interface #Object-oriented Programming #PHP #Widget API # The first thing you should learn from object-oriented programming Polymorphism and WordPress: Abstract classes.


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.


Step 3: Creation of mock-up and presentation

Data Driven Websites.
Data Driven Websites.
Get The Most From Your Online Solution The Website Conundrum.
The decision to create a new website for your company or organization is usually an easy one.
Whether you currently do not have a web presence or the one you currently have seems outdated the need for a new site is usually high up on the “to-do” list.
However, choosing the right web-design company is a lot more difficult.
Will the design be to my liking after my initial deposit.
Will the technology they use to build the site meet modern standards.
Will I have to start over after a hefty investment.

How Are Our Websites Different?

We try to make the decision a little easier.
From the initial meeting to product delivery our process is designed for transparency and ease of use.

Built around a Content Management System

You can update your site easily through a back-end management system.  We specialize in WordPress development but analyze your needs before making a recommendation.
We ask specific questions regarding site design and special functionality.
You can see your site’s design progress on a testing server.
Complete set-up and training upon site completion.
Our Design Process.

Step 1: Initial Needs Analysis Meeting to determine your goals and design preferences

Step 2: A detailed proposal is presented explaining all features and functions that your new site will have along with a detailed explanation of the investment required.

After acceptance of the proposal we will proceed to Step 3

Step 3: Creation of mock-up and presentation.
At this point we can accept the design, tweak it or move in a different direction.
Step 4: Site is designed to your specs and desired functionality is added to the Content Management System.
Progress can be seen on a dedicated testing server.
Step 5: The first product presentation allows for the site to be unveiled and functions to be explained.
The customer can make requests for changes.
Step 6: Requests for changes are implemented and all feature requests from the proposal are fulfilled.
Contact Us.

267 John Knox Road Suite 113 Tallahassee

FL 32303 (850) 270-7789 Request a Proposal.
We understand you may have a lot of questions before starting your project.  You may not even know the right questions to ask.  No worries, we know exactly what to ask to make sure we know what you are trying to achieve in your project.
Contact Us.
Data Driven Websites.


← Back to Barcade® | Jersey City

Powered by WordPress.
Username or Email Address Password Remember Me Lost your password.
← Back to Barcade® | Jersey City, New Jersey Privacy Policy.


← Back to Barcade® | Jersey City

Powered by WordPress.
Username or Email Address Password Remember Me Lost your password.
← Back to Barcade® | Jersey City, New Jersey Privacy Policy.