Server-Side HTML Handling Using phpQuery

In our day to day tasks of web development it is necessary for us to work with both client and server-side code. We write the business logic using PHP and generate the HTML to be displayed in the users’ browsers. Then we use frameworks such as jQuery or Prototype to provide client-side interactivity.

Now think about how you can change and manipulate the generated HTML using server-side PHP code. phpQuery is the ultimate solution that will come to your mind. If you haven’t heard about phpQuery, you may be interested in this article since I am going to give you a brief introduction to phpQuery and explain how you can use it in real-world projects.

What is phpQuery

phpQuery is a server-side, chainable, CSS3 selector driven Document Object Model (DOM) API based on jQuery JavaScript Library.

This is the definition given on the official phpQuery project page. If you have used jQuery, then you will have an idea of how it can simplify many tasks requiring DOM manipulation. phpQuery provides exactly the same functionalities to be used inside your server-side PHP code. You can say good bye to untidy HTML code generation using echo statements and similar methods.

You will have access to most of the functionality provided by jQuery in phpQuery, which can broadly be divided into the 4 tasks mentioned below:

  • Creating DOM elements
  • Selecting and Manipulating elements
  • Iterating through the DOM
  • Printing the output to the browser

You can execute the tasks using the features provided by phpQuery which is known as “ported jQuery sections.” Let’s see the features first:

  • Selectors – find elements based on given condition.
  • Attributes – work with attributes of DOM elements.
  • Traversing – travel through list of selected elements.
  • Manipulation – add and remove content on selected elements.
  • Ajax – create server side ajax requests.
  • Events – bind DOM events on selected elements.
  • Utilities – generic functions to support other features.

You can download the phpQuery library from the project page at code.google.com/p/phpquery. Copy the folder to your web server and you are ready to go. Installation is simple as that, and you can execute the demo.php file to get started.

How to Use phpQuery

I’m going to show you how to create a two-column unordered list with headers and different row colors for odd and even rows, as shown in the image below:

First, let’s create an HTML document using phpQuery:

<?php
require("phpQuery/phpQuery.php");
$doc = phpQuery::newDocument("<div/>");

The above code will create a basic HTML document with a div tag. The library provides various methods of creating documents; I have used the simplest one, but you can find others in demo.php and the documentation.

Now we need to create an unordered list and add it to our HTML document.

<?php
...
$doc["div"]->append("<ul><li>Product Name</li><li>Price</li></ul>");
$products = array(
    array("Product 1","$30"),
    array("Product 2","$50"),
    array("Product 3","$20"));

foreach($products as $key=>$product) {
    $doc["div ul"]->append("<li>$product[0]</li><li>$product[1]</li>");
}
print $doc;

You can see that we have the unordered list now. But all the elements are in a single column, which is the default. We have to move the even elements of the list into a second column.

<?php
...
$doc["div ul"]->attr("style", "width:420px;");
$doc["div ul"]->find("li:even")->attr("style","width:200px; float:left; padding:5px; list-style:none;");
$doc["div ul"]->find("li:odd")->attr("style","width:200px; float:left; padding:5px; list-style:none;");

I’m using the style attribute to define the CSS styles required for our example here, but it’s not recommended to use inline styles unless it’s really needed. Always use CSS classes to add styles.

Now, let’s highlight the header and even numbered rows using phpQuery methods.

<?php
...
$doc["div ul"]->find("li:nth-child(4n)")->attr("style","background:#EEE; width:200px; float:left; padding:5px; list-style:none;");
$doc["div ul"]->find("li:nth-child(4n-1)")->attr("style","background:#EEE; width:200px; float:left; padding:5px; list-style:none;");
$doc["div ul"]->find("li:lt(1)")->attr("style","background:#CFCFCF; width:200px; float:left; padding:5px; list-style:none;");

We have completed our simple example, and you should now have an idea of how phpQuery can be used to simplify HTML generation server-side. Everything we did is almost the same as would be done with jQuery, except we did all the actions against the $doc object.

