Reasons PHP sucks #753

Another good example of a PHP “quirk” is the way PHP handles constants. It was one of the major factors affecting performance. Just removing all the constants allowed us to improve the performance by almost 2x (we left one constant to be precise).

From The Need for Speed.

That’s right - PHP is up to 2X faster if you don’t use constants. You know, that means hardcode values in…

Life as a Coder

I never thought I’d manage to find a job coding in python for a living. But that is what my full-time job is, right now, anyway.

I can’t really talk about the work I’m doing, since it is a commercial enterprise, but I can talk about what sort of things I am coding. I have spent the last couple of weeks rebuilding a server in python that uses SOAP to communicate with the outside world (well, a client application, anyway) over SSL connections. Python is used so it is easily extensible, without having to recompile. Eventually it will be dynamic, where new modules can be added to a database, and depending on the userid of a request, a different function will be called. It’s really quite exciting.

I’ve come across a couple of new software programs - one of which is NX (nomachine) Client, which is a remote tunnel for X windowing. I can remote in via this to work from home, as well as ssh or sftp. Which is fairly cool. Speedier than VNC, since I think the local X-Windowing system is responsible for some of the drawing tasks. Feels about the same over ADSL as ARD does over WLAN.

In my “free” time I’ve been doing a couple of other things, both programming tasks. The first is a web application for an art gallery to create HTML and PDF invitations and newsletters. Originally I planned to use a web app so that it could eventually be rolled out as a blog-alike - in fact originally it was just going to be a WordPress installation with some minor modifications. It turned out to be easier to rewrite it from scratch. I have learned from this process that PHP is crap: it’s never clear about the way to do stuff, and many functions have weird names. count_chars, for instance, doesn’t really count the characters, unless you decide that things like #@! aren’t characters. In which case you want strlen. Which had me tricked for some time, since I stopped looking once I had found count_chars. Python and len(anything) is much better.

Speaking of python (again), I’ve also been working on a Regular Expression helper - similar to the one that comes with Komodo IDE. I started (and pretty much finished, in a matter of hours, to the extent it solved my first use problem) this after having to load up Komodo just to get a visual representation of which bits of a text block were being matched by a regex. Still some kinks to work out - I need to figure out how to put stuff into an outline view, so I can see more than just matches, but match groups. Then it will be all good.

In the process of my work job, I downloaded SOAP Client, a freeware tool for testing SOAP packets. It was all good until I tried sending HTTPS requests, which it fails unfathomably on (cannot connect to endpoint…). I emailed the author, and he then promptly released the source code. I’ve snaffled that from Google Code, and I’ll try to hack through it a bit to implement SSL connections. Not sure how to go about it at this stage - dunno if it is with WebKit or something else I need to do. I also plan to add in the ability to edit the SOAP request manually before it is sent off to the server.

I start Uni in a couple of weeks - I’m doing an introductory Java course in intensive mode, which I expect to be fairly easy. I’m really only doing it so I can do the meatier sounding subjects, like Programming Language Concepts and Systems Programming. I really think I’m going to enjoy this course. I will be interested when I come to the Internet Computing subject, since I’ve been doing a fair bit of that in various forms over the past few years. Be interesting to see what the academics think it means.

Well, that’s been my life over the time period since coming back from the beach for a 10 day holiday over New Year. Apart from squeezing in a few games of Touch Football here and there, I’ve pretty much been chained to my laptop.

And loving it.

Human Time Plugin at 9am.

Doesn’t seem to be working properly.

Move to NearlyFreeSpeech nearly complete.

Well, I’ve moved over from Blogsome to NearlyFreeSpeech.net It’s very cool, and looks to be pretty cheap. I might have a high bandwidth bill for the last two days as I had to keep uploading SQL dump files (much faster than editing using nano over ssh!). When I imported all of my comments, the posts weren’t updated with the number of comments they each had, so I needed to run the following PHP script:

 1     <?php  
 2     require_once('admin.php');  
 3     
 4     echo "Approving comments...";  
 5     
 6     // Approve all comments  
 7     
 8     $wpdb->query("UPDATE $wpdb->comments SET comment_approved = '1'");  
 9     
