Choosing the right PDF library in PHP

Generate pdf files with PHPThere are a number of PDF libraries that can help you dynamically generate .PDF files through PHP : FPDF, TCPDF, DOMPDF, CEZPDF…

I’ve long worked with FPDF which is very light (with a core file of 49ko), easy to implement but the functions provided are limited.

TCPDF first astonished me since it is able to display pure HTML in a pdf file (tables, css,… support). It also provides transformation functions that allow you to display text vertically on a .pdf file, for example. But the TCPDF core file is much too heavy : 963 ko. I run AJAX scripts that load that library at each AJAX call, which makes the time waiting for the .pdf files to be ready much too long. So, I’ve tried to make that class lighter and got rid of all comments it contained. But I still had more than 500ko of file.

I then decided to go back to FPDF and found an extension class that allows FPDF to rotate text on a page.

This tutorial is devoted to implementing FPDF (and rotation class) in PHP framework Codeigniter 2.0.3.

First download the FPDF class. You’ll find it in the download page of http://www.fpdf.org. Along with the fpdf.php class, you’ll find tutorials and docs in the downloaded file

Simply copy the file fpdf.php to /Codeigniter_2.0.3/application/libraries/ and copy the font/ directory to /Codeigniter_2.0.3/application/third_party/fpdf/

Then go to http://www.fpdf.org/fr/script/script2.php where you’ll find two classes you’ll copy in the same libraries directory:

  • The FPDF_Rotate class as an extension of the FPDF class
  • The PDF class as an extension of the FDPF_Rotate class

Be careful when creating those two libraries in Codeigniter to follow Codeigniter’s tips on creating libraries. You’ll find the files attached to the bottom of this tutorial.

You’ll also have to define the path to the FPDF font directory in /Codeigniter_2.0.3/application/config/config.php :

//FPDF FONT DIRECTORY
$config['fonts_path'] = APPPATH.'third_party/fpdf/fonts/';

Then, in your controller, simply call the library and set the necessary items :
define('FPDF_FONTPATH',$this->config->item('fonts_path'));
$this->load->library(array('fpdf','fpdf_rotate','pdf'));
$this->pdf->Open();
$this->pdf->SetFont('Arial', '', 12);
$this->pdf->SetDrawColor(0);
$this->pdf->RotatedText(10,40,'Hello World!',90);
$this->pdf->MultiCell(100,5,"Test\nSecond line");
$this->pdf->Output('./pdfs/test.pdf', 'F');

You can download the necessary files : fpdf class files

Webliography :

FPDF Class

TCPDF

DOMPDF

Code Igniter, a PHP Framework

Choosing a PHP Framework to develop websites can save you time and efforts. I’m not writing this post to write an exhaustive list of the pros and cons of different PHP frameworks but just want to highlight why I think PHP frameworks are worth trying.

In my very case, it has also led me to better programming practises.

Code Igniter - PHP FrameworkKohana - PHP5 FrameworkZend Framework

The framework I’ve developed entire projects with is Code Igniter (v.1.7.2). I have also had a go with Kohana3 (just for fun) because I was looking for a strict PHP5 lightweight framework. I have not got very deep into Zend Framework because it looks a bit heavyweight for my purposes. However, it is developed by Zend Technologies Ltd., the reference in terms of web-based PHP applications.

If you have never tried a PHP framework and are ready to view how it works in a few minutes, just try one of the official video tutorials on http://codeigniter.com/tutorials/ : strictly astonishing!

At first, I was a bit reluctant to choose a framework and preferred coding my own scripts because I tought starting to use a framework would force me into a practise I could later regret (use of coding rules imposed by the framework itself, efficiency in the long term…). This was my first thought before ever trying one.

After my first Code Igniter experience, I have changed my mind and now think they make my life easier. And even if the framework I have chosen was no longer maintained, I could easily choose another framework to develop my new websites without changing so much my programming practise.