Importance of phpQuery

Even though I explained the functionality of phpQuery, you must be wondering why we need the library when we have jQuery on the client-side. I’ll show the importance of phpQuery using a practical scenario.

Consider the following situation: assume we have a table like the following, which has all the information about web developers who went to an interview.

Now here’s the list of requirements we have to develop in this scenario:

  • Applicants who got a mark over 60 for the exam should be highlighted in blue.
  • Applicants with more than 3 years working experience should have a link in front labeled “Apply for Senior Software Engineer” and other applicants should have the link “Apply for Software Engineer”.
  • The company has a salary structure based on experience:
    • 1 year – $5,000
    • 2 years – $10,000
    • 3 years – $20,000
    • more than 3 years – $50,000

    The salary column should be highlighted in green for applicants who match the criteria.

This is how the output should look:

A developer might provide the following solution to meet the requirements using pure PHP code.

<?php
// retrieve applicants from database
//...

echo <<<ENDHTML
<div id="main">
 <div class="row_head">
  <div>Name</div>
  <div>Marks</div>
  <div>Experience</div>
  <div>Position</div>
  <div>Expected Salary</div>
 </div>
ENDHTML;
foreach ($applicants as $applicant) {
    echo '<div class="row">';
    echo "<div>" . $applicant["name"] . "</div>";
    echo '<div class="' . marksClass($applicant["marks"]) . '">' . $applicant["marks"] . "</div>";
    echo "<div>" . $applicant["experience"] . "</div>";
    echo "<div>" . positionLink($applicant["experience"]) . "</div>";
    echo '<div class="' . salaryClass($applicant["experience"], $applicant["salary"]) . '">' . $applicant["salary"] . "</div>";
    echo "</div>";
}
echo "</div>";

function marksClass($info) {
    return ($info > 60) ? "pass" : "fail";
}

function positionLink($experience) {
    return ($experience > 3)
        ? '<a href="#">Apply for Senior Software Engineer</a>'
        : '<a href="#">Apply for Software Engineer</a>';
}

function salaryClass($experience, $salary) {
    switch ($experience) {
        case 1:
            return ($salary < 5000) ? "accept" : "reject";
        case 2:
            return ($salary < 10000) ? "accept" : "reject";
        case 3:
            return ($salary < 20000) ? "accept" : "reject";
        default:
            return ($salary < 50000) ? "accept" : "reject";
    }
}

Now let’s do it using phpQuery and compare the code and advantages.

<?php
require("phpQuery/phpQuery.php");
$doc = phpQuery::newDocument('<div id="main"></div>');
phpQuery::selectDocument($doc);

// retrieve applicants from database
//...

