PHP / Zend / www.elcome!

PHP dream development environment

Have you ever dreamed to have a set of cross-platform PHP development tools that not only would make your life easier when programming from both Windows and Linux / Mac OS X but also would have a self sufficient full-featured environment to build a new Facebook from scratch ? :) Sounds like a swiss army knife for PHP developers? Well, now you have it! I’m not going to describe in detail the features of each tool, but rather to summarize them.

Swiss Army Knife

Eclipse PDT (PHP Development Tools) + Xdebug is a complete IDE for PHP which has a nice add-ons market for extra features you might need for coding. The only drawback is that it tends to eat lot of resources and slows your computer down, but nowadays the RAM memory is very cheap, so you’re good to go.

Navicat for MySQL is a MySQL IDE that simply is the best choice over phpMyAdmin and MySQL Workbench. Althought the Standard Edition (for reporting capabilities consider buying the Enterprise Edition) costs $129, it simply outcomes the price. The only important feature which I didn’t found in this tool is the tracing capability, but Jet Profiler for MySQL does this for you.

Jet Profiler for MySQL is a “real-time query performance and diagnostics tool for the MySQL database server” (http://www.jetprofiler.com/) that is a bit expensive: $399. If the price isn’t your only criteria for choosing development tools, this is the best choice..and the only one, because the alternatives seem to be simple tools for reading MySQL logs, which is not what you wished for.

If you are using a Version Control System, like Git, you can use an Eclipse add-on, or you might as well use SmartGit for $69, which is an easy-to-use GUI Git client.

Now for the File Manager, here is a delicate subject, because there is no cross-platform complete solution. For example, if you are using Windows, Total Commander is the best tool you can get and it only costs $46. For Mac OS X, Vladimir Stanciu which was a colleague of mine at IBM and an Objective C ninja, is promising a lot with his Xmander app – hope to see lot more from it in the future. For Linux, I don’t have the slightest idea of a good File Manager.

The web browser that fits best for PHP development and for the future in general :) is Google Chrome. It’s fast, stable, web store enabled and now it has Mozilla’s most wanted development tool: FireBug which at the time of writing is only at it’s “Lite” version.

With all this tools, you can consider yourself platform independent and ready to code (not just ready to edit text a.k.a. Vim). What else can you dream to have, ehh?

Happy coding!


Closure in PHP

Q: Is Closure the same thing as Anonymous Functions, in PHP?

It appears that yes…at least from what is written on http://php.net/manual/en/functions.anonymous.php

Closure in PHP

A: A definition for Closure, applicable to all the programming languages that supports it including PHP, can be found here: http://en.wikipedia.org/wiki/Closure_(computer_science) – no sign of “Anonymous Functions”, just in the context of “closure is often mistakenly used to mean anonymous function” :)

However, on http://php.net/manual/en/functions.anonymous.php, “Example #3 Closures and scoping” section you can find a good code example on Closure. I’ll point out the things that matters:
- the parameter &$total is passed by reference, so that the value of the parent-local variable $total could keep the sum of all the products in the cart;
- the keyword “use” if used together with Anonymous Functions, allows importing variables into the scope of the function;
- $callback = function ($quantity, $product) use ($tax, &$total)… creates the Closure, which means that the local variable $total is “closed” in the same scope as the Anonymous Function and persists even after returning from it.

The conclusion: php.net is not so evil after all ^^

See ya!


Multiple Inheritance suddenly more easy in PHP (than in C#)

Nor C#, neither PHP supports multiple inheritance, but in both programming languages there are workarounds. A nice article for simulated multiple inheritance in C# you can find here http://www.codeproject.com/KB/architecture/smip.aspx, and in PHP here http://www.serversidemagazine.com/php/how-to-inherit-from-multiple-objects-workaround/.

However, today I realised that none of the problems that can be caused by strong-typed programming languages in multiple inheritance can ever be found in PHP (doh!), which is a weak-typed programming language. Let me show you why.

Since declaration type doesn’t exists in PHP, you can’t have a construction like this:

BClass $var_a = new AClass();

instead, the correct syntax in PHP is:

$var_a = new AClass();

So keeping this in mind, in PHP if two classes BClass and CClass redefine a method m1() from AClass, and DClass inherits from BClass and CClass,

then you’ll never have to worry about constructions like:

CClass $var_d = new DClass();
$var_d->m1();

..because in PHP they simply not exists :) Anyway, it would be interesting in the future to see in PHP construction like in JavaScript: BClass.prototype = new AClass();


Custom implementation for Wall, Messages and Like Button

The ubiquitous “Like” button…it’s nice, easy to use and pretty damn useful.

