I’ve just replied to the first comment on my blog, when I started to wonder: And does he gets now an email, which informs him about the reply. Well, I didn’t know, and so I started to dig a bit into the core. Sure, I could just test it by writing a comment and comment on this comment. But what fun would that be? And journeys into the core are always more than just to dig one answer. So follow me on my trip and find out what happens, when a user writes a comment.
Snæfellsjökull: The journey begins
If you have ever developed your own WordPress Theme, you know, how easy you are able to implement the WordPress Comment System. I started to wonder, what does happen, when a reader actually sends a comment. So I went into the core.
To integrate the Comment Form, you usually just use
So we start to digg for it. I always use Nodepad++ and search all files in wp-includes for a term. Lazy as I am, I just type in ‘comment_form’ and suddenly end up in ‘comments-popup.php’. Its dark and cold in here and a voice in the off is whispering:
This file is here for Backwards compatibility with old themes and will be removed in a future version
Back again and more focussed, we end up in comment-template.php Line 1960. Here, we find the comment_form, which returns the complete form, you need, to enable users to send their comments. As we can see in Line 2046 a comment will be send to wp-comments-post.php
The first lines check, wether there was used the right Request-Method, which is POST. So, if you create your theme and for some reasons, you don’t want to use
make sure, you do send the form via POST, which is the proper way to do anyway.
The first value, that is checked, is the comment_post_ID. This is the ID of the Post, you reply to. Now, the System grabs this Post, and first checks, whether there is a valid comment_status for this Post. If not, the script terminates after firing the Action Hook ‘comment_id_not_found’.
The lines 40 to 84 handle now all the exceptions.
- Comments Closed
- When the comments are closed for this Post, the Action Hook ‘comment_closed’ is fired and the script terminates
- Post is in Trash
- Fires the Action Hook ‘comment_on_trash’ and terminates script
- Post is a Draft
- Fires the Action Hook ‘comment_on_draft’ and terminates
- Post is protected by Password
- Fires ‘comment_on_password_protected’-Hook and terminates.
If everything works fine, it goes on after firing ‘pre_comment_on_post’. Now, WordPress grabs the information send by the Form:
If the user is not loggedin, the System checks now, whether this post is private or you need to be loggedin in order to post a comment. If so, the script terminates.
But, we are back on track. Lets say everything works fine. Now, the information, which has been send by the form needs some validation. Is an Email given and do we need an email? Do we need a name, and no name is given. Does the Comment itself contains text? If not, the script terminates and tells the visitor to provide sufficient information.
But, lets say, everything is correct. Now, its time to check, whether this comment is a reply to another comment. And after this all these information get stored in $commentdata with compact() and send over to the function wp_new_comment()
Onto the coastline: wp_new_comment()
So, we passed all the validated data to wp_new_comment(), which first of all fires the ‘preprocess_comment’-filter, through which all the commentdata will be processed. So, for example, Akismet uses this filter in order to process this data. Akismet sends this data with some other $_SERVER-Information to akismet.com, where it will be checked whether this comment is spam or not.
But, after some Plugin daylight, back into the cave. wp_new_comment() now validates again some data, so for example it ensures that the comment_post_ID is really an Integer.
$commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID'];
Interesting is to see the user_ID-Section in Line 1697 – 1700. Whether the User-Id gets transmitted via $commentdata[‘user_ID’] or $commentdata[‘user_id’], it will be checked and from now on, it will be called ‘user_id’. One might wonder, how come. The history for this piece:
- 4 years ago sirzooro posted a bug “PHP Notice while untrashing comment”
- Ryan provided a first fix, by changing all user_IDs into user_ids.
- But, this lead to another bug, as westi reported:
This broke setup_userdata()
- So, since Changeset 12300 user_id as well as user_ID are accepted
Well, after this small piece of history for an awkward piece of code, we want to return on our track and stop digging into caves, we didn’t wanted to explore in the first place. Maybe one last comment. 14 month ago, westi suggested to do this user_id/user_ID-normalisation before the “preprocess_comment-filter”. I would approve this. Well, I have to digg into how to interact with the WordPress Core Development and how to actually contribute with changes in the trunk. Another day, another place.
WordPress now checks on the comment_parent. Does this parent exist and is it approved or pending. If so, the parentID remains. If not, the ID is set to 0.
WordPress continues to enrich the data of the comment by the IP and the User-Agent of the User and the Date.
But still, our first question: Does the author of the comment, I’ve replied to got an email? We have to go on. So, what happens now with our data? Three new functions appear at the horizon:
This is quite a nice function. Every piece of information runs now through an explicitly defined filter. The following filters are applied:
- pre_user_id (Where we, by the way, find again a user_id-normalisation)
After all these filters are applied, a new value is added:
$commentdata['filtered'] = true;
and the data gets returned.
I am especially happy about ‘pre_comment_user_ip’. When we came to comment.php Line 1706
$commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '',$_SERVER['REMOTE_ADDR'] );
I was a bit concerned for privacy reasons. I already wrote a small comment, that it would be nice, to have a filter, which enables you to get rid of the IP. Well – thats WordPress – this filter exists! Here is, what I wrote, because this issue can even end up in legal matters:
Especially, when it comes to the IP, one might wonder, whether it would be possible to add a filter-hook here. Maybe some bloggers do not want to save the IP of commentators for privacy reasons. As far as I know, there might be even legal problems concerning the saving of IP addresses, for example in Germany. It would be nice to have the possibility to disable this function.
For all those of you, who want to get rid of this IP-saving method, check out Remove IP by guido. It exactly hooks into the ‘pre_comment_user_ip’-Filter and sets the IP to “127.0.0.1” (localhost). (See here)
This function is defined in Line 756. It first runs a check, whether this content is already saved in the database. If it is a double entry, the action ‘comment_duplicate_trigger’ is fired and the script terminates with the output:
‘Duplicate comment detected; it looks as though you’ve already said that!’
We come close now to the point, where the comment indeed will be saved. Close to the Center of the Earth! We bypass the Filter ‘check_comment_flood’, which allows checking for comment flooding. We check, whether the comment was send by the post author. If so, it gets approved immidiatly. If not, some tests are run, whether the comment needs moderation or not. See for more information: check_comment().
wp_allow_comment() returns as far as I can see it three possible values: 0, 1 and “spam”. This information is saved here:
$commentdata['comment_approved'] = wp_allow_comment($commentdata);
And now, we come to wp_insert_comment(), which reminds one the famous wp_insert_post()!
We are here. Where the heart of the comment system beats! After some checks on the data, the comment will be finally saved in Line 1564! We receive an ID. If the Comment is already approved, the Counter will be set +1 (Line 1569), well okay, after running through two different functions and acutally, in the end its not a $count++ but an SQL Statement, but after all, the counter is set +1.
At last, we get the whole comment-Object (Line 1571), execute the action ‘wp_insert_comment’ (Line 1581) and set the ‘last_changed’-value with wp_cache_set (Line 1583). What will be returned is the ID.
But still, we don’t know yet: Did he got a mail? So we return into Line 1716, where we now have at least the ID of the new comment. After the ‘comment_post’-Action, two functions are quite interesting:
wp_notify_moderator() and wp_notify_postauthor()
The result of the first function, we know it all quite too well. It will send the Email to the Administrator, that an Comment is waiting for approval.
The second one will send an Email to the postauthor, if the specific option ‘comments_notify’ is set. Actually, the function itself contains a lot of possibilities to add other Email addresses as well. But anyway, by default this Notification is disabled, and my blog runs quite the basic values.
And suddenly, I get it. Of course not! There are some extensions, which say, notify me, when someone replies. But I haven’t such a function. So we return with our new Comment ID, which we received in wp_new_comment() and find ourselfs again in wp-comments-post.php
More or less, after some filters, we get redirected to our Post, where we started our journey. And we know: He doesn’t know at all.