In my opinion, the following features make a good and efficient framework :

  • a PHP 5 framework
  • an MVC (Model-View-Controller) architectural pattern
  • a set of helpers (scripts that save you time for managing urls, or form elements, for example)
  • the possibility to script your own controllers, libraries, helpers
  • a well-documented practise
  • a lightweight (thus fast loading) system

It’s all a question of powers…

PHP5-powered

The power of PHP5 frameworks like Kohana is they’re strictly OOP (Object Oriented Programming), a proof of better programming practises. Code Igniter is partly written in PHP5, helpers including PHP4 functions.

MVC-powered

The power of an MVC architectural pattern is that it allows you to structure your code : your PHP scripts are not mixed anymore with Html and MySql requests as they would be in a home-made script without a framework. In a Model-View Controller pattern,you may define specific folders to hold your controller classes, your models classes and your views. Practically speaking, a web page will call a controller. This controller may define variables or call a model to retrieve data from a database, for example. So, the model will execute the MySql query, get the data from the database and return the result to the Controller. The controller will then pass the results to the view which is like an Html template.

MY_Controller.php : writing your own controllers

A PHP Framework lets you write your own controllers. In Code Igniter, the main controller is in /system/libraries/Controller.php. Code Igniter lets you write your own controller. You should name it /system/application/libraries/MY_Controller.php. This file contains a MY_Controller class that actually extends the main Controller class :

<?php if ( ! defined(‘BASEPATH’)) exit(‘No direct script access allowed’);
class MY_Controller extends Controller {
public function __construct() {
parent::__construct();
$this->load->helper(array(‘my_html’,'my_url’,'my_date’));
}
}
?>

My personal controller here loads the Main Controller constructor and then loads some helpers. These helpers are not the default CI helpers that can be found in /system/helpers/ but my own rewritten helpers : my_html_helper.php, my_url_helper.php and my_date_helper.php which are copies of the original helpers to which I have added some personal functions.

Scripting websites with Code Igniter has led me to write scripts which are very short, easy to manage and wonderfully efficient.

Webliography

Code Igniter

Code Igniter Video Tutorials

Code Igniter Tutorials

Derek Allard’s tutorials

Kohana

PHP forms thank-you pages

Google Analytics filter page names

I used to script login forms, contact forms, registration forms, order forms,… that were displayed in a login.htm, contact.htm, register.htm, order.htm with the forms action attributes set to the same url. Once submitted, PHP checked the form fields and displayed a thank you message (or an error message) within the same html page. This is not the right way to do it if you want to track users that filled in the form correctly or the ones who abandoned filling in the form or the ones who filled it incorrectly and submitted it again…

If you want to get deeper into the statistics of your website thanks to Google Analytics tools, for example, it may be wiser to generate login-thank-you.htm, contact-thank-you.htm, register-thank-you.htm or order-thank-you.htm. Once the form is submitted, have PHP check what has to be checked through a form validation class (check for required fields, email fields,…) and redirect the user to the thank-you-page to display the thank you message if he filled it in correctly. If this is not the case, redirect him to a contact-error-email.htm page for example.

After some time, you’ll go to Google Analytics / Content / Full report : use the filter field at the bottom of the page to filter by “containing keyword” or “not containing keyword” with a value like ‘thank-you’ to display all pages containing those keywords and get statistics about those pages.

ClickHeat – Crazy Egg alternative

clickheat

Crazy Egg provides you with Heat Mapping tools that lets you visualize your visitors’ clicks on your website. You need to register for that tool and pay $9 to $99 according to the pack you choose.

Another powerful tool is Clickheat. It’s free and Open Source. It is easy to configure and will take you 30 seconds of implementation.

You simply upload the extracted clickheat directory to the root of your website. Then follow the configuration wizard. You’ll have to set the domain names that are allowed to trigger a clickheat indexation and to set a login-password to get to the results. Then login to http://www.mydomain.com/clickheat/ and generate the javascript code to be pasted in the head of your index.php file.

