contact
----------------------------

Blog <-

Archive for the ‘php’ Category

RSS   RSS feed for this category

Templum v0.4.0 released (Simple PHP templating)

I've released Templum v0.4.0

Templum is an extremely lightweight, simple yet powerful and fast templating engine for PHP. It re-uses the power of PHP itself for rendering templates, but provides additional features making it easier to write templating code. Rendering templates using Templum is very fast; it approximates native PHP rendering speed for include() statements.

This release features:

  • Some small bug fixes
  • Documentation updates
  • The ability to include other templates in a template

Download instructions here.

Templum v0.2.0: Simple PHP Templating

I just released v0.2.0 of Templum, a simple templating engine for PHP.

About

From the homepage:

Templum is an extremely lightweight, simple yet powerful and fast templating engine for PHP. It re-uses the power of PHP itself for rendering templates, but provides additional features making it easier to write templating code. Rendering templates using Templum is very fast; it approximates native PHP rendering speed for include() statements.

Changes

Changes in this release:

  • PHP 4 support added (patch by Pierre Jochem).
  • Bugfix (#1): {{$var}} at end-of-line eats newline following it.
  • Various examples added.
  • Added the ability to turn off automatic escaping using htmlentities().
  • Improved the error reporting.
  • The locale can now be changed after creating a Templum instance.
  • Userguide updated.

This release is backwards compatible with the previous version 0.1.0.

Install

You can install Templum v0.2.0 using PEAR:

pear install http://templum.electricmonk.nl/releases/templum-0.2.0.tgz

If you've got a previous version of Templum installed, you must first uninstall that one:

pear uninstall channel://__uri/templum-0.1.0

There's also a non-PEAR tar.gz which also contains examples and the API documentation and Userguide:

templum-src-0.2.0.tar.gz.

More information

Easy PEAR Package creation

Here's a fairly simple way of creating PEAR package so you can distribute your application or library as a PEAR package.

Premise.

Let's assume you've written an application or library called 'MyApp'. The following is a possible directory structure layout:

myapp/
  |- MyApp.php
  \- MyAap/
       \- MyApp/MyLib.php

Install the PackageFileManager.

Since writing a package file by hand is way too much work, we first install the PEAR_PackageFileManager utility. This PEAR package includes a commandline utility which will guide you through the steps needed to create a proper package.xml file for your application or library.

# pear install pear install PEAR_PackageFileManager_Cli-0.3.0
Ignoring installed package pear/pear
No releases available for package "pear.php.net/install"
Cannot initialize 'channel://pear.php.net/install', invalid or missing package file
WARNING: channel "pear.php.net" has updated its protocols, use "channel-update pear.php.net" to update
Did not download optional dependencies: pear/PHP_CompatInfo, use --alldeps to download automatically
pear/PEAR_PackageFileManager can optionally use package "pear/PHP_CompatInfo" (version >= 1.4.0)
downloading PEAR_PackageFileManager_Cli-0.3.0.tgz ...
Starting to download PEAR_PackageFileManager_Cli-0.3.0.tgz (8,051 bytes)
.....done: 8,051 bytes
downloading PEAR_PackageFileManager-1.6.3.tgz ...
Starting to download PEAR_PackageFileManager-1.6.3.tgz (82,393 bytes)
...done: 82,393 bytes
Package "channel://pear.php.net/install" is not valid
install ok: channel://pear.php.net/PEAR_PackageFileManager-1.6.3
install ok: channel://pear.php.net/PEAR_PackageFileManager_Cli-0.3.0

If your PEAR is outdated, you will see something like:

pear/PEAR_PackageFileManager requires PEAR Installer (version >= 1.8.0alpha1), installed version is 1.7.1

In that case, you can upgrade your PEAR like so:

# pear channel-update pear.php.net
Updating channel "pear.php.net"
Update of Channel "pear.php.net" succeeded
# pear upgrade PEAR
...
upgrade ok: channel://pear.php.net/PEAR-1.8.1

You may also run into problems with alpha/beta state packages required for installation of the PackageFileManager CLI package. For instance, I got the following error: Failed to download pear/XML_Serializer within preferred state "stable", latest release is version 0.19.2, stability "beta", use "channel://pear.php.net/XML_Serializer-0.19.2" to install

In that case, you must manually install the beta packages:

# pear install channel://pear.php.net/XML_Serializer-0.19.2
downloading XML_Serializer-0.19.2.tgz ...
Starting to download XML_Serializer-0.19.2.tgz (39,703 bytes)
..........done: 39,703 bytes
downloading XML_Parser-1.3.2.tgz ...
Starting to download XML_Parser-1.3.2.tgz (16,260 bytes)
...done: 16,260 bytes
install ok: channel://pear.php.net/XML_Parser-1.3.2
install ok: channel://pear.php.net/XML_Serializer-0.19.2

Generate a package.xml file.

You can now use the PackageFileManager command-line utility to bootstrap a package.xml file for your PEAR package.

Change to the directory that is the root of your package:

$ cd myapp/

myapp$ ls
MyApp  MyApp.php

myapp$ pfm

PEAR Package File Manager Command Line Tool

Please enter the location of your package [.]*: .

Creating a new package file ...

Enter the base install directory*: .

Enter the name of the package [myapp]*: MyApp

Channel or URI based package? [c] (c,u)*: u

Enter the package URI*: http://www.example.com/download/PEAR/

Enter a 1 line summary*: My first PEAR example application.

Enter a description* (2 blank lines to finish):
MyApp is my first PEAR example application.
It rocks.


Enter the release version*: 0.1.0

Enter the API version [0.1.0]*: 

Choose a release stability [alpha] (alpha,beta,stable)*:  

Choose an API stability [alpha] (alpha,beta,stable)*: 

Enter any release notes* (2 blank lines to finish):
Initial release


Enter the minimum PHP version [5]*: 

Enter the minimum PEAR Installer version [1.4.0]*: 

Please choose a license from one of the following options

    1) Apache
    2) BSD Style
    3) LGPL
    4) MIT
    5) PHP