10     echo "Updating post counts...";  
11     
12     // Populate comment_count field of posts table  
13     
14     $comments = $wpdb->get_results( "SELECT comment_post_ID, COUNT(*) as c FROM $wpdb->comments WHERE comment_approved = '1' GROUP BY comment_post_ID" );  
15     
16     if( is_array( $comments ) ) {  
17     
18     foreach ($comments as $comment) {  
19     
20     $wpdb->query( "UPDATE $wpdb->posts SET comment_count = $comment->c WHERE ID = '$comment->comment_post_ID'" );  
21     
22     }  
23     
24     }  
25     
26     echo "Done.";  
27     
28     ?>

Blogsome Post Parser

A bit down the track, I want to fix some things with the way Posts are parsed when they are created or edited using Blogsome. I’m loath to change too much the stuff in the actual edit box: people need to be able to put Raw HTML code in there if they want, and if they don’t, they can use the WYSI-editor. However, the Title field needs to be escaped before being saved to the database. Having an & in the title generates invalid XHTML code. This is basically the same issue as what causes the errors when extended (non-latin) characters are used in the title, and transferred into the post-slug. This should be fairly easy to fix: just a function that looks at this field on posting (and perhaps the post-slug field also, just to check), and replaces any invalid characters with the escaped version. I only thought about this again, as I had two posts with & in their titles in the past week.

Displaying Comments awaiting Moderation

It’s a neato feature to be able to show a reader the comment they have just posted, but which is awaiting moderation before it it displayed to everyone else. Newer versions of Wordpress can do this, and many templates also have this feature built in, but it does not work on Blogsome. The code that grabs the Comment data is found, in Blogsome, in wp-inst/wp-comments.php:

1         $query = "SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND comment_approved = '1' ORDER BY comment_date";
2         $comments = $wpdb->get_results( $query );

This code was fairly easy to find, but the equivalent code in Wordpress was a bit tricker. Eventually, I found it in wp-includes/comment-functions.php:

 1         if ( is_single() || is_page() || $withcomments ) :
 2             $req = get_settings('require_name_email');
 3             $comment_author = isset($_COOKIE['comment_author_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_'.COOKIEHASH])) : '';
 4             $comment_author_email = isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_email_'.COOKIEHASH])) : '';
 5             $comment_author_url = isset($_COOKIE['comment_author_url_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_url_'.COOKIEHASH])) : '';
 6         if ( empty($comment_author) ) {
 7             $comments = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND comment_approved = '1' ORDER BY comment_date");
 8         } else {
 9             $author_db = addslashes($comment_author);
10             $email_db  = addslashes($comment_author_email);
11             $comments = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND ( comment_approved = '1' OR ( comment_author = '$author_db' AND comment_author_email = '$email_db' AND comment_approved = '0' ) ) ORDER BY comment_date");
12         }

I had to modify the code somewhat, but here is the code I have put on the private Blogsome test server:

 1         $comment_author = isset($_COOKIE['comment_author_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_'.COOKIEHASH])) : '';
 2         $comment_author_email = isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_email_'.COOKIEHASH])) : '';
 3         $author_db = addslashes($comment_author);
 4         $email_db  = addslashes($comment_author_email);
 5         if ( empty($comment_author) ) {
 6             $query = "SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND comment_approved = '1' ORDER BY comment_date";
 7             $comments = $wpdb->get_results( $query );
 8         } else {
 9             $comments = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND ( comment_approved = '1' OR ( comment_author = '$author_db' AND comment_author_email = '$email_db' AND comment_approved = '0' ) ) ORDER BY comment_date");
10             $comments = $wpdb->get_results( $query );
11         }

It could probably be tweaked (for instance, moving the addslashes() calls to within the else block would save a couple of CPU cycles when no author is set), but it works!. You need to have the following in your Comments Template:

1     {if $comment->comment_approved == "0"}
2         <p>Your Comment is awaiting Moderation.</p>
3     {/if}

I’m also going to style the comment box a little differently for unmoderated comments.

Gravatar Smarty Modifier

I wrote a Gravatar modifier for Smarty: {$comment->comment_author_email|gravatar:"default":     size:"rating":border} For example, to use no default image, and limit the size to 40x40 pixels, you’d use: {$comment->comment_author_email|gravatar:"":"40":"":""} Notice the double quotes where the argument is missing, and that (in this version) you actually need to include all of the arguments. (I’ve updated this, but haven’t tested it fully yet). Anyway, here’s the code:

 1     <?php
 2     /**
 3      * Smarty plugin
 4      * @package Smarty
 5      * @subpackage plugins
 6      */
 7     
 8     
 9     /**
10      * Smarty gravatar plugin
11      *
12      * Type:     modifier<br>
13      * Name:     gravatar<br>
14      * Author:   Matt Schinckel<br>
15      *           mailto:matt@schinckel.net<br>
16      *           aim:mschinckel<br>
17      *           http://schinckel.net<br>
18      * Purpose:  convert email address to gravatar
19      * @param string
20      * @return string
21      */
22     function smarty_modifier_gravatar($email, $default=false, $size=false, $rating=false, $border=false)
23     {
24         $gravurl = "<img src='http://www.gravatar.com/avatar.php?gravatar_id=".md5($email);
25         if ($default)
26         {
27             $gravurl = $gravurl."&amp;default=".urlencode($default);
28         }
29         if ($size)
30         {
31             $gravurl = $gravurl."&amp;size=".$size;
32         }
33         if ($rating)
34         {
35             $gravurl = $gravurl."&amp;rating=".$rating;
36         }
37         if ($border)
38         {
39             $gravurl = $gravurl."&amp;border=".$border;
40         }
41         return $gravurl."' alt='Gravatar Image' />";
42     }
43     
44     ?>

{todayayearago}

Another Blogsome user was wanting to use {todayayearago} to get posts from a week ago, which reminded me I do it with a month ago. Looking through the source to see if a weekly one exists (it doesn’t, as yet), I came across this little nugget:

1     function todayayearago( $when, $wpblog, $spacer ='<br /' )

Now, ignoring the apparent typo (I think there should in fact be a closing tag to that there BR tag…), what do you see that is notable? That’s right, you can choose a different blog. So I tried this with my test blog:

 1     {todayayearago when='month' wpblog='schinckel'}
 2     {if $todayayearago != ''}
 3         <h2>A Month Ago</h2>
 4             <ul>
 5                 {foreach from=$todayayearago key=id item=details}
 6                     <li>
 7                         <a href="{get_permalink id=$id}" title="{$details.content|truncate:25:"..."}">
 8                             {$details.title|truncate:20:"..."}
 9                         </a>
10                     </li>
11                 {/foreach}
12             </ul>
13     {/if}

Now, this gives some very interesting results. Firstly, it works, except for the part that tries to get the permalink:

href="{get_permalink id=$id}"

This fails, because it (naturally) tries to grab the permalink from the current blog, not the other one. My next step was to see if {get_permalink} can handle an argument like wp_blog. The sad news is it can’t. So, the next question is: does the URL get returned in the data structure along with the content and title. I suspect so, but need to see how. Looking through the source of wp-db.php doesn’t throw much light on the topic, so it’s time for trial and error. That’s what a test blog is for, after all! Aha! You can see most of what happens from the todayayearago file itself:

1     foreach( $reqhistory as $row )
2     {
3      $todayayearago[ $row->ID ] = array( "title" => strip_tags( stripslashes($row->post_title) ),
4       "content" => strip_tags( stripslashes( $row->post_content ) ) );
5     }

