Showing posts with label trick. Show all posts
Showing posts with label trick. Show all posts

Thursday, October 29, 2009

Coding standards: converts PHP4 style constructors to PHP5 one

A quick way to convert all occurences of old PHP4 constructors like in:

class XYZ {
    /**
     * Constructor of XYZ.
     */
    
function XYZ() {
    }
}


to PHP5's __construct():

class XYZ {
    /**
     * Constructor of XYZ.
     */
    
function __construct() {
    }
}


is done using a quick Perl Regular Expression like in the following Linux shell command:

$ perl -i -e 'undef $/;while($_=<>){s/^(class\s+(\w+)\b.*^\s+function\s+)\2\b/\1__construct/gms;print $_;}' $(find -name "*.php")

Once you have done converting your constructors definition you still may have to fix constructor calls like:

MyClass::MyClass();
parent::MyClass();
$this->MyClass();


First of all, you need to know what classes to search for, because you would be crazy to work without a software revision control tool (like Git, SubVersion, Mercurial,...), use the diff output to extract the changes you just made with previous command. Next command extract class names from the SubVersion diff output:

$ svn diff | grep "^-[^-]" | sed -r "s/-\s*function\s*([a-zA-Z0-9_]*).*$/\1/"

The regular expression to convert all three types of constructor call is the following one:

s/((?:parent|\2)::|\$this->)(Class1|Class2|Class3|...)\b/parent::__construct/g

To embed this in the regular expression needed, we modify the output of the command with echo to join all the lines on one line and sed to replace this space separated list of classes with pipes (|):

$ echo 's/((?:parent|\2)::|\$this->)('$(echo $(svn diff | grep "^-[^-]" | sed -r "s/-\s*function\s*([a-zA-Z0-9_]*).*$/\1/") | sed 's/ /|/g')')\b/parent::__construct/g'

(green: regular expression, blue: command extracting class names, orange: joining lines with pipes)

Last step is to use this regular expression with perl:

$ perl -pi -e 's/((?:parent|\2)::|\$this->)('$(echo $(svn diff | grep "^-[^-]" | sed -r "s/-\s*function\s*([a-zA-Z0-9_]*).*$/\1/") | sed 's/ /|/g')')\b/parent::__construct/g' $(find -name "*.php")

For the one-liners out there, here is the full command you might execute (SubVersion based):

$ phpfiles=$(find -name "*.php") && perl -i -e 'undef $/;while($_=<>){s/^(class\s+(\w+)\b.*^\s+function\s+)\2\b/\1__construct/gms;print $_;}' $phpfiles && perl -pi -e 's/((?:parent|\2)::|\$this->)('$(echo $(svn diff | grep "^-[^-]" | sed -r "s/-\s*function\s*([a-zA-Z0-9_]*).*$/\1/") | sed 's/ /|/g')')\b/parent::__construct/g' $phpfiles

I leave as exercise the reader to port these Linux commands to Microsoft Windows' native command shell.

This article assumes your classes are always declared with the class keyword starting at the beginning of the line and that your files have the .php extension.
Modify the commands to match your standards.

Sunday, June 8, 2008

Apache as an MVC controller

My colleagues know I am not a big fan of PHP frameworks, this is probably why I somewhat agree with the no-framework PHP MVC framework of Rasmus Lerdorf or with the idea of Akash Mehta thinking, to an extent, that PHP IS a framework. Keeping this global picture in mind, let me show you how Apache could take the C of MVC.

Because not mixing the business logic and the presentation is always a good idea, this leads up to have two types of PHP files: those with business logic only (the M of MVC), whether it's OOP or procedural programming, and those that are just templates containing mostly HTML (the V of MVC).

Mapping URLs to actions/views is the job of Apache, by default, it will look at your URL and make the match with the corresponding script on the filesystem. This is why I consider Apache as a controller.

Nice URL

One of the goal of a web controller is also to provide nice URL's to your application, I will present you here two methods to achieve this with Apache exclusively.

Method #1

It is quiet common Apache being configured with