If you ever wondered how can you implement it on your website..well, it’s very easy and it’s called Facebook Like Button: http://developers.facebook.com/docs/reference/plugins/like/, but that’s not what I want to talk about here.

Now, imagine the next situation: you have a simple web application in which each user can have a Wall and can post on each other’s or on it’s own wall. What? Facebook has this functionality too :) ?

Currently being my first language of choice, I will explain the implementation in PHP / MySQL. First of all, you should have a simple database structure, like the one below:

Just to be sure you understood the structure:
- a user (wall_entries.user_id) can post a message (wall_entries.message) on other’s or on it’s own (wall_entries.to_user_id) wall;
- the message (wall_entries_likes.wall_entry_id) can be liked/unliked by others or by the author (wall_entries_likes.user.id);
- the message can have one or more replies (wall_entries_messages.message) from others or from the author (wall_entries_messages.user_id);
- the replies (wall_entries_messages_likes.wall_entry_id) can be liked/unliked by others or by the author (wall_entries_messages_likes.user_id)

Let’s take a look at the SELECT statement which retrieve all the messages and likes that belongs to the first user Wall:

SELECT
  `wall_entries`.`id`,
  `wall_entries`.`user_id`,
  `users`.`name` AS `user_name`,
  `wall_entries`.`to_user_id`,
  `u2`.`name` AS `to_user_name`,
  `wall_entries`.`message`,
  (
    SELECT
      GROUP_CONCAT(user_id)
    FROM
      wall_entries_likes
    WHERE
      wall_entry_id = wall_entries.id
    GROUP BY
      wall_entry_id
  ) AS `user_ids_liked`,
  (
    SELECT
      GROUP_CONCAT(users. NAME)
    FROM
      wall_entries_likes
    INNER JOIN users ON users.id = wall_entries_likes.user_id
    WHERE
      wall_entry_id = wall_entries.id
    GROUP BY
      wall_entry_id
  ) AS `user_names_liked`
FROM
  `wall_entries`
LEFT JOIN `users` ON users.id = wall_entries.user_id
LEFT JOIN `users` AS `u2` ON u2.id = wall_entries.to_user_id
WHERE
  (to_user_id = '1')

Now, let’s take a look at the SELECT statement which retrieve all the replies and likes of all the messages that belongs to the first user Wall:

SELECT
  `wall_entries_messages`.`id`,
  `wall_entries_messages`.`wall_entry_id`,
  `wall_entries_messages`.`user_id`,
  `users`.`name` AS `user_name`,
  `wall_entries`.`to_user_id`,
  `u2`.`name` AS `to_user_name`,
  `wall_entries_messages`.`message`,
  (
    SELECT
      GROUP_CONCAT(user_id)
    FROM
      wall_entries_messages_likes
    WHERE
      wall_entry_id = wall_entries_messages.id
    GROUP BY
      wall_entry_id
  ) AS `user_ids_liked`,
  (
    SELECT
      GROUP_CONCAT(users. NAME)
    FROM
      wall_entries_messages_likes
    INNER JOIN users ON users.id = wall_entries_messages_likes.user_id
    WHERE
      wall_entry_id = wall_entries_messages.id
    GROUP BY
      wall_entry_id
  ) AS `user_names_liked`
FROM
  `wall_entries_messages`
INNER JOIN `wall_entries` ON wall_entries.id = wall_entries_messages.wall_entry_id
LEFT JOIN `users` ON users.id = wall_entries_messages.user_id
LEFT JOIN `users` AS `u2` ON u2.id = wall_entries.to_user_id
WHERE
  (wall_entries.to_user_id = 1)

So far, you can clearly see that Tom Cat who posted on John Doe’s Wall got 2 replies from Indiana Jones and John Doe and only the reply from Indiana Jones was liked by two people: John Doe and Tom Cat! Whoaa!

Let’s take a look at the View Helper which generates the HTML near the Like button:

/**
* Generate the HTML that will be displayed near the Like button
*
* @return string
*/
public function wallEntryLikes($item, $userId)
{
    $isLikedByCurrentUser = false;
    $wallEntryLikesHtml = '';

    //Check if the wall entry is liked by current user
    if(XX_Utilities_Like::getInstance()->isLikedByCurrentUser($item, $userId))
	{
        $wallEntryLikesHtml .= 'You';
        $isLikedByCurrentUser = true;
    }

    $userIds = '';

    //Check if other users like the current wall entry
    if($item->user_ids_liked) {
        $userIds = explode(',', $item->user_ids_liked);
        $userNames = explode(',', $item->user_names_liked);

        //The wall entry is liked by the current user and one or multiple users
        if($isLikedByCurrentUser) {
            if(count($userIds) - 1 == 0) {
                $wallEntryLikesHtml .= ' like this';
            } else if(count($userIds) - 1 == 1) {
                $wallEntryLikesHtml .= ' and '
                . $userNames[array_search($userId, $userIds)]  . ' like this';
            }
            else {
                $wallEntryLikesHtml .= ' and ' . (count($userIds) - 1) . ' others like this';
            }
        }
        //The wall entry is liked by one or multiple users
        else {
            if(count($userIds) == 1) {
                $wallEntryLikesHtml .= $userNames[0] . ' like this';
            } else if(count($userIds) == 2) {
                $wallEntryLikesHtml .= $userNames[0] . ' and '
                . $userNames[1] . ' like this';
            }
            else {
                $wallEntryLikesHtml .= $userNames[0] . ' and '
                . (count($userIds) - 1) . ' others like this';
            }
        }
    }

    if ($wallEntryLikesHtml) {
        return '' . $wallEntryLikesHtml . '';
    }

    return '';
}

Happy coding!


“Method Overloading” in PHP

While I was testing some features of PHP in general, I stopped and stared at one OOP principle that seems not to be supported by PHP: method overloading. Because PHP is a weak-typed programming language, method overloading will never be supported, but there is always a workaround.

Example for “method overloading” (another monolithic PHP approach of doing things) in PHP:

function someFunction($param1, $param2 = null)
{
  if(is_array($param1) && isset($param2))
  {
    echo ‘bla’;
  }
  else if(is_int($param1) && isset($param2) && is_string($param2))
  {
    echo ‘bla bla’;
  }
  //else if, else if, else...and so on and so forth
}

The more overloads, the more spaghetti code with lots of if-else if-else statements in a single big boned (not fat! a.k.a Cartman form South Park ;) function.


Between Adjacency List Model and Nested Set Model

There is a time in your project when you need to represent some hierarchical data and the database engine happens to be MySQL and not MSSQL which by the way supports recursive CTEs! and recursive functions.

Now, there is a simple and easy way to represent the hierarchical data, which is fast at insert/update operations and slow at select operations (recursion bleah!) :

Category(
  id  INT, PRIMARY KEY, AUTO INCREMENT
  name  VARCHAR(50), NOT NULL
  parent  INT, NULL
)

..and there is the opposite of the first solution (no recursion, fast select):

Category(
  id  INT, PRIMARY KEY, AUTO INCREMENT
  name  VARCHAR(50), NOT NULL
  lft  INT, NOT NULL
  rgt  INT, NOT NULL
)

I will concentrate on the second solution, and just to be sure you get the idea, I will attach an image with a full structure of categories:

Categories Structure

The blue text represents the depth of the category and the red text represents the lft and rgt database fields. The green text represents the number of products in the category. You’ll see that if “Portable electronics” has 4 products, the sum of it’s child categories must be equal to 4 (“MP3 Players” + “Flash” + “CD Players” + “2-Way Radios”).

Before you choose the second solution to implement, you should be aware of the complexity of the queries and the conditions for the CRUD operations.

A quick look at the:
- update condition, when moving a category in another category (PHP syntax):
Update Condition

- select statement for categories and number of products (MySQL sytanx):

SELECT
  `parent`.`id`,
  `parent`.`name`,
  `parent`.`visible`,
  (
    SELECT
      COUNT(id)
    FROM
      categories
    WHERE
      rgt = lft + 1
    AND id = parent.id
  ) AS is_leaf,
  (
    SELECT
      (COUNT(parent_sub. NAME) - 1)
    FROM
      categories AS node_sub,
      categories AS parent_sub
    WHERE
      node_sub.lft BETWEEN parent_sub.lft
    AND parent_sub.rgt
    AND node_sub.id = parent.id
    GROUP BY
      node_sub.id
    ORDER BY
      node_sub.lft
  ) AS depth,
  COUNT(products.id) AS products_count
FROM
  `categories`
CROSS JOIN .`categories` AS `parent`
INNER JOIN .`products`
INNER JOIN `categories_products` ON products.id = categories_products.product_id
WHERE
  (
    categories.lft BETWEEN parent.lft
    AND parent.rgt
  )
AND (
  categories.id = categories_products.category_id
)
AND (categories.store_id = 1)
GROUP BY
  `parent`.`id`,
  `parent`.`visible`
ORDER BY
  `parent`.`lft` ASC
LIMIT 100

and the result..

Result

…and if you want to wrap it nicely, in a tree control, it should look like this:

Tree View

So the conclusion (from the perspective of a .NET / C# developer and a PHP / Zend Ninja wannabe :) ? Doesn’t matter if you work with MySQL, SQL Server, PHP, Zend, .NET C#, you can always find a way to get the best of it.