All posts by admin

log4php example

This log4php example demonstrates how to the log4php library in a non-trivial way. log4php is a version of the Apache log4J package ported to PHP5. Please refer to the apache project for complete details.

log4php is a powerful logging mechanism that allows you to use a configuration file to specify what types of messages you want to route to various destinations (eg: file, email, database record). This makes it easy to change what messages are logged or where they are sent without tinkering around with code in the application.

In concept log4php has three core entities that work together:

(1) Loggers – loggers know what level of message to capture, they use one or more Appenders; loggers can be defined in a heirarchial manner, always stemming from the root logger. Loggers are named, and the periods in the name indicate the heirarchial levels (phpfunk, phpfunk.descendant, phpfunk.descendant.furtherDescenant, etc).

(2) Appenders – determine where the logged messages are sent and use Layouts to determine how the message should be formatted.

(3) Layouts specify the formating of the logged message.

In this example we want to capture all log messages to a file called default.log. We also want to capture serious errors to error.log and send an error message via email to the webmaster to let him know something is wrong.

First lets look at how simple the code is. I’m throwing an Exception just for fun. The configuration that I’ll explain below is the key to harnessing the power of log4php.

require_once 'log4php/Logger.php';
Logger::configure('logconfig.xml');

// get logger by name, if this name isn't listed 
// as a logger name in the configuration 
// file you will get the root level logger
$logger = Logger::getLogger('phpfunk');
$logger->info("beginning log4php example");
try {
    throw new Exception("log4php example error");
}
catch(Exception $e) {
    $logger->error($e->getMessage());
}

The log files now contain:
::::::::::::::
/tmp/default.log
::::::::::::::
2011-04-13 13:25:35 [INFO] phpfunk: beginning log4php example (at /home/tom/testlog.php line 8)
2011-04-13 13:25:35 [ERROR] phpfunk: log4php example error (at /home/tom/testlog.php line 12)
::::::::::::::
/tmp/error.log
::::::::::::::
2011-04-13 13:25:35 [ERROR] phpfunk: log4php example error (at /home/tom/testlog.php line 12)

And in addition an email was sent with the same message body as the message captured in error.log.

Now lets dig into the log4php configuration. I’m using an XML file for configuration because I want to use a Filter in the Appender. At the bottom of the configuration file I set the logging level and appender to use for the root logger and another logger named phpfunk, which descends from the root because the root logger is always the top most logger.

Appenders are referenced by name in the appender_ref element in the logger mark up. For phpfunk I have specified two appenders, a file and one to send messages via email. Both of those appenders further use filtering to control what level messages are captured in the logs; in this case ERROR and FATAL messages.

The root logger records DEBUG and higher messages. The Appender it uses writes these messages to default.log.

I want to use phpfunk logger, for this example, to capture all messages to default.log, but only capture ERROR and FATAL messages to error.log and also to email ERROR and FATAL messages to the webmaster. If I set the default logger level to ERROR for phpfunk then any INFO level messages it receives are discarded. By setting the level to ANY I ensure that INFO level messages get passed up to the default logger’s Appender and appear in default.log.

Here is the log4php XML configuration and you can download the example log4php configuration.

<?xml version="1.0"?>
<log4php:configuration xmlns:log4php="http://logging.apache.org/log4php/">
    <appender name="default" class="LoggerAppenderFile">
        <param name="file" value="/tmp/default.log" />
        <param name="datePattern" value="Ymd" />
        <layout class="LoggerLayoutPattern">
            <param name="ConversionPattern" value="%d{Y-m-d H:i:s} [%p] %c: %m (at %F line %L)%n" />
        </layout>
    </appender>
    <appender name="errorLog" class="LoggerAppenderFile">
        <param name="file" value="/tmp/error.log" />
        <param name="datePattern" value="Ymd" />
        <layout class="LoggerLayoutPattern">
            <param name="ConversionPattern" value="%d{Y-m-d H:i:s} [%p] %c: %m (at %F line %L)%n" />
        </layout>
        <filter class="LoggerFilterLevelRange">
            <param name="LevelMin" value="error" />
            <param name="LevelMax" value="fatal" />
        </filter>
        <filter class="LoggerFilterDenyAll" />
    </appender>
    <appender name="emailNotice" class="LoggerAppenderMail">
        <param name="from" value="webmaster@phpfunk.com" />
        <param name="to" value="tom@phpfunk.com" />
        <param name="subject" value="Error" />
        <layout class="LoggerLayoutPattern">
            <param name="ConversionPattern" value="%d{Y-m-d H:i:s} [%p] %c: %m (at %F line %L)%n" />
        </layout>
        <filter class="LoggerFilterLevelRange">
            <param name="LevelMin" value="error" />
            <param name="LevelMax" value="fatal" />
        </filter>
        <filter class="LoggerFilterDenyAll" />
    </appender>
    <root>
        <level value="DEBUG" />
        <appender_ref ref="default" />
    </root>
    <logger name="phpfunk" additivity="true">
        <!-- with additivity set to TRUE the default is also called, eg parent -->
        <level value="ALL" />
        <appender_ref ref="errorLog" />
        <appender_ref ref="emailNotice" />
    </logger>