After some visitors’ clicks, you’ll get the following results :

clickheat

Local Web Servers

As a webdeveloper on a Server Linux distro as OpenSuse, you simply have to activate and configure Apache and MySql.

If you’re working on a Windows station, you need to install a web server application to execute your PHP scripts.

Years ago, I used to work with EasyPhp, which stopped to evolve for some time 3 or 4 years ago. So I switched to WampServer at the time, which is still the one I use daily. It provides you with Apache, Mysql, Phpmyadmin (php scripts to handle your databases). It is really easy to install and has the up-to-date versions of PHP and MySql available.

I’ve recently come across ZMWS. ZazouMiniWebServer is a standalone webserver that can be installed on a Windows machine. It is released with PHP4 and PHP5 but you could also add handlers to make it work with ASP, PERL,… :

I’ve tested ZMWS on a USB stick as a portable web server.

You could distribute your PHP web project on a CD and have an auto-browsable dynamic website.

The website of the ZMWS Open Source project provides you with documentation and a forum.

Webliography :

ZazouMiniWebServer

WampServer

Polymorphism in PHP5

Polymorphism is one of the major Object Oriented Programming features (OOP) mature programming languages like C++ or JAVA implement. Though OOP experts may reasonably claim that PHP is not strictly Object Oriented (since nothing is an object in PHP unless you create it), the language has become polymorphism-ready with version PHP5.

What is polymorphism?

Its principle lies in its name : [poly-] means much, many and [-morphism] means forms, states. So, programming in a polymorphic way allows the developer to have one single object behaving differently according to its type. Practically speaking, the developer writes a base class which defines the basic features of the object and as many subclasses as necessary to build the different behaviours the object may have. Calling the same method will have the object’s children behave differently.

Here is a simple example of polymorphic design with a vehicle object. The Vehicle class has as attributes pieces of information that are found in every kind of vehicle : make, registering date, price :

Class Vehicle {

 

protected $make;
protected $date;
protected $price;
public function __construct($make,$date,$price) {
$this->make = $make;
$this->date= $date;
$this->price= $price;
}

}

Then create the plane class and the car class that will inherit the vehicle class :

Class Car Extends Vehicle {

private $engine;
private $mileage;
public function __construct($array) {
parent::__construct($array["make"],$array["date"],$array["price"]);
$this->engine = $array["engine"];
$this->mileage = $array["mileage"];
}
}

Class Plane Extends Vehicule {

private $type;
private $seats;
public function __construct($array) {
parent::__construct($array["make"],$array["date"],$array["price"]);
$this->type = $array["type"];
$this->seats = $array["seats"];
}

}

Structuring your classes that way allows you to make your OOP more abstract, thus more flexible. Changing a specific type of object after production time will be made easier and more stable.

Objects without polymorphic design

Imagine you want to create a form generator class without polymorphism :

<?php
class Form {
public function genInput($type,$name,$value) {
SWITCH($type) {
Case”text”:
print “<input type=\”text\” name=\”".$name.”\” value=\”".$value.”\” />\r\n”;
break;
Case”textarea”:
print “<textarea name=\”".$name.”\”>”.$value.”</textarea>\r\n”;
break;
}
}
}
$form = new Form(); //initialize form object
$form->genInput(“text”,”field1″,”Lorem ipsum…”); //prints <input type=”text” name=”field1″ value=”Lorem ipsum…” />
$form->genInput(“textarea”,”field2″,”Lorem ipsum…”); //prints <textarea name=”field2″>Lorem ipsum…</textarea>
?>

Objects with polymorphic design

In order to allow an object to have several forms, be it a text field or a textarea field, we need to make our class more abstract.
So, we will create an interface, an abstract class and several subclasses (depending on the type of field).
The interface will define the functions the object will perform, no matter what the function will do. But the interface will not define any functionality of the methods, it only names them. The precise functionalities of the genInput() function will be defined in every subclass.

