Normally, I’ve been trying to release a new blog entry every Tuesday. I took this last week off to celebrate my birthday. Tomorrow should restart the grind.
My Blog
contains PHP and other web related content. (Sometimes there are some off topic things - don't freak out!)
Time off for B-day is done
June 28th, 2010Restrict your .git directory on live site
June 15th, 2010Do you use Git to manage your repository? If so, do you use it to check out code onto the server as well? If you do, you really should restrict access to your .git directory if it’s in your public root. (If you’re using things like Zend Framework, chances are your root directory is not your public directory, so you have less to worry about.)
Simply, add the following lines to your apache config:
1 2 3 | <Directory /full/path/to/public/.git> Deny from All </Directory> |
This will simply rewrite the request to your home page. No more accessing things like your ‘config’ file that could potentially hold useful information about your Git repo configuration. (You may remember this topic being discussed with SVN here.
Twitter @anywhere platform: web414 presentation
June 13th, 2010When uniqid is too slow in PHP
June 8th, 2010I just profiled some of my code and found out that the biggest chunk of my processing time was used by uniqid(). I use this to generate form tokens to prevent cross site request forgeries. On one page, I have 6 forms each with its own unique uniqid().
The first thought is to just use one ID per page. However, I didn’t want to change too much of my code. It’s working – so lets just make it work faster. I did some thinking and realized that maybe my unique id didn’t need to be THAT unique – just not super predictable. A sha1() hash of a random number should do the trick. And it should be faster. Just to verify, I did my own benchmark using this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | $start = $stop = array(); $start['uniqid'] = microtime(TRUE); for ($x = 0; $x< 1000; $x++) { $val = uniqid(); } $stop['uniqid'] = microtime(TRUE); $start['mt_rand'] = microtime(TRUE); for ($x=0; $x<1000; $x++) { $val = mt_rand(0, 1000000); } $stop['mt_rand'] = microtime(TRUE); $start['sha1/mt_rand'] = microtime(TRUE); for ($x=0; $x<1000; $x++) { $val = sha1(mt_rand(0, 1000000)); } $stop['sha1/mt_rand'] = microtime(TRUE); foreach ($start as $key=>$startval) { echo "{$key}: " . ($stop[$key] - $startval) . "<br />"; } |
The results:
uniqid: 1.1227629184723 mt_rand: 0.0030300617218018 sha1/mt_rand: 0.0076968669891357
As you can see, sha1/mt_rand combination is so much faster. In fact, 140x! While this is still micro-optimization, running that 6 times to me makes a difference.
Your thoughts? Is this still unique enough for form tokens?
Using ApacheTop with Cronolog
June 1st, 2010I love ApacheTop. I love Cronolog. After I installed cronolog and used it in my apache configuration, however, I found it more and more difficult to use apachetop. I stopped using it. Well, I finally came up with a bash script that eases my frustration with calling the proper path names for apachetop. Check it out:
My httpd.conf customlog
The entry in httpd.conf vhost contains this line:
1 | CustomLog "|/usr/local/sbin/cronolog /etc/httpd/logs/%Y/%m/%Y-%m-%d-access_log" combined |
Pretty simple – just keeping my logs separated.
My webtop.sh script
In order to make it easier on myself, I now envoke apachetop with this bash script.
1 2 | #!/bin/bash apachetop -T 3600 -f "/etc/httpd/logs/`date +%Y`/`date +%m`/`date +%Y`-`date +%m`-`date +%d`-access_log" |
And then I’m good to go.
How to Log PHP Errors like a Pro
May 25th, 2010The error log can be fun to parse through and figure out what happened. Ok, so if you just read that and agreed, you need to move on. This is not for you. That’s not fun. However, you CAN make error gathering easier on yourself by including the right information in the error log. To top it off, you can present your users with something that is a bit more ‘friendly’ than the standard error display or blank page. Let’s check it out.
Create an Error Handling Class
All of my error handling is going to be pretty much uniform. In order to do this, I want to share a lot of code. I’m going to do this by creating a class and having my error handling methods a part of it. I plan on gathering data from both standard PHP errors and uncaught PHP Exceptions.
First, PHP Errors
The first thing I want to do is grab my PHP errors. I’ll make the following code:
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 | class errorhandlers { public static function error_handler($errno, $errstr, $errfile, $errline, $errcontext) { $string = "ErrorNo: {$errno}:: {$errstr} || {$errfile} on {$errline} || "; ob_start(); var_dump($errcontext); debug_print_backtrace(); $string .= ob_get_clean(); if (ENVIRONMENT == 'DEV') { print $string; } else { error_log($string); } switch ($errno) { case E_NOTICE: case E_USER_NOTICE: //do nothing break; default: self::beFriendly(); break; } } protected static function beFriendly() { /** * kind of hacky */ if (ENVIRONMENT != 'DEV') { die(header("Location: /error")); } } } |
The first thing that is done is to grab all of the error context that PHP sends to the error handler. That is what the 5 parameters are for. I begin by making a string with this information in it. The last parameter is actually an array, so I use var_dump(). Before that, however, the output buffering is initiated. Then, the context is var_dump()’d. Finally, to get a little bit more context, the command debug_print_backtrace() is used. The contents of this output buffer is then added to the string.
The reason I am using ob_start() and not using the parameters / functions to return the data is because of a recursion issue that can happen with var_export()/var_dump() in certain contexts. I’m not sure what happened, but it was an infinite type of recursion – so I chose this method.
The final two steps of that method are pretty straight forward. If we are working in our development environment, print the error information to the screen. Otherwise, log it to the standard error log. (This is better than using ini_set() with display errors because of the extra context I’m adding with the debug_print_backtrace()). Finally, if not a NOTICE type error, call the beFriendly() static protected method.
The beFriendly() method simply redirects a user to a friendlier “ruh roh” type page if we’re not in the development environment.
Do something with uncaught exceptions
To handle exceptions, the following method is added to the class:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static function exception_handler($exception) { $string = str_replace("\n", ' ', var_export($exception, TRUE)); if (ENVIRONMENT == 'DEV') { print $string; } else { error_log($string); } self::beFriendly(); } |
This is much more simple. The exception details are exported to a string. The new lines are removed because they play havoc with the error log grep’ing. Then, as with the previous method, it is either displayed or logged and then the user is redirected possibly.
Register the Error Handlers
The last thing to do is to register each of these error handlers. That is done with this simple code:
1 2 | set_error_handler(array('errorhandlers', 'error_handler')); set_exception_handler(array('errorhandlers', 'exception_handler')); |
And then, you’re ready to go. Happy error logging!
Disable jQuery from loading in custom wordpress template
May 18th, 2010On the rest of my site, I load jQuery from the google cdn. However, wordpress likes to load it from the local cache using wp_enqueue_script(). I didn’t want to delete the jQuery file it was loading because a) that would be wrong, b) it would still have to make a 404 call to the server, and c) the admin section uses it I’m sure.
Instead, I found there was a function called wp_deregister_script(). Using this, I removed the jquery from my header.php file – and continued to load it from the cdn. For reference, this is what I did:
1 | <?php wp_deregister_script('jquery');wp_head(); ?> |
Twitter @anywhere proof of concepts
May 13th, 2010First off, let me just remind you to not be an idiot like I was. I simply found the documentation here and here and went to town. After hours of trying to figure out exactly what was going on, I stumbled across some very interesting comments in the news group: The @anywhere api is not in chirp_preview anymore – but its not done either. So some of the stuff won’t work – and that wasn’t my fault! Dang! However, I was able to create a few proof of concept things. I’m going to cover connecting to your application (which you should be familiar with anyway), showing the connection/authentication system, and retrieving information about other users via your authenticated account.
First, set up the HTML
The following is the snippet of HTML I used for my demonstration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <html> <head> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script src="http://platform.twitter.com/anywhere.js?id=YOUR_API_KEY&v=1" type="text/javascript"></script> </head> <body> <p>This should give some pretty cool features of the Twitter AT-anywhere API.</p> <hr /> <div id="connect"></div> <div id="disconnect"></div> <div id="connectedUser"></div> <div id="connectedFunctions"> <p>The following stuff can only be done if you're connected.</p> <label>Search this user: <input id="searchUser" /></label><button id="doSearch">Search this user</button> </div> </body> </html> |
This is pretty simple, create a document, load jquery and load the twitter api using your api key, and then create some placeholder boxes. Let’s check out the first set of javascript.
The Javascript for Authentication and User Information
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | twttr.anywhere.config({ assetHost: "twitter-anywhere.s3.amazonaws.com" }); twttr.anywhere(function(T) { T("#connect").connectButton({ authComplete: function() { $("#connect").hide(); $("#disconnect").html('<a href="#" id="signoutlink"">Sign Out</a>'); showUserInfo(T); }, signOut: function() { $("#connect").show(); } }); if (T.isConnected()) { $("#connect").hide(); $("#disconnect").html('<a href="#" id="signoutlink">Sign Out</a>'); showUserInfo(T); } }); |
To access the twitter @anywhere functionality, there is a global object called twttr. Then, call the anywhere method and passing in a variable – in this case T. T refers to the current instance of the @anywhere object.
The first block is finding the div with the id of ‘connect’. This will make this a connect button. The connect button has two actions bound to it. The first is authComplete() which will be called once the user authorizes this application. The second is signOut() which is executed if someone clicks the button after they’ve connected. In my example, I’m going to not use this functionality. I did leave it in there, however, for demonstration.
The authComplete() method then hides the connect button. We’re already connected so we don’t need to show this box. It’s important to know that this box actually transforms into a ‘you are connected’ icon – and allows for disconnect. In my example, I thought this might be confusing for the user, so I remove the box entirely. Instead, I populate the disconnect box with a signout link. This has an ID that we’ll reference later. Finally, the showUserInfo() function is called. It receives a parameter of the current instance.
All that functionality only happens when the user uses the connect button. The page can still be refreshed or visited at a different time. This is where the next snippet comes in. When this call continues, it verifies if the user is connected with T.isConnected(). If so, it basically does the same stuff as the authComplete() method of the connectButton() call. (Yah, I should have put this in a different method..)
Next, lets look at the other code bits that we reference in this snippet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | $("#signoutlink").live("click", function () { twitterSignout(); }); function twitterSignout() { twttr.anywhere.signOut(); $("#disconnect").hide(); $("#connect").show(); $("#connectedUser").html(''); } function showUserInfo(T) { currentUser = T.currentUser; screenName = currentUser.data('screen_name'); profileImage = currentUser.data('profile_image_url'); profileImageTag = "<img src='" + profileImage + "'/>"; $('#connectedUser').append("Logged in as " + profileImageTag + " @" + screenName); T.hovercards(); } |
First, the link with the id of ‘signoutlink’ is now bound with jquery to the method twitterSignout(). Whenever the previous bit of javascript creates this link, and it gets clicked, the twitterSignout() method will be called. twitterSignout() simply calls the @anywhere signOut() method, hides the disconnect link, shows the connect button, and clears the information about the previously logged in user.
The showUserInfo() method’s content is a copy from the JS API documentation. All it does is access the currentUser of the T (twitter @anywhere instance) and retrieve that data. It then populates it into the div with the ID ‘connectedUser’. One thing I do extra is call the hovercards() method. hovercards() parses the document and highlights every twitter handle and creates a hover-card or popup. I thought this would be a nice edition for the user information.
The Javascript for Interacting with Other Users
The HTML still has an input box and button I need to add javascript for. For the proof of concept, all we’re going to do is search the user by their username and alert a few bits of information about them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $("#doSearch").click(function(){ var username = $("#searchUser").val(); if (!username) { alert('you must specify a user'); return false; } twttr.anywhere(function(T) { if (!T.isConnected()) { alert('you can only use this if you are connected'); return false; } T.User.find(username, function(user) { alert("user has " + user.attributes.friends_count + " friends"); alert("user is following " + user.attributes.followers_count + " tweeple"); alert("you are following them? " + user.attributes.following); alert(user.attributes.name + " last said: " + user.attributes.status.text); }); }); }); |
Once the button with the id ‘doSearch’ is clicked, the value of the input with the ID of ‘searchUser’ is evaluated. If empty, it tells the user to actually do something – or search someone! duh! Then, the twitter @anywhere instance is executed again. The first step is to make sure that the current visitor really is an authenticated user. If not, tell them they need to be. You’ve seen this code before in the upper snippet.
Next, the User.find() method is executed on the content of that username. The twitter user object is passed into the callback. The call back tells how many friends and followers the searched user has, whether the current user is following the searched user, and what that person’s last status was (it uses their name to start out the sentence.).
Ending Thoughts
I’m looking forward to what Twitter comes up with. So far, I’m super impatient. I was ready to make my full fledged application today – but was stuck by lack of documentation and lack of features. However, I’m sure when it’s done, it will be great. I’ll post something when it’s finished with a more indepth tutorial.
Use your own short domain while waiting for BitLy Pro
May 11th, 2010If you’ve checked out BitLy Pro, you’re probably pretty excited like I am. I saw it and immediately registered saray.me for a short URL. When I went to sign up, I found it was still in a queue system where you had to wait to get an invite. In the mean time, I still want to start using my domain.
After reading some of the documentation, I found that once you sign up for a bit.ly pro account, your existing bit.ly links that were created when you were logged in will be accessible via your short URL. With this in mind, I decided to just continue to create my bit.ly links but redirect my domain to bit.ly for the time being.
Log In to Bit.Ly
Goto bit.ly and login. (or create an account if you want). This is necessary so that you can now create and track your links.
Modify your domain
I’m using GoDaddy. I decided to forward my domain.
First, select your domain in the domain manager. Then, choose the forward domain option at the top of the list.
On the popup, choose the advanced option link. Make sure to choose temporarily forward the domain. This is needed because at some point, we’ll be redirecting this domain’s nameservers to bit.ly again to natively do this URL forwarding. Enter “http://bit.ly” in the box and click ok.
Try it out
Now, goto bit.ly and shorten an URL. I’m shortening http://aaronsaray.com/blog. This is now: http://bit.ly/cZqq0e. When I enter http://saray.me/cZqq0e into the browser, it serves the short URL from bit.ly.
Yay!
Timesaving Feature
Since I do continue to get my bit.ly links with http://bit.ly instead of http://saray.me in the beginning, I decided to make a Firefox bookmarklet to replace this for me. It’s pretty simple. All it does is take bit.ly and replace with saray.me.
1 | var x=prompt('Bit.ly URL');alert(x.replace('bit.ly','saray.me')); |
Here, you can drag this to your toolbar if you want ![]()
Saray.Me the Bit.Ly
Branding your Tweets using @anywhere tweetbox
May 4th, 2010With Twitter’s new @anywhere features, it’s now possible to brand your tweets from your own web page. While I still like using Tweetdeck or Seesmic for my actual interaction on twitter, I have started tweeting a bit from my own web page as well.
@anywhere features an interactive tweetbox that you can embed on your website. The javascript call allows you to add your own content to the box – just waiting for the user to hit TWEET. (Of course, they have to connect to your website.) In order to leverage this for my own website, and brand my tweets from AaronSaray.com, I decided that I could use this box in my browser sidebar. Here are the steps – in case you want to do it yourself!
Get Your “App” an API Key
This is pretty simple. First, log into the web interface for twitter. Then, visit the apps page at http://twitter.com/apps. Click on register a new application if you need to.
On this page, you can enter all the details of your application. For the application name, I entered AaronSaray.com to provide proper branding. Last, make sure that you check Read/Write access. Your application needs to be able to post!
After this is complete, you may want to view your page’s settings. (You can do this by clicking on the name of the application on the apps page.) The important thing to do here is to access the ‘Consumer Key’ and copy this down.
Create your webpage
Next, I created a web page on my domain. It has the following code:
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 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>Twitter</title>
<style type="text/css">
body {
background-color: #f3f3f3;
}
</style>
<script src="http://platform.twitter.com/anywhere.js?id=CONSUMER_KEY&v=1" type="text/javascript"></script>
</head>
<body>
<div id="custom-tweetbox"></div>
<script type="text/javascript">
twttr.anywhere(onAnywhereLoad);
function onAnywhereLoad(twitter) {
twitter("#custom-tweetbox").tweetBox({
label: "<span style='color: #aaa'>Tweet this:</span>",
height: 90,
width: 200
});
};
</script>
</body>
</html> |
First off, make sure to change CONSUMER_KEY to the actual key from your applications settings page. Next, you can change the CSS in this page to match your theme if you want. Since I’m loading this in the side bar, this is the simple layout I chose.
Finally, include the javascript and configure the tweetbox. I chose not to prepopulate my tweetbox with content. However, you could if you wish…
Add Book Mark
Add the bookmark to your browser. I added mine to my firefox bookmark toolbar. Then, I right clicked and chose properties. I checked the ‘load this in the sidebar’ option.
Use It!
The first time you use the tweet box, it will prompt you to connect to your website. After this is done, you can use this easily. My branded tweets show that its from AaronSaray.com nicely. See?