Which clearly shows that only the title and content are ‘grabbed’. So, until I rewrite this function (so it includes the URL, and perhaps even so it allows for other intervals), it’s use is still limited to posts within your own blog, from either a year or a month ago. Unless I can find a way to get post data from another blog’s database…

XMLRPC Escaped Quotes

For some time, since the XMLRPC file was updated, posting to Blogsome via the XMLRPC interface has been broken. It works, but extraneous slashes are inserted before quotes and apostrophes. I’ve been lucky enough to get my dirty little mitts on the source code, on a test server, and have come up with what may be a fix. The good folks at Blogsome (I think) replaced the dodgy, broken version of XMLRPC.php with one that prevented some problems with a security hole. However, this version of the file came from a more recent WordPress installation. The version of WP-µ that Blogsome uses may not have actually been vulnerable to the problem that the XMLRPC file could have caused, because of this code:

1         // Do some escapes for safety
2         $post_title = $wpdb->escape($post_title);
3         $post_name = sanitize_title($post_title);
4         $post_excerpt = $wpdb->escape($post_excerpt);
5         $post_content = $wpdb->escape($post_content);
6         $post_author = (int) $post_author;

However, the new XMLRPC file also escapes everything. So everything gets escaped twice, causing the quotes to be double-escaped. So, I replaced the above code with the code from the new version:

1         // Get the basics.
2         $post_content    = apply_filters('content_save_pre',   $post_content);
3         $post_excerpt    = apply_filters('excerpt_save_pre',   $post_excerpt);
4         $post_title      = apply_filters('title_save_pre',     $post_title);
5         $post_category   = apply_filters('category_save_pre',  $post_category);
6         $post_status     = apply_filters('status_save_pre',    $post_status);
7         $post_name       = apply_filters('name_save_pre',      $post_name);
8         $comment_status  = apply_filters('comment_status_pre', $comment_status);
9         $ping_status     = apply_filters('ping_status_pre',    $ping_status);

My test blog (which isn’t available to the public, as it’s on another server) seems to be coping well with this, I think I’ll publish a heap of entries to it and see how it holds up. I’m hoping that Ronan will be able to have a look over these changes, and hopefully we’ll see the Quote Escape bug gone, for good, very soon! Update: Apparently I didn’t do enough checking. The filters that are called don’t actually exist, so no escaping is done. That will teach me for going off half cocked. I will try to implement the new filters used by this version of the code, but we’ll see.

Arguments that don't work

I’ve come across several Smarty Tags that call WordPress functions that have listed arguments that don’t work. I didn’t bother too much about it - usually I can live with it. Then, I realised how I solved one instance of this happening: the Recent Comments plugin.

I downloaded the source to this, and looked at it. Besides there being some spelling mistakes, it had argument names which differed from the documented ones. No wonder it was failing when I tried to change the number of comments, and the comment length! So, looking at the code helped me solve the problem. Maybe it will with the other issues I have come across: most notably {previous_post_link} and {next_post_link}, and {edit_post_link}.

I’ll start with the {edit_post_link}. The codex says the argument I want to change is called text, but the source reveals it to be link. Changing this enabled me to change the text that is presented to whatever I want. The same goes for {edit_comment_link}.

I was originally using the tags {previous_post} and {next_post}, but was unable to change the previous post: next post: text that appeared. Changing the name of the argument from text to previous and next (as applicable) fixed this. Similarly, since {previous_post} and her sister are deprecated, {previous_post_link} has arguments that differ from those detailed in the Codex. The arguments it (and {next_post_link}) can handle are:
  • format - defaults to &laquo; %link. You need to have the %link where you want the URL to appear! You can add more stuff that you want to appear but not be part of the link itself.
  • link - defaults to %title. Add other text that you want to add into the link area.
  • in_same_cat
  • excluded_categories

Depending upon if you want an image clickable, you can either insert the code into the format or link argument. Just for fun, I included my favicon in the non-link section of the link text (ie, the format argument). I’ll leave it there for a while, just to prove it can be done…in the meantime, I’m going to fix the Codex.