DirectoryIndex index.php
Knowing this you can architect your directories with the same structure as your URLs always with an index.php file handling the request. Let's take the example of a web project management application having a dedicated page for projects and users. Your URLs might be:
  • http://project-management/projects/?id=xxx
  • http://project-management/projects/remove/?id=xxx
  • http://project-management/users/?id=xxx
  • http://project-management/users/add/
Handling this could be done using the filesystem layout as shown on the following picture:

Method #2
Another method, which may provide you even nicer URLs, relies on Apache's mod_rewrite. Let's change our URLs a little bit:
  • http://project-management/projects/xxx
  • http://project-management/projects/remove/xxx
  • http://project-management/users/xxx
  • http://project-management/users/add/
To handle this, I use a flatter filesystem layout: with the following rewrite rules:

RewriteEngine On

# Preventing access to php files directly
RewriteRule \.php$ /NotFound [L]

# Simple URL mapping
RewriteRule ^/$ /index.php [L]
RewriteRule ^/users/$ /ListUsers.php [L]
RewriteRule ^/projects/$ /ListProjects.php [L]
RewriteRule ^/users/add/$ /AddUser.php [L]
RewriteRule ^/projects/add/$ /AddProject.php [L]

# URL mapping with captured IDs
RewriteRule ^/users/([0-9]+)$ /ViewUser.php?id=$1 [L]
RewriteRule ^/projects/([0-9]+)$ /ViewProject.php?id=$1 [L]
RewriteRule ^/users/remove/([0-9]+)$ /RemoveUser.php?id=$1 [L]
RewriteRule ^/projects/remove/([0-9]+)$ /RemoveProject.php?id=$1 [L]

The first thing done is preventing direct access to PHP files, this is not mandatory but it adds some more security. The only way to reach the desired script is to match strictly the regular expression making some input filtering at the same time. Then comes the real and interesting rewrite rules. First ones are very simple mapping while the 4 last ones takes care of extracting a numerical ID from the URL and passing it to PHP as a $_GET parameter.

What do you think about such approach? Please, leave some comments :)

Monday, May 26, 2008

Hierarchical data in MySQL (and other RDBMS)

Introduction

There are lot of cases we want to store hierarchical data into relational database instead of hierarchical ones like XML databases. Several approaches exist and are already well explained, the most well known are the adjacency list and the nested set models. After briefly introducing those models I will present an extension to the first one making it much more usable. This extension has already been presented by Joe Celko under the name Path enumeration

The adjacency list model

The most common way to store such data is using the adjacency list model as introduced by former IBM fellow Edgar F. Codd (the father of relational database theory):

idnameboss
1AnneNULL
2Bernard1
3Charlie1
4Delphine3
5Elodie3
6Fanny3
7Georges5

Such model respects fully the relational idea based on primary and foreign keys, but this system shows his limit when we have to retrieve the full path to a node: "Anne > Charlie > Elodie > Georges" or when we need all people working below Charlie. Such question typically requires recursion with many queries which may become very slow:

-- Path to Georges?
SELECT * FROM people WHERE id = 7; -- boss = 5
SELECT * FROM people WHERE id = 5; -- boss = 3
SELECT * FROM people WHERE id = 3; -- boss = 1
SELECT * FROM people WHERE id = 1; -- boss = NULL (STOP)

-- People under Charlie?
SELECT * FROM people WHERE boss IN (3); -- id = 4,5,6
SELECT * FROM people WHERE boss IN (4,5,6); -- id = 7
SELECT * FROM people WHERE boss IN (7); -- no results (STOP)

The nested set model

The second popular approach is to make use of the depth first traversal algorithm to assign a left and right number to any node of the tree:

idnamelftrgt
1Anne114
2Bernard23
3Charlie413
4Delphine56
5Elodie710
6Fanny1112
7Georges89

This model is algorithmically beautiful! It solves in an elegant way the problem of running multiple queries to retrieve hierarchical information:

-- Path to Georges?
SELECT * FROM people WHERE 8 BETWEEN lft AND rgt ORDER BY lft;