Please choose an option: 4

How many maintainers?*: 1

What type of maintainer is #1? [lead] (lead,developer,contributor,helper)*:     

Enter maintainer #1's name*: Ferry Boender

Enter maintainer #1's username*: fboender

Enter maintainer #1's email [fboender@php.net]*: ferry.boender@example.com


PEAR Package File Manager Command Line Tool

    1. Package name                 [MyApp]
    2. Channel/URI                  [URI: http://www.example.com/download/PEAR/]
    3. Summary                      [My first PEAR example application.]
    4. Description                  [MyApp is my first PEAR example applic...]
    5. Maintainers
    6. Version                      [Release: 0.1.0 API: 0.1.0]
    7. Stability                    [Release: alpha API: alpha]
    8. License                      [MIT]
    9. Notes                        [Initial release]
   10. Dependencies
   11. Tasks
   12. Regenerate contents
   13. Echo package file to stdout
   14. Save & Quit
   15. Quit without saving          (ctrl-c)

Please choose an option from the menu: 14

Analyzing MyApp/MyLib.php
Analyzing MyApp.php

This generates a package.xml for you, containing details for the PEAR package creation.

Create a PEAR package.

PEAR can use this package.xml file to create a PEAR package for you:

myapp$ pear package package.xml 
Analyzing myapp/MyApp.php
Analyzing myapp/MyApp/MyLib.php
Package MyApp-0.1.0.tgz done

Testing and (un)installing your package

Test the installation of the package:

myapp$ pear install ./MyApp-0.1.0.tgz
pear install ./MyApp-0.1.0.tgz 
install ok: channel://__uri/MyApp-0.1.0

It appears to work. Make sure to check the PEAR directory to see if it placed the files in the right spot:

myapp$ ls /usr/share/php/
Archive  MyApp      OS    PEAR.php  Structures  adodb  doc   pearcmd.php  php-shell-cmd.php  
Console  MyApp.php  PEAR  PHP       System.php  data   docs  peclcmd.php  

The MyApp.php file, and the MyApp directory have been added to the PEAR library directory. If their PHP include_path is set correctly, users of your package should now be able to include it using:

include('MyApp.php');

You can uninstall the package using the pear uninstall command:

$ pear uninstall channel://__uri/MyApp-0.1.0
uninstall ok: channel://__uri/MyApp-0.1.0

Distribute your package

You can now put your PEAR package online, and point users to it by mentioning they can install it using PEAR with the command:

$ pear install http://www.example.com/download/PEAR/MyApp-0.1.0.tgz

Templum: Simple PHP Templating

At the company I work for (ZX), we needed a simple yet powerful templating language for PHP. I googled around a bit for something, but we couldn't really find anything. So I wrote Templum, and ZX was kind enough to allow me to release it as Open Source under the MIT License.

From the Templum homepage:

Templum is an extremely lightweight, simple yet powerful and fast templating engine for PHP. It re-uses the power of PHP itself for rendering templates, but provides additional features making it easier to write templating code. Rendering templates using Templum is very fast; it approximates native PHP rendering speed for include() statements.

You can install it using PEAR:

pear install http://templum.electricmonk.nl/releases/templum-0.1.0.tgz

For some examples, check the website.

Links

Here are some random links to interesting stuff:

FirePHP
FirePHP is a PHP debugging library and a Firefox plugin which allow you to output debugging information to the Firebug debugging panel. Since it doesn't intermingle debugging information with your page output, but writes in a special HTTP header instead, it's especially useful for AJAX debugging. It can also come in handy when you're trying to debug a server-side script which generates something else than a HTML page. A PDF or PNG file, for example.

OpenProj
OpenProj is a project management application written in Java and therefor platform independent. It has a lot of the features Microsoft Project has (according to the webpage; I have never used MS Project before, so I wouldn't know) such as Resources, Gantt Charts, Network Diagrams (PERT Charts), WBS and RBS charts, etc. There are also various different representations of tasks for resources. It doesn't really outshine Gnome Planner, but at least it's platform independent.

Typechecking Python module
Typecheck provides powerful run-time typechecking facilities for Python functions, methods and generators. Without requiring a custom preprocessor or alterations to the language, the typecheck package allows programmers and quality assurance engineers to make precise assertions about the input to, and output from, their code.

Here's a little code example:

@accepts(String, [Number], {str: Number})
def my_func(a, *vargs, **kwargs):
    pass
 
@accepts(String, Number, Number)
def my_func(a, *vargs, **kwargs):
    pass

Giving up on PHP

I have given up on PHP. I will still need to use it at my job, but in private I refuse to even touch it anymore.

Lately, I've been busy creating a framework, or a bunch of libraries actually, which where meant to negate some of PHP's worst qualities. It had better error reporting (the kind you can't just ignore), logging, sane naming schemes, better configurability, etc. But at every turn, it felt like PHP was actively trying to fight my efforts at overcoming its obscene shortcomings.

I will not rehash here those shortcomings of PHP, except that it's a pathetic excuse for a programming language written by a bunch of particularly bad programmers. The same goes for PEAR. They're simply horrid.

Some time ago I wrote a little web application in Python, and I actually had fun doing it. It felt liberating not having to fight your programming language every step of the way for once. I revelled in the professionalism with which basically everything in and for Python is constructed. It's invigorating to work with libraries that aren't written in a fundamentally broken way just so the authors can use every Design Pattern under the sun.

It's not strange that Ruby (On Rails) has become so popular. Even though Rails is severely limited in its abilities, people will gladly put up with it because of Ruby. Anybody speaking in favour of PHP compared to Python or Ruby simply hasn't worked with either Ruby or Python, or has never done anything significant in PHP. Or, like me, where so blinded by how much time they've invested in PHP, that they couldn't and didn't want to admit what a total piece of crap PHP really is.

Thank you, PHP, for finally exposing yourself as the leper you are. Our ways part here, and may I never run into you again. I'm running off with Python, and we'll live happily ever after.

The Unexpected SQL Injection

Something every PHP developer should be reading:

The Unexpected SQL Injection – When Escaping Is Not Enough

The conclusions:

  • Write properly quoted SQL:
    1. Single quotes around values (string literals and numbers)
    2. Backtick quotes around identifiers (databases, tables, columns, aliases)
  • Properly escape the strings and numbers:
    1. mysql_real_escape_string() for all values (string literals and numbers)
    2. intval() for all number values and the numeric parameters of LIMIT
    3. Escape wildcard/regexp metacharacters (addcslashes('%_') for LIKE, and you better avoid REGEXP/RLIKE)
    4. If identifiers (columns, tables or databases) or keywords (such as ASC and DESC) are referenced in the script parameters, make sure (and force) their values are chosen only as one of an explicit set of options
    5. No matter what validation steps you take when processing the user input in your scripts, always do the escaping steps before issuing the query. Validation is not a substitute for escaping!

Like my rule #1 of what I like to call Defensive Coding: Don't be implicit, be explicit. In other words, don't try to escape things you don't want in your strings, simply only leave everything you do want in your strings. A column name in a ORDER BY clause should only consist of A-Z, a-z and 0-9. Anything else in the string invalidates that string.

Callback functions in PHP

I was implementing a plugin system for a framework I'm writing, and I needed plugins to be able to register a callback function or method with the Plugin Manager so that the callback function would get called when a signal was emitted. Here's a very stripped down version of what I ended up with:

<?php
 
// Sample callback function
function callback_function() {
	print("callback_function()\n");
}
 
// Sample class with static and non-static callback functions
class Callback
{
	// Sample callback method in a class instance
	public function callback_method() {
		print("Callback->callback_method()\n");
	}
 
	// Sample static callback method
	public static function callback_method_static() {
		print("Callback::callback_method_static()\n");
	}
}
 
// Hook class with which you register callback functions and methods
// which will be called when you call the emit() method.
class Hook
{
	private $callbacks = array(); // All the registered callbacks.
 
	//
	// Register a function/method as a callback function.
	//
	public function registerCallback($callback) {
		$this->callbacks[] = $callback;
	}
 
	//
	// Run all the functions/methods registered as callbacks.
	//
	public function emit() {
		foreach($this->callbacks as $callback) {
			call_user_func($callback);
		}
	}
}
 
// Create a hook class instance with which we will register our
// callbacks.
$hook = new Hook();
 
// Instantiate the sample callback class.
$callback = new Callback();
 
// Register all the sample callbacks with the Hook class instance.
$hook->registerCallback('callback_function');
$hook->registerCallback(array($callback, 'callback_method'));
$hook->registerCallback(array('Callback', 'callback_method_static'));
 
// Make the Hook class instance call all the registered callback
// methods.
$hook->emit();
 
?>

First, we set up some sample callback functions and methods (callback_function(), Callback->callback_method() and Callback::callback_method_static()). Then we define the Hook class with which we can register callback functions. Finally, we register our sample callbacks with the Hook class and emit the 'signal'.

Right now, there's only a single signal (or no signal at all, depending on how you look at it), but that's easy to implement. The magic basically is in the emit() method of the Hook class. It iterates over the registered callbacks, which is nothing more than an array containing either strings for function callbacks or arrays for static and non-static callback methods. It then calls those using the call_user_func() method.

All this results in the following output, when ran:

callback_function()
Callback->callback_method()
Callback::callback_method_static()

Nothing fancy, and I must have implemented something similar a gazillion times, but I thought I'd share it with you all.

PHP Configuration hell

From the Apache2 configuration file for host example.com:

php_admin_value upload_tmp_dir "/var/www/example.com/tmp/"

From the file /var/www/example.com/htdocs/test.php:

var_dump(ini_get("upload_tmp_dir"));

Output:

string(29) "/var/www/example.com/tmp/"

Then, after trying to upload a file, from the /var/www/example.com/logs/error.log:

[Sun Sep 02 18:09:05 2007] [error] [client 88.211.179.104] PHP Warning:  Unknown: open_basedir restriction in effect. File(/tmp) is not within the allowed path(s): (/var/www/example.com/) in Unknown on line 0, referer: http://example.com/test.php
[Sun Sep 02 18:09:05 2007] [error] [client 88.211.179.104] PHP Warning:  File upload error - unable to create a temporary file in Unknown on line 0, referer: http://example.com/test.php

I'll highlight the important part for you: open_basedir restriction in effect. File(/tmp) is not within the allowed path(s):.

The lesson?? PHP needs to STOP silently ignoring errors and stop just using the default value when errors are encountered! The problem was that I specified the wrong upload_tmp_dir. It should have been /var/www/example.com/htdocs/tmp/. PHP should have thrown an error because this directory doesn't exist (it has detected this, because it falls back to the default of /tmp) and not just continue.

PHP's configuration implementation is one of the worst I've ever seen, and whoever's responsible for these kinds of problems should feel ashamed. PHP, like MySQL, is way to lenient when it comes to errors; silently trying to 'recover' from them. And people wonder why they're considered such bad projects?

A tip for PHP's developers: Fail early and fail loudly.

Vim and PHP: tips

I've been using Vim for years now, but there's still new stuff to learn. Check out this page for the PDF version of the slides of a talk given by Andrei Zmievski on editing PHP with Vim. His configuration files are also available.

Here's my favourite list of tips:

Add the following text to your ~/.vim/ftplugin/php.vim file:

set formatoptions+=tcqlro
let php_sql_query=1
let php_htmlInStrings=1
let php_folding = 1

This will:

  • Turn on automatic text formatting for PHP so that, for instance, Vim automatically inserts a '*' if you press enter inside a /* */ comment.
  • Makes Vim highlight SQL queries in strings.
  • Makes VIm highlight HTML in strings.
  • Allows folding on PHP classes and functions. (With the cursor on the first line of a function, press z-c to hide the function. z-o to show it again. (Close and Open the fold)