//INTERFACE
interface Form {

public function genInput();

}

The abstract class is a class that we only partially implement. It contains abstract methods (functions) that provide some common functionalities. The rest of the necessary functionalities will be defined in the subclasses.
//ABSTRACT CLASS
abstract class Input {
protected $name;
protected $value;
public function __construct($name,$value) {
$this->name = $name;
$this->value = $value;
}
}
Then the derived classes will call the method set in the abstract class (parent::__construct()) and the method named in the interface (genInput()) :
//TEXT & PASSWORD & HIDDEN INPUT
class Text extends Input implements Form {
private $type;
private $size;
public function __construct($array) {
parent::__construct($array["name"],$array["value"]);
$this->type = $array["type"];
$this->size = $array["size"];
}
public function genInput() {
return “<input type=\”".$this->type.”\” name=\”".$this->name.”\” value=\”".$this->value.”\” size=\”".$this->size.”\” />\r\n”;
}
}
//TEXTAREA INPUT
class Textarea extends Input implements Form {
private $cols;
private $rows;
public function __construct($array) {
parent::__construct($array["name"],$array["value"]);
$this->cols = $array["cols"];
$this->rows = $array["rows"];
}
public function genInput() {
return “<textarea name=\”".$this->name.”\” cols=\”".$this->cols.”\” rows=\”".$this->rows.”\”>”.$this->value.”</textarea>\r\n”;
}
}
This is how you can create a text field :
$object = new Text(array(“type”=>”text”,”name”=>”form_id”,”value”=>$item,”size”=>20));
$hidden = $object->genInput();
Generating a textarea field looks like this :
$object = new Textarea(array(“name”=>”form_txta”,”value”=>$item ,”cols”=>20 ,”rows”=>5));
$textarea = $object->genInput();
Webliography :

Abstract classes and interfaces
Devshed polymorphism in PHP5 article
PHP5 Tutorial : Abstract classes and Interfaces
PHP5 OOP abstract classes & interfaces

Empty $_POST array issue

I twice had the following issue with my custom CMS : posting a form with post method did not result in sending any data to the form action page. Printing $_POST :

print_r($_POST);

always resulted in displaying the following on my page :

array();

My .htaccess file simply set Magic_quotes to 0, Register_Globals to 0 and PHP_VER to 5. Then I have 301 redirects :

SetEnv MAGIC_QUOTES 0
SetEnv REGISTER_GLOBALS 0
SetEnv PHP_VER 5
Options +FollowSymlinks -MultiViews
RewriteEngine on
rewritecond %{http_host} ^mydomain.be [nc]
rewriterule ^(.*)$ http://www.mydomain.be/$1 [r=301,nc]
And the empty $_POST problem comes from the 301 redirect. Indeed, my form was posted to http://mydomain.be/index.php thanx to a “base” tag set inside the head of my document :
<base href=’http://mydomain.be/’ />
and the form action looks like the following :
<form method=’post’ action=’./index.php’>
With the 301 redirect that has mydomain.be point to www.mydomain.be and the form pointing to mydomain.be/index.php, Apache does not associate $_POST with www.mydomain.be but with domain.be. Hence the empty $_POST issue.
I hope this will spare you some debugging time.
Webliography :

MySql Fulltext search

Scripting an internal search engine requires :

  • a client form to submit requests
  • database fields to search posted keywords
  • database queries to match the form request

I want to focus on the database requirements to make such a search.

I used to build queries with sub-queries of the following type :

SELECT id,title,content from table_name WHERE title LIKE ‘%”.$search.”%’ OR content LIKE ‘%”.$search.”%’;

which performs a kind of regular expression search and can lead to MySql resource problems (large number of rows parsed, large number of results found,…).