-- People under Charlie?
SELECT * FROM people WHERE lft BETWEEN 4 AND 13 ORDER BY lft;

While this model is very good at retrieving hierarchical information, it is somewhat more complex and less competitive at updating the hierarchy (inserting, moving or removing nodes) since it requires to update several records even if it is feasible in one query. Not to mention that this model is not relational at all and does not prevent through integrity constraints the accidental removal of a boss! However, this is easily circumvented by adding the boss column and then mixing both models.

Path enumeration model

The path enumeration model is based on the adjacency list one, the idea is quite simple: add a column materializing the full path (which is unique) to your node.

idnamebosspath
1AnneNULL/1/
2Bernard1/1/2/
3Charlie1/1/3/
4Delphine3/1/3/4/
5Elodie3/1/3/5/
6Fanny3/1/3/6/
7Georges5/1/3/5/7/

The path is always computed by taking the path of the parent node concatenated with "<ID>/". Retrieving the path to Georges or people working for Charlie is a kid's game:

-- Path to Georges?
SELECT path FROM people WHERE id = 7; -- path = /1/3/5/7/
SELECT * FROM people WHERE id IN (1,3,5,7) ORDER BY path;

-- People under Charlie?
SELECT * FROM people WHERE path LIKE '/1/3/%' ORDER BY path;

To benefit from all the speed of this solution, you should have a UNIQUE KEY on your path column. Unfortunately it isn't (yet?) possible to know the assigned auto-increment ID inside an insert-trigger using MySQL, it is then mandatory to work in two phases while inserting a record. A first solution is to insert dummy data in the path while taking care of the UNIQUE KEY constraint and then updating the record using LAST_INSERT_ID(). My preference goes to using another table (people_tree) with a 1..1 relation. Here is an example:

-- Adding Helena below Bernard
INSERT INTO people (name, boss) VALUES ('Helena', 2);
INSERT INTO people_tree
SELECT p.id, CONCAT(pt.path, p.id, '/') FROM people p JOIN people_tree pt ON p.boss = pt.id WHERE p.id = LAST_INSERT_ID();

Of course, this complexity may be hidden in a stored procedure.

Monday, October 29, 2007

PHP interactive shell


A feature of PHP I use in my day to day life as developer — but that lots of PHP developers seems to ignore — is the Interactive shell of PHP:

$ php -a

Like with Python, Ruby, Perl and many others, PHP benefits also of this feature that lets you try copy/pasted code, try some functions/algorithms before integrating them directly into a code or maybe just trying to find some weird behavior of PHP itself.

Like a real shell, the PHP interactive shell comes with:

  • Tab completion
  • History
  • Integration of all the interesting shortcuts to edit the current line (PG+Up/Down, ALT+Backspace, ALT+Left/Right, CTRL+D,...)
  • etc.

Next time you will:

  1. Create a PHP file into your DocumentRoot
  2. Open it with your favorite editor
  3. Fire up your browser to reach your newly created file
  4. Show the source code of the generated page

Maybe this day will you find command line easier :)

Want to view a demo of all this ? Sure:

Monday, September 10, 2007

Are you a PHP 5 guru?

If you are able to answer the following questions, you are certainly one of them! Consider the following script:
<?php
error_reporting
(E_ALL | E_STRICT);
$test[isset($test)] = $test;
var_dump($test);
?>

  1. How many notices are displayed?
  2. What is the output?
Ok, you replied with:
  1. 1 notice (Notice: Undefined variable: test in ...)
  2. array(1) {
    [0]=>
    NULL
    }
Congratulations! You just failed your Zend Engineer Certification.
Joke aside, correct answers were:
  1. 0 notice
  2. array(1) {
    [0]=>
    array(1) {
    [0]=>
    *RECURSION*
    }
    }
Well, in PHP 4 you would be in the right, but PHP 5 introduces a new feature that let you construct a kind of recursive array... More interesting is that if you used an isset on the right part of the assignment it would return you false!