</log4php:configuration>

So you see with a single configuration file its easy to switch around what level messages are captured, how they are handled (written to file, emailed) and how they are formatted. Hopefully you will find this log4php example to be a nice addition to the information presented in the log4php project documentation.

How to Get Raw Request Variables in PHP

Turns out php will tinker with names of HTTP request parameters in some circumstances. The reason is historical: register globals was a dumb idea that allowed request parameters to be converted into php variables. A request parameters is something like ?id=42 in a GET request, or the name of a form input in a POST request, to use common examples. The problem was, register globals couldn’t convert a variable with a space or dot in the name into a valid php variable because dot and space are not allowed in php variable names. In other words ?object.id=42 would become in $_GET object_id with a value of 42. Eventhough the use of register globals is frowned upon today because its a security vulnerability, the behavior of PHP remains unchanged as far as tinkering with request parameter names.

So how can you get at the raw values? I found these two mechanisms. For HTTP GET requests you can read the raw query string from $_SERVER[‘QUERY_STRING’]. For POST requests you have to call file_get_contents(‘php://input’), which is a trick I learned from this post.

Caching Static Content with mod_expires and Apache

Visitors to your website will experience faster page loads if you take some steps to enable caching of static content, such as CSS files, javascript and images. One simple way to enable such caching is to use mod_expires with Apache. If you run your own server you can easily enable this module. If you are on a shared host you should check with your hosting provider to see if its enabled. If you are looking for reasonably priced virtual private hosting you should checkout rimuhosting.com.

Here is the official documentation for the module.

I found that with ExpiresByType specifying text/javascript did not set the expiration time for javascript files. However, changing text/javascript to application/javascript did work.

If you are serving dynamic content do not set a default expires time (ie with ExpiresDefault). You do not want to cache dynamic pages like a shopping cart or other dynamically generated HTML.

Learn efficient MySQL

Having a good understanding of database schema design, how to use indices and write table joins is important for any developer. If you use mysql there is an excellent book that covers such information in a clear and concise manner.

Click on the cover image to visit amazon.com:

Traversing XML with JavaScript

Here are some excellent references on walking thru a XML blob with JavaScript. I was a little rusty. The main issue was that every element has a child node that is a text node, even if that text is empty. I was laboring under the misconception that somehow the text portion of an element was something we could get with a method call….as you can with getAttribute(attrName) for example. Not so. I was confusing “elements” with “nodes”. That’s bad.

I’m always looking for API: give me all the methods and attributes in one place so I can use it as a quick reference…this one seems like a decent reference list of methods and properties for nodes in an XML DOM.

IBM developer works usually has good tutorials. This particular one gives you a fast overview of the DOM and especially points out that elements and nodes are not the same. But the code portions are in Java, not JavaScript.    And for some reason its not on developer works anymore.  There is a PDF of it here.

Code Reference

This is my collection of links to useful information for developers.  Unfortunately, most of this information was collected between 2009 to 2012, and technology has moved on and many of the links are now broken.  Some links will even take you to an ancient version of phpfunk.com.  Ah, those were the days!

CSS filters by browser

How about a nice chart that shows you which css rule is understood by each browser so you can work around all those non-standard compliant browser “quirks”.

CSS variable column layout

The ruthsarian skidoo_too layout is so awesome, I can’t say enough about it. One, two or three columns of cross-browser compatible css bliss. You might want to check out the menus too.

Unit Testing

When I started simpletest was the unit testing framework for php that I adopted and continue to use. I’m getting more into JavaScript so of course I’ve got jsunit installed on the development server. Cutest is a nice simple C unit testing framework and it has the best name! And it all started, for me, with junit for java because I learned java first (actually I learned BASIC first, but thats a long story). I actually like the old junit website better. Oh well. And when I had to dig into OO perl I found Test::Simple to be exactly the perl unit testing framework I needed.

AJAX library

I wouldn’t touch ajax without a library. I’m not that crazy about wading thru the DOM with javascript or dealing with browser-specific javascript issues. Someone else who really is crazy about that, fortunately, has already done the hard work. So far I’ve been using prototype and its awesome. Here are some sublimely simple ajax examples with the associated source code for the php server pages.

JavaScript Context Menus

Ever want to make your web app more like a desktop app? What if mouse clicks on various widgets caused context menus to popup, like say to edit a record in a table for example. Proto.Menu is a nice clean javascript context menu library that uses Prototype.

JavaScript Character Counting

This javascript all you to track how many characters user has typed in a field.

PHP Sessions and URLs

Purpose: remove trans SID from URL for XHTML compliance.

Example php code shown here, from mtdev.com article on removing trans sid from URL. Verify that you can actually set these values on the server where you host the php code; to date we found one instance where this was not possible.

 

ini_set(‘session.use_trans_sid’, FALSE);

ini_set(“url_rewriter.tags”,””);

 

Of course you call them before calling session_start().

C Programming

I do not do much C coding, but when I do I go visit this guide at UIUC for a quick reference. If you’re a complete noob to c you could start here.

Source Code Control

I still use cvs.   Nope, I love git now.

There are some best practices for source code version control.

Unicode

Joelonsoftware.com has lots of good info and this post about unicode tells you in a straight-forward manner how it works.

CSV File Format

The csv file format (comma-separate values) is not a “standard” but it should be. Its the time honored way of organizing data. Fortunately someone has put together a nice reference about the non-standard csv “standard”.

Perl How-to

When you cannot remember how to do something in perl, simply search for the how-to. For example, if you cannot remember how hashes work you would search for the perl hash how-to.

Review of Web Hosting Companies

Here find summarized our experiences with some hosting providers. In most cases we have worked on more than one site on each host, and maintained them over a period of a couple years. Hopefully this can help you find reliable hosting company for your site.

Dreamhost.com

Dreamhost.com is the best shared hosting web host. We have hosted on their least expensive plan since 2004, which now includes unlimited domains, unlimited mysql databases and ridiculous bandwidth. Uptime is very good but they have occasional server, dns and network issues, all of which are documented in their blog. That said, their issues are not any more in number than other shared hosting providers, and they can actually tell you in technical details what went wrong. Support is very good: they respond quickly and are knowledgeable. I don”t really remember a time when I had to submit a ticket more than once to get something fixed. They have a custom control panel for managing your account and domains which is better than any other web interface I”ve used. You can request shell access. Dreamhost.com is the best place to host a site if you are on a tight budget. Moreover its a very programmer-friendly environment. They now offer VPS hosting as well. They also publish a monthly email newsletter to keep you up to date on changes.

m5hosting.com

If you are looking for a dedicated server and awesome support look no further than m5hosting.com. They can set up a server to your specifications including OS and partitioning of the disk. The support is fantastic. m5hosting.com has been our best experience with dedicated server hosting and overall our best hosting experience, period.

inmotionhosting.com

Inmotionhosting.com offers a VPS hosting service which is excellent. Includes cpanel and WHM (web host manager) in the backend for easy maintenance. You can get SSH access; select which IPs can get throught the firewall using web host manager. Hosting support is very good. Check out VPS Hosting by InMotion Hosting.

rimuhosting.com

Recently we set up a domain on a VPS hosting account at rimuhosting.com. They also offer dedicated server hosting. The set up was quick and the price is very attractive.

Lunarpages.com

Lunarpages has excellent uptime and support for shared hosting. They have cpanel interface, phpmyadmin but no shell access. Overall, I”d say they offer the best overall shared hosting in terms of performance, service and uptime. Some of our biggest projects are hosted at lunarpages.

Object Oriented JavaScript Inheritance

Here”s the short explanation of how to do inheritance with javascript. This was the best site I found on the subject and I tell the same story albeit tersely.

I defined a base class, called Base just to be clear:

Base = function()
{
}

Base.prototype.say = function(foo)
{    
    return foo;
}

Then I defined a class to extend Base called Extender:

Extender.prototype = new Base;

function Extender()
{
    // this line calls parent class constructor
    // like saying super() in Java
    // or like using parent::Base() in PHP
    Base.call(this);
}

You will have problems if you try to say Extender = function() {}…at least it didn”t work for me written that way.

Override say() method of parent class, but I also call the parent class method:

Extender.prototype.say = function(foo)
{
    // in this case we override but also call the parent method
    return 'extend ' + Base.prototype.say(foo);
}

This was tested with jsunit.