Under MySql, a FULLTEXT search may solve this resource problem and achieve the search since it performs the search in indexed data. This feature allows

  • stopwords (words which will be excluded by the server itself)
  • boolean searches (using + or -, for example, to add to search or substract from search)
  • relevancy scoring

Type of table that allows fulltext indexing

FULLTEXT search is available in MyISAM tables. Though the InnoDB Storage Engine offers transactions (commit and rollback error handling), the fulltext search feature is absent. So, let’s stick to MyISAM tables.

You can define a FULLTEXT index on one or more table fields of types CHAR, VARCHAR and TEXT. As stated in MySql reference on the subject, creating rows in a table with fulltext index will take longer. So, if lots of rows need to be treated with a fulltext index (a fulltext index will be most efficient in this very case), it is better to first insert the rows, then create the index.

You can add a fulltext index on the 2 fields “title” and “content” with the following query :

ALTER TABLE table_name ADD FULLTEXT index_name (‘title’ , ‘content’);

Once the table has a fulltext index defined, the query can be the following :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘”.$search.”‘);

The fields in the MATCH statement have to be the same as defined in the table’s fulltext index, and you can define a fulltext index on one or more field.

Fulltext search parameters

MySql is configured with a set of automatic parameters :

  • the results are ordered by the relevancy score MySql gives to each row found
  • words with less than 4 characters will be ignored
  • the fulltext index contains complete words only
  • depending on the language MySql is set to, a list of stopwords will be automatically ignored
  • the score for each row depends on the number of occurences of that word in the table. If a word occurs in more than half the lines, it will be ignored

Changing these default parameters will only be possible if your MySql Web Server allows you to modify those parameters.

Boolean mode search

The MATCH… AGAINST… statement allows you to perform a Boolean search :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘”.$search.”‘ IN BOOLEAN MODE);

MySql will then return all rows matching the searched words even if more than 50% of the rows contain the keyword.

Boolean searches allow you to use operators like + or – to force MySql to return rows containing ‘works’ and hide rows containing ‘tables’ :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘+works -tables’ IN BOOLEAN MODE);

You can also use the * operator as a wildcard in Boolean mode to search part of a word :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘peopl*’ IN BOOLEAN MODE);

Webliography :

MySql FULLTEXT search reference

MySql FULLTEXT search – Boolean mode reference

PHP5 to watch over MySql queries

If the developer wants to write more efficient MySql queries, he may want to display all the queries a web page has generated and the time the whole script took to output its Html result. This is useful while developing. Once the website is launched, you may get rid of these script lines (just comment them with // or /* */) in order not to take extra CPU time.

Changes in the MySql class

In order to count MySql queries generated by a web page and display the list of them, he should add a static variable to his MySql Class :

public static $queries = array();

When initializing the class, the static variable $queries becomes an empty array. Each time a query will be sent to the class for execution, array $queries will be incremented this way :
array_push(self::$queries,$query);
Before destroying the $mysql object
return count(self::$queries);
will return the number of MySql queries generated by a Web page.
return self::$queries;
will return the array containing all MySql queries for the current web page.

PHP script execution time

In order to check how long it takes a script from start to end, just work with microtime :
<?php
//top of PHP script
$start_time = microtime(true); // >= PHP 5.0.0 gives microtime as a float
//bottom of PHP script
$end_time = microtime(true);
$total_time = $end_time – $start_time;
?>

Preventing [F5] submission

Form handling may require the script to identify if the user has first submitted the form or if he has pressed the key [F5] to submit the form again. Indeed, if form submission triggers an insert into database query, you may want the insertion not to happen twice.

Basically, the only thing to do is to post a hidden field (called “process” in my example) containing the Unix timestamp of the time when the form was generated (microtime(true) in PHP). Then write a short PHP function that will

  • compare the posted process value with a Session variable called “process”. If the posted process variable equals the Session value, trigger the error : “Please use submit button”
  • if it doesn’t, process form, then store the posted process time in a Session variable ($_SESSION["process"]).