$doc["#main"]->append('
<div id="main">
 <div class="row_head">
  <div>Name</div>
  <div>Marks</div>
  <div>Experience</div>
  <div>Position</div>
  <div>Expected Salary</div>
 </div>');

foreach ($applicants as $key => $applicant) {
    $doc["#main"]->append('<div class="row" id="app_' . $key . '"></div>');
    foreach ($applicant as $field => $info) {
        $doc["#main"]->find("#app_" . $key)->append('<div class="_' . $field . '">' . $info . "</div>");
        if ($field == "experience") {
            $doc["#main"]->find("#app_" . $key)->append('<div style="width:400px" class="_position">-</div>');
        }
    }
}

addMarksClass($doc);
addSalaryClass($doc);
addPositionLink($doc);

print $doc;

function addMarksClass(&$doc) {
    $marks = pq("._marks");
    foreach ($marks as $appMark) {
        if (pq($appMark)->html() > 60) {
            pq($appMark)->addClass("pass");
        }
        else {
            pq($appMark)->addClass("fail");
        }
    }
}

function addSalaryClass(&$doc) {
    $marks = pq("._salary");
    foreach ($marks as $appMark) {
        $experience = pq($appMark)->parent()->find("._experience" )->html();
        $salary = pq($appMark)->html();

        switch ($experience) {
            case 1:
                pq($appMark)->addClass(
                    ($salary < 5000) ? "accept" : "reject"
                );
                break;
            case 2:
                pq($appMark)->addClass(
                    ($salary < 10000) ? "accept" : "reject"
                );
                break;
            case 3:
                pq($appMark)->addClass(
                    ($salary < 20000) ? "accept" : "reject"
                );
                break;
            default:
                pq($appMark)->addClass(
                    ($salary < 50000) ? "accept" : "reject"
                );
        }
    }
}

function addPositionLink(&$doc) {
    $experience = pq("._experience");
    foreach ($experiece as $appExp) {
        if (pq($appExp)->html() > 3) {
            pq($appExp)->parent()->find("._position")->html('<a href="#">Apply for Senior Software Engineer</a>');
        }
        else{
            pq($appExp)->parent()->find("._position")->html('<a href="#">Apply for Software Engineer</a>');
        }
    }
}

phpQuery is easy if you have the knowledge of working with jQuery already. Most of the above code will be self-explanatory. I want to mention though that pq() refers to the current document. All the others are jQuery functions.

And even though both look similar, the code which uses phpQuery provides better quality and extendibility. Think how brittle the original code can be if you have to add extra functionality later. Let’s assume we want to add additional validation on marks based on the working experience. In that scenario you’d have to add another method and assign the returned result inside the foreach loop. That means you have to change already written code, violating the Open-Closed Principle:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

With the second example which uses phpQuery, the code is first generated without any validation, and then we pass the table into each function and the changes are placed into the original table. Each function does not affect the other functions, so we can write a new function for any new requirements and use it outside the loop with the other functions. We don’t modify already existing code, which sounds good, right? This is called decoration:

Decorator pattern is a design pattern that allows behavior to be added to an existing object dynamically.

Summary

We started this tutorial by introducing phpQuery features and its importance. After learning how to use phpQuery using a simple example, we moved to practical example where it became much more important in improving the quality of code. phpQuery has provided us with a new perspective to working with HTML in server side, and I hope you will use phpQuery in different ways and share your personal experiences in the comments below.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Patrick

    There’s this great HTML tag you may want to look into – it’s called , and it’s used for displaying tabular data. Unordered lists? Nested divs? Is this really how you would go about creating a table?

    This article isn’t bad for showcasing the features of phpQuery, but the author evidently needs to spend some time learning the fundamentals of HTML before he tries to instruct others. The code shown here is just horrible.

    • Patrick

      The tag I was referring to there was the table tag. Apparently phpmaster completely removes anything that looks like HTML from comments. You’d think they’d just run it through htmlspecialchars because people might reasonably want to show example HTML in their comments (after all, the purpose of this site is to discuss web development), but I guess these masters of code couldn’t come up with a sensible solution like that.

    • Chris Emerson

      Agree, terrible case of div-itis…

    • Dave

      I’ve been bombarded by articles for the past 5 years proclaiming how nasty and outdated the TABLE tag is… perhaps that’s why the DIVs and ULs take its place in this example (and several hundred others I’ve seen). The point is to create rich HTML that’s cross-browser compatible. Go ahead and throw some tables on a page for FF/Chrome/Safari users, style it up pretty and point that page at IE6/IE7. Then you’ll know why pure CSS-driven DIV & UL tables are indispensable. As for the overall usefulness of phpQuery, eh. It seems like a poor substitute for standard scripting just for the sake of making it look like jQuery. Thanks but no.

      • Nick Shaw

        I think Patrick is right in this case. Its just wrong and semantically incorrect to use divs to display tabular data. Tables ARE cross browser compatible. Using divs doesn’t make the markup ‘richer’ if anything it makes it poorer as the data loses its meaning.

      • Steve

        Tables for tabular data, not for page structure. It’s a simple rule that people over-complicate. The cross browser issues you mention mostly don’t exist anymore and those that do are promptly solved with a css-reset. (Which you should be using anyways.)

  • paul

    a follow up would be awesome cause im still struggling to see why i would use this, but am interested…

    • http://richardathome.com Richard@Home

      I think the value to this isn’t for DOM creation, but for DOM parsing. Think screen scrapers…

  • Sandor

    I can’t see what’s the point. I thought someone write a quick simplehtml parser… I think, to use style instead of class just not effective in long run. More examples could help.

  • Eric

    I just can’t imagine the processing overhead caused by this kind of practice in a production application…

  • Adam

    It is an interesting exercise, but I fail to see why you’d want to do this. It seems to fly in the face of the idea of not embedding html inside php constructs.

    • http://www.innovativephp.com Rakhitha Nimesh

      Hi Adam

      Thanks for the comment.

      What is your preferred method of adding dynamic behaviour to HTML ? Please share your thoughts as we all want to learn something new.

  • http://okeowoaderemi.com Okeowo Omololu Remi

    For the life of me, the thoughts of Mixing PHP and HTML like that scares the living day lights out of me, but that’s my own opinion.

    • http://www.innovativephp.com Rakhitha Nimesh

      Hi Okeowo
      Thanks for the comment.
      I agree with you regarding mixing HTML and PHP. I always use templating framework to separate the HTML content. I have used HTML here just to make the tutorial simple.

      Purpose of this article is to show how to add dynamic behaviours to HTML. Even when you use a templating framework this concept would work.

      Please share your thoughts if you have better method of adding dynamic behaviour.

    • Les

      I would agree with you entirely, after seeing the first example of the unordered list the first thought was “you are mixing presentation with business logic”.

      That IS digusting and goes against EVERYTHING I’ve learnt over the last 5 years about software engineering, separation of concerns and layering.

      Simple examples or not, this library is bad, bad news and I would suggest those with a sense of duty to their clients to stay away from this.

      Do you smell something bad? I do :(

  • Alex Gervasio

    The mingling between presentation and business logic is so overwhelming glaring here that it pretty much resembles the tons of sloppy transaction scripts we used to code in the late 90′s. While programmatic markup generation might drag in some benefits in a few specific use cases, in this one in particular it doesn’t make any sense at all. Just my 2 cents.

  • Alex Stoia

    Firstly thanks for the tutorial, this is the first time I see this library. I can see the elegance of phpQuery. But, comparing the two php examples..I have to say that phpQuery is more complex, takes more space, probably is more “resource” expensive…
    For complex projects…I don’t think its efficient. But its damn elegant :D

    ps: I love jQuery, so it’s not an understanding problem.

    • http://www.innovativephp.com Rakhitha Nimesh

      Thanks for the comment.
      I understand that phpQuery might be resource expensive. My suggestion is to test performence properly before using in any projects.

  • http://fuzzy76.net/ Håvard Pedersen

    First of all, I really don’t understand everybody claiming this is a mixup of business logic and presentation. The logic that happens here is presentational logic, which DOES belong (no matter which templating system you use) in the presentational layer. Business logic is what created the initial array to begin with, and that is handled elsewhere, as it should.

    On the other hand, I will not use a framework that makes me write 80% more code than I do when I don’t use it. That is the real dealbreaker for me.

    • http://www.innovativephp.com Rakhitha Nimesh

      Thanks for the comment.
      I might agree with you depending on the context it is used. Sometimes it will need more codes.

  • http://www.domtemplate.com/ MIchael Mifsud

    Good tutorial Rakhitha, I think you are on the right track however as mentioned in a previous comment, the no lookup overhead could get quite expensive over an entire application. I have developed a DOM templating system that overcomes the node lookup blowout and have been using it in productions site for the past 10 years and has served me well. You can get it at http://www.domtemplate.com/. We will be releasing a new version in the comming months that renames a few function calls and brings them into line with the php DOM lib. Feel free to contact for a pre-release copy.

    • http://www.innovativephp.com Rakhitha Nimesh

      Thanks for the comment.
      I understand and performence needs to be tested properly before using in any projects.

  • http://inventikasolutions.com Pritesh

    While the author may have made the mistake of combining business logic and presentation, let’s consider the example just as a demo for phpQuery. I think its cool to have a library like phpQuery, might come in handy some day.
    There is another library ‘simple dom’ it is amazing, it lets you access the dom elements in php – http://web-developer-thing.blogspot.in/2010/02/php-simple-html-dom-parser-makes.html

  • James Ong

    Found how elegant phpQuery could do, I would still say XSLT + XML are concise and cleaner since it is entirely done with the familiar HTML-syntax and separate data from business logic.

  • http://www.karsites.netphp-debuggers.org.uk Keith Roberts

    I’d prefer to see the examples written using smarty template engine, instead of this mish-mash of code!
    http://www.smarty.net/forums/

  • http://techishard.wordpress.com/ Grant

    This is a terrible idea.

  • threedot

    ugly

  • http://careers.stackoverflow.com/frostymarvelous Stefan Froelich

    I think the reason this looks like a bad idea is because the author tried to use this entirely for something it isn’t meant for.
    Sure, jQuery allows us to create elements, but how often do we do that? The beauty, elegance and simplicity comes from css style selectors. That’s what makes jQuery jQuery. And that is what PHPQuery tries to replicate. The ability to find elements in a document using a jQuery-like API.
    As Richard@Home mentioned, the benefits of the library are easily seen when parsing or scraping a document. Sure, it allows you to easily modify portions of it as well.

    Using the library for DOM creation is just plain crappy. The performance hit alone is obvious without any benchmarks. As I always say, PHP is a templating language. Why slow down your code anymore than necessary.

    A follow up article showcasing the library’s DOM manipulation abilities is a MUST. Else, I fear many would never get to enjoy this great framework.

  • Roman

    hi, I’m from Cancún Mexico, sorry for may bad English. hello sounds interesting.
    I’m a beginner always learning.
    I use php, jquery and json. php for business, then I sent the data in json format through ajax.jquey and use jQuery (for each) to generated html.
    so I have html, php and jquery without any kind of mix, what do you think of this

  • http://sultanshakir.com Sultan

    Pointless. Even the last example requires almost twice as much code. The three layers of web are content, style, and behavior. K.I.S.S. and only mix when necessary.

  • http://www.kafcomng.com Agbaje Olalekan

    It would be pointless to restate all that has already been stated.

    However what I can see here is borne out of lack of experience. The quick solution to the above problem(s) is to introduce the writer to mature PHP frameworks.

    If you have used even the most basic of php frameworks (cakePHP – the best of them all :) – smiling, Yii, Zend, Symfony even codeigniter) you will realize that the absolute separation of presentation and business logic is a must-have.

    Dear Rakhitha Nimesh take time out and look into a php framework. I promise you, your life will never remain the same.

  • http://floppydeuce.com Chris

    The point of this article is to show how the library can be used. Sure, the author doesn’t mention that developers should evaluate when this library is appropriate, but as developers, we should know that, yes?

    Anyway, I wish there were more comments/descriptions in/about the code. In one of the first code snippets I noticed the CSS property “list-style” being specified without the hyphen. After seeing it several times I thought maybe hyphens might mess up the parsing. Then a snippet specified “nthchild” followed soon after by “nth-child.” Were the hyphens for these properties left out on accident? Also, what is the purpose of the line: “phpQuery::selectDocument($doc);”?

    • http://www.innovativephp.com Rakhitha Nimesh

      Hi Chris

      1. Hyphens for list-style and nth-child should be there and it looks like its missed somewhere. Thanks for pointing out.

      2. pq() function works on the selected document. If there were more than 1 document we need phpQuery::selectDocument($doc); to specify the document which pq() function will be applied.

      • http://zaemis.blogspot.com Timothy Boronczyk

        I’ve added the hyphens in where they were missing from list-style and nth-child. Thanks for the heads up!

        • http://www.innovativephp.com Rakhitha Nimesh

          Thank you for making the corrections in the code.

  • Brian

    Please make an email newsletter!