Tag Archives: PHP

An example of why i don't like the ext/filter API

Earlier this week i decided to experiment with the Filter functions. Here’s an example that illustrates why i think the API needs to be improved:

<?php
$isgoodapi = filter_input(INPUT_GET, 'isgoodapi', FILTER_VALIDATE_BOOLEAN);

if (is_null($isgoodapi)) {
 echo "the 'isgoodapi' argument is missing.";
} else if ($isgoodapi === FALSE) {
 echo "The 'isgoodapi' argument must be a valid boolean.";
} else {
 echo "isgoodapi is: $isgoodapi.";
}
?>

And now you request the page with ?isgoodapi=false. The obvious problem is the fact that the function returns multiple ‘sorts’ of return values: Value of the requested variable on success, FALSE if the filter fails, or NULL if the variable_name variable is not set. If the flag FILTER_NULL_ON_FAILURE is used, it returns FALSE if the variable is not set and NULL if the filter fails.

The documentation for Filter Functions says for FILTER_VALIDATE_BOOLEAN: Returns TRUE for “1″, “true”, “on” and “yes”, FALSE for “0″, “false”, “off”, “no”, and “”, NULL otherwise. So if you try with ?isgoodapi=konijn you would expect NULL but that isn’t the case either.

Generate a menu with month names

I still see people building their calendar control or month (or day) picker with a hardcoded array of month (or day) names. With the use of strftime you can easily build a locale aware version. Here is an example:

<?php
function SelectMonths($name = 'selectMonths', $id = 'selectMonths') {
 $current_month = date('n');

 echo '<select name="' . $name .'" id="' . $id . '">';

 for ($i = 1; $i < 13; ++$i) {
  echo '<option value="' . $i . '"';

  if ($i == $current_month) {
   echo ' selected';
  }

 $month_name = strftime('%B', mktime(0, 0, 0, $i, 1, 2006));

  echo '>' . $month_name . '</option>';
 }

 echo '</select>';
}
?>

And now you can easily generate a localized menu:

<?php
include('SelectMonths.php');

SelectMonths();

// Tested on a Windows host - Read the http://be.php.net/setlocale
setlocale(LC_TIME, 'dutch');
SelectMonths();
?>

Dynamic CSS with PHP

Both html and css are simply text. Thus you should be able to generate css as easily as html with php. Now if you add a reference to the css.php file in your html (eg: <link rel=”stylesheet” href=”http://example.com/css.php” type=”text/css” media=”screen” />) you’ll probably experience that your browser ignores the file. How is this possible? Here is an example of a simple css.php file

body {
        background-color: <?php echo 'yellow'; ?>;
}

Here is a simulation of what your browser recieves when it requests the file:

HTTP/1.1 200 OK
Date: Sat, 26 Aug 2006 23:36:21 GMT
Server: Apache/1.3.34 (Unix) PHP/4.4.2 mod_macro/1.1.2
X-Powered-By: PHP/4.4.2
Connection: close
Content-Type: text/html; charset=iso-8859-1

body {
        background-color: yellow;
}

Where did that content-type header come from? Well, php outputs a default content-type header (text/html) when you don’t set value explicitely. This means that your browser will try to interpret the file as html instead of css. Although it may seem weird, this behaviour is explicitely defined in RFC 2616.

So the solution is pretty simple: explicitely generate content-type header. Here is an example for css: (You’re smart enough to figure it out for csv, m3u, …)

<?php header('Content-type: text/css');?>
body {
  background-color: <?php echo 'white'; ?>
}

Allow a form to be posted only once

People can fill in a form and submit it. Then they can hit their back button, and choose to submit it again. Usually the second time this form is being posted, the values in that form aren’t valid anymore and thus corrupt the database.
Most developpers i know try to work around this problem by using the header function or the html meta tags to set the expiration date. However, this solution does not only limit the usability of a site, it simply does not work for visitors that have a browser that ignores the expiration date.

My solution for this problem is quite easy. For each entity in the database that can be updated by a form, we should add an attribute lastupdate. Now every time we build a form that contains data of that entity, we should also add an input of type hidden with the value of that lastupdate attribute. If the value of the lastupdate attribute in the database is more recent than the value of the posted lastupdate in the recieving script, then the posted values are invalid and this script should tell the user about this error. Offcourse, every time such an entity is updated, the lastupdate attribute of this entity should be updated too.

Using cmd.exe

Earlier someone asked me how he could use windows cmd.exe with PHP. People run into trouble as soon as there are quotes needed because there are special characters (>/&()[]{}^=;!’+,`~ and <space>) in the command. I do it like this:

<?php
$result = `""c:\\my path\\prog.exe" "filename""`;
?>

In case you have to do it often you might want to wrap it into a little function like this:

<?php
function cmd($command, $arguments = null)
{
        $commandline = '';
        foreach(func_get_args() as $word) {
                $commandline .= '"' . $word . '" ';
        }
        $commandline = rtrim($commandline, ' ');
        $commandline = '"' . $commandline . '"';
        return `$commandline`;
}

// run blah.exe
cmd('blah.exe');

// run c:\my path\blah.exe with the arguments "foo" and "bar bar"
cmd('c:\\my path\\blah.exe', 'foo', 'bar bar');
?>

Pagination for all

Suppose you have a a large collection of items and you want to display them. Users don’t want to see 5000 items at once. They only want to see a couple of items and have the possibility to look at the next (or previous) couple of items. The solution for this problem is usually named pagination. You can compare this technique with paging. Most people seem to come up with their own (My)SQL specific implementation. Here are a couple of examples how you can use mine:

<?php
require('http://www.timvw.be/wp-content/code/php/pagination.txt');

// step 1 - create pageable data: array example
$data = array(
	array('name' => 'Jameson', 'surname' => 'Jenna'),
	array('name' => 'Banks', 'surname' => 'Briana'),
	array('name' => 'Giovanni', 'surname' => 'Aria'),
	array('name' => 'Rush', 'surname' => 'Daniella'),
	array('name' => 'Flowers', 'surname' => 'April')
);
$pageabledata = new PageableArray($data);

// step 2 - create the pager
$pager = new Pager($pageabledata);

// step 3 - create the pagewriter
$pagewriter = new PageWriter($pager);

// step 4 - create the paginator
$paginator = new Paginator($pagewriter);

// step 5 - run the paginators
$paginator->run();

// setp 6 - output
$paginator->output();
?>

I have also provided code for situations where you want to paginate File contents, MySQL or ADODB resultsets. In that case the code for step 1 would look like:

<?php
// file example
$pageabledata = new PageableFile("/home/users/timvw/.bash_history");

// mysql example
$dblink = mysql_connect('localhost', 'username', 'password');
mysql_select_db('dbname', $dblink);
$pageabledata = new PageableMySQL('SELECT * FROM wp_posts ORDER BY 1', $dblink);

// adodb example
require('adodb/adodb.inc.php');
$db = NewADOConnection('mysql://username:password@localhost/dbname');
$pageabledata = new PageableADODB('SELECT * FROM wp_posts ORDER BY 1', $db);
?>

You will probably want to modify the code so that it generates the html you want. Here is how an example of such a change:

<?php
/**
 * This class represents a PageJumpWriter
 */
class PageJumpWriter extends PageWriter {
        /**
         * Default constructor
         * @param $pager the pager
         * @param $base_url the baseurl for the pager
         * @param $page_param the name of the page parameter
	 * @param $items_per_page_param the name of the items per page parameter
	 * @param $params additional url parameters in the form of a name=>value array
         */
	function PageJumpWriter(&$pager, $base_url = '', $page_param = 'page', $items_per_page_param = 'items_per_page', $params = null) {
		parent::PageWriter($pager, $base_url, $page_param, $items_per_page_param, $params);
	}

	/**
	 * Generate html for the items pager
	 * @see PageWriter#makeItemsPager
	 */
	function makeItemsPager() {
                $current_page = $this->pager->getCurrentPage();
                $last_page = $this->pager->getLastPage();
                $prev_page = $current_page - 1;
                $next_page = $current_page + 1;
		$items_per_page = $this->pager->getItemsPerPage();
                $html = "<div class='itemspager'>";
		$html .= "<form method='GET' action='{$this->base_url}' onChange='this.submit()'>";
		$html .= "<select name='{$this->page_param}'>";
		for ($i = 1; $i <= $last_page; ++$i) {
			if ($i != $current_page) {
				$html .= "<option value='{$i}'>Page {$i} of {$last_page}</option>";
			} else {
				$html .= "<option value='{$i}' selected>Page {$i} of {$last_page}</option>";
			}
		}
		$html .= "</select>";
		$html .= "<input type='hidden' name='{$this->items_per_page_param}' value='{$items_per_page}'/>";
		foreach($this->params as $name => $value) {
			$html .= "<input type='hidden' name='{$name}' value='{$value}'/>";
		}
		$html .= "<input type='submit' value='Go'/>";
		$html .= "</form>";
                $html .= "</div>";
                return $html;
	}
}
?>

If you want to use this customized html generator you simple change the code in step 3 as following:

<?php
$pagewriter2 = new PageJumpWriter($pager2);
?>

The problem with most of these paginators is that you can only use one per page. This is a serious PITA. It’s your lucky day, here is an example of two paginators that can run separately in the same page:

<?php
// step 1 - create the pageable data
$dblink = mysql_connect('localhost', 'username', 'password');
mysql_select_db('dbname', $dblink);
$pageabledata = new PageableMySQL('SELECT * FROM wp_posts ORDER BY 1', $dblink);
$pageabledata2 = new PageableFile('/var/www/somefile.txt');

// step 2 - create the pagers
$pager = new Pager($pageabledata);
$pager2 = new Pager($pageabledata2);

// step 3 - create the pagewriters
$pagewriter = new PageWriter($pager);
$pagewriter2 = new PageJumpWriter($pager2, '', 'page2', 'items_per_page2');

// step 4 - create the paginators
$paginator = new Paginator($pagewriter);
$paginator2 = new Paginator($pagewriter2);

// step 5 - run the paginators
$paginator->run();
$paginator2->run();

// add extra url parameters for pagewriters
$pagewriter->setParameters(array(
	$pagewriter2->getPageParam() => $pagewriter2->pager->getCurrentPage(),
	$pagewriter2->getItemsPerPageParam() => $pagewriter2->pager->getItemsPerPage()
));

$pagewriter2->setParameters(array(
	$pagewriter->getPageParam() => $pagewriter->pager->getCurrentPage(),
	$pagewriter->getItemsPerPageParam() => $pagewriter->pager->getItemsPerPage()
));

// step 6 - output
$paginator->output();
$paginator2->output();
?>

Accept posted XML data

I remember that i’ve spent a lot of time finding something that allowed me to accept the posted XML data. The solution was very simple:

$data = file_get_contents('php://input');

Brainteaser

Earlier today Chung Leong, an intelligent regular at comp.lang.php, posted a little brainteaser:

The two functions in the example below behave differently. The difference is easy to spot, of ocurse. The challenge is correctly explaining why this is so. Why does the second function
seemingly corrupt the cloned copy of an object?

<?php
// uncomment the clone operator for PHP 5

function Bobcat(&$obj) {
        $clone = /* clone */ $obj;
        $obj->attributes['Length'] = 0;
        $obj->data = "";
        return $clone;
}

function BritneySpear(&$obj) {
        $attr =& $obj->attributes;
        $clone = /* clone */ $obj;
        $obj->attributes['Length'] = 0;
        $obj->data = "";
        return $clone;
}

$data = "This is a test";
$obj1->attributes = array('Length' => strlen($data));
$obj1->data = $data;
$clone1 = Bobcat($obj1);
print_r($clone1);

$obj2->attributes = array('Length' => strlen($data));
$obj2->data = $data;
$clone2 = BritneySpear($obj2);
print_r($clone2);
?>
Result:

stdClass Object
(
    [attributes] => Array
        (
            [Length] => 14
        )

    [data] => This is a test
)
stdClass Object
(
    [attributes] => Array
        (
            [Length] => 0
        )

    [data] => This is a test
)

It took me fifteen minutes to figure out the source of this mysterious behaviour, but it took me a couple of hours to come up with the following explanation: After $attr =& $obj->attributes in the BritneySpear function the container that holds this variables has is_ref=1. Any properties that are references to other variables, will remain references when $obj is copied into $clone as explained in Object cloning.

If you want to know more about references i can advise you to read PHP References by Derick Rethans.

How private is private really?

Today i ended up at private properties exposed (Apparently it’s also used by PHPUnit)

class foo {
 private $bar = 42;
}

$obj = new foo;
$propname="\0foo\0bar";
$a = (array) $obj;
echo $a[$propname];

Wordfinder

I’ve noticed there are still a lot of shows on television that want you to find a word. So i wrote a little wordfinder that does this for you using the ispell wordlist. As always, you can download the wordfinder.txt.