public
Last active

Quick doesn't have to mean dirty: Also applies to PHP!

  • Download Gist
guestbook.markdown
Markdown

Quick doesn't have to mean dirty: Also applies to PHP!

This is just a quick response to http://me.veekun.com/blog/2012/07/28/quick-doesnt-mean-dirty/. I won't bother to write a proper blog post for this, so a Gist will have to do ;)

When I read that article, one thing really striked me: If you want to quickly create a web app in PHP, you do exactly the same. I mean, exactly.

I never used the Silex microframework before, so I took this as a chance to see how it works. I'll just do the same as eevee did, only with a bit less commentary (this is a Gist after all!)

I hope that this will show you that PHP and Python are really similar to work with. Also this should show that just because you're using PHP, doesn't mean that you write dirty code. The similarity in the process and code is really incredible :)

Creating a guestbook using Silex

First create the project:

curl -s https://getcomposer.org/installer | php
php composer.phar create-project fabpot/silex-skeleton guestbook
cd guestbook/

Run php -S localhost:8888 -t web/ to start the webserver. You'll find a friendly Hello World message if you visit localhost:8888/index_dev.php in your browser.

Then create a git repo:

rm -rf .git
git init
git add -A
git commit -m "Initial commit"

There are already two templates in the templates/ folder, we just modify them a bit to match eevee's code:

vim templates/layout.html
<!DOCTYPE html>
<html>
    <head>
        <title>{% block title '' %} - My Awesome Site</title>

        <link href="{{ app.request.basepath }}/css/main.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <section id="content">
            {% block content %}{% endblock %}
        </section>
        <footer id="footer">
            My awesome guestbook
        </footer>
    </body>
</html>
vim index.html
{% extends "layout.html" %}

{% block title %}Guestbook{% endblock %}

{% block content %}
    <h1>Guestbook</h1>

    <p>Welcome to my guestbook!</p>

    <ul class="guests">
        <li>...</li>
    </ul>
{% endblock %}

That doesn't give us much yet, we need to create some kind of database. To save me the installation of mysql I'll use sqlite:

vim src/app.php (addition)
<?php
$app['db'] = $app->share(function() {
    $pdo = new PDO('sqlite:' . __DIR__ . '/../db.sq3');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    return $pdo;
});

Also create a console task to create the table structure (there is already a skeleton in the file, so I don't even have to look at docs):

vim src/console.php (change)
<?php
$console
    ->register('create-db')
    ->setDescription('Create database tables')
    ->setCode(function (InputInterface $input, OutputInterface $output) use ($app) {
        $app['db']->query(
            "CREATE TABLE guestbook (
                 id INTEGER PRIMARY KEY AUTOINCREMENT,
                 timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                 name TEXT, message TEXT 
             );
             CREATE INDEX timestamp_index ON guestbook (timestamp);"
        );

        $output->writeln('Created database!');
    })
;

Now we can create the DB using ./console create-db.

Oh, and yeah, I hate ORMs, I prefer raw SQL.

Next let's modify the template to actually display something:

vim templates/index.html (change)
<ul class="guests">
    {% for entry in entries %}
        <li>
            <blockquote>{{ entry.message }}</blockquote>
            <p><cite>{{ entry.name }}</cite>, <time>{{ entry.timestamp }}</time></p>
        </li>
    {% endfor %}
</ul>

And adjust the controller for this:

vim src/app.php
<?php
$app->get('/', function () use ($app) {
    return $app['twig']->render('index.html', array(
        'entries' => $app['db']->query(
            'SELECT * FROM guestbook ORDER BY timestamp DESC'
        )
    ));
})

Finally, let's add the code for adding new guestbook entries:

vim templates/index.html
<hr>

<form action="" method="POST">
    <p>Name: <input type="text" name="name"></p>
    <p>Message: <textarea name="message" rows="10" cols="40"></textarea></p>
    <p><button>Sign</button></p>
</form>
vim src/controllers.php
<?php
$app->post('/', function (Request $req) use ($app) {
    $app['db']->prepare(
        'INSERT INTO guestbook (name, message) VALUES (?, ?)'
    )->execute(array(
        $req->request->get('name')    ?: 'Some dummy who forgot to leave a name',
        $req->request->get('message') ?: 'WOW THIS IS THE BEST WEBSITE EVER',
    ));

    return $app->redirect('/');
});

Again, you can see the page using the php -S localhost:8888 -t web/ webserver. It is safe against XSS and SQLi, just like the Python code (the template engine does automatic escaping and we used prepared statements).

I won't bother with deployment here. You could obviously just deploy to Heroku, exactly as you did with Python. Or just upload it to some freespace via FTP. Whatever.

Thanks for listening,
~nikic (http://nikic.github.com/, https://twitter.com/nikita_ppv)

And all these dependencies for a guestbook?


  • Installing silex/silex (dev-master) Cloning 337af0c031195d58a5502358ab78c4f30caa7fd0
  • Installing monolog/monolog (1.1.0) Downloading: 100% - Installing symfony/monolog-bridge (dev-master)
    Cloning v2.1.0-BETA4

  • Installing symfony/dom-crawler (dev-master)
    Cloning v2.1.0-BETA4

  • Installing symfony/browser-kit (dev-master)
    Cloning v2.1.0-BETA4

  • Installing symfony/class-loader (dev-master)
    Cloning v2.1.0-BETA4

  • Installing symfony/config (dev-master)
    Cloning cb15ab7da9a69b123433c4edca68e23564345a31

  • Installing symfony/console (dev-master)
    Cloning 7c328a9752ac035fe38dfae5425cf66b905c5394

  • Installing symfony/css-selector (dev-master)
    Cloning v2.1.0-BETA4

  • Installing symfony/finder (dev-master)
    Cloning 7fe9695cb1dae09df7f545aabed8fa4b0026108b

  • Installing symfony/options-resolver (dev-master)
    Cloning d839487dafd0e69b3e67b5819caa11a9bb85f215

  • Installing symfony/locale (dev-master)
    Cloning d6f016c243105b365a80da60b049ea884b002ecd

  • Installing symfony/form (dev-master)
    Cloning 1f83baa5096b87e85149a933df46881cce04787c

  • Installing symfony/process (dev-master)
    Cloning 7278cfeeb168654bcd148127796cfcd504a30dbe

  • Installing symfony/security (dev-master)
    Cloning 554ad6bb1448fdd54274a061d6a9ee8951284078

  • Installing symfony/translation (dev-master)
    Cloning 3ee2b629bc1fcac1d90bf76c251b31ee6acd4704

  • Installing symfony/twig-bridge (dev-master)
    Cloning 1e9873087f35a730d8d8d01050efbde724e76815

php -S localhost:8888 -t web/ doesn't work for me.

php -S localhost:8888 -t web/ is a PHP 5.4 feature. Which PHP version are you using?

Btw, yes, there are many deps because I used a skeleton to create the project and the skeleton is designed to be used for a bit more than just a guestbook ;)

You can easily eliminate almost all dependencies in this skeleton, it's just a sample. You only need silex (which is obvious) and twig (for templating). Just edit composer.json to your taste.

People tend to criticize PHP, but the last time they ever wrote PHP code was back in 2002. Or never cared about learning about clean, modern and elegant PHP programming. Take some time, and please open your mind, to learn more about projects like Symfony, Behat, Lithium and even ZF2. Also: http://fabien.potencier.org/article/64/php-is-much-better-than-you-think

My response to anyone criticizing PHP is simple. Give me a CMF (Content Management Framework) that is remotely close to what Drupal provides... and then we can talk :-)

@nikic I am using php 5.3, this should be modern enough. Btw, if I install 5.4, how easy will it be for me to switch between versions, for various projects?

@klaussilveira I have used ZF, Symfony, and other PHP frameworks. Most of them lift concepts from other languages/frameworks, so they don't fit perfectly. There is simply no "unity of design" in the language, you have to do a lot of context switching writing in one language. Its a productivity killer, well for people who look for fun in what they do.

@marioawad, a programming language is not a CMF, and a CMF is not the only thing you do with programming languages. I'd achieve anything you would do in Drupal with Django and in a lot lesser lines of code, thus making it more readable and maintainable.

@damilare definitely a programming language is not a CMF but the availability of a well-designed and popular CMF built on top of that particular language is a huge advantage. As least for me and my company. I'm afraid I'll have to disagree that with Django you can achieve anything I'd build with Drupal with less lines of Code. If we're building a website with different content types (services, sub-services, clients using those services, projects stating which services they were built upon...) then Drupal is the tool of choice, and you can't beat it for this particular use case. If we're building an online accounting system, then definitely Django is better suited, and for us PHP developers, then maybe we'd look at Zend Framework, Symfony, Code Igniter, CakePHP, ...

@marioawad

You'd still do it with a lot less code. Django has a concept of Content Type which is more abstract and powerful than what Drupal offers. https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/

@damilare

PHP 5.4 is required for the built-in webserver, since it was added in that version. The feature doesn't exist in 5.3.

You should be able to install 5.4 as a separate executable, and run each version from command-line by calling php5-3 or php5-4 respectively. If you're referring to a module installation then obviously it's going to be more complex, and goes outside the point of this Gist.

@bitdagger

Good to know php now has an inbuilt web server, out of curiosity, is this a production ready feature?

Update: http://php.net/manual/en/features.commandline.webserver.php

@damilare

5.4.5 is the current stable build iirc. I haven't heard of any major issues with it, so I don't really see any reason not to use it in a production environment. My day-to-day experience is with 5.2 ( sysadmins refuse to update to the latest versions due to legacy support, etc. for forever, so we're always several versions behind ) so I comment from personal experience.

EDIT: Inbuilt server docs: http://us.php.net/manual/en/features.commandline.webserver.php
"This web server is designed for developmental purposes only, and should not be used in production."

There's your answer I suppose.

@bitdagger

I am with your sysadmin on this one :)

@damilare having content types is one of the needed features to build such websites. I'm curious about Django CMS (cause it seams more appropriate to compare Drupal with Django CMS rather than plain Django), are you using Django CMS? or do you prefer to deliver website solutions on Django only? thank you for your input.

@marioawad

I would compare Wordpress to Django CMS and sort of compare Drupal with Django in a poor man's sense. Reason being that, Django ships with an Admin interface you can give your clients, while you still have the power and flexibility of Django modules to change the underlying business logic. With Django CMS, the conventions quite stricter.

I'd say spend some time with Django and play around, its easier to churn out all sorts of applications from websites to "enterprise" systems with little efforts and the power of a very good programming language.

@damilare thanks again for your input. I'd love to see what can be accomplished and have a sense of the available dev tools so that I compare with what I already have. Any recommendations?

@marioaward

https://www.djangoproject.com/ is very resourceful. You can also ask questions on #django @ freenode

@damilare thanks a lot. Do take a look at Drupal 8 and Symfony 2 framework whenever you have time too. Cheers.

I think that the point of the article was "Yeah, you like PHP because it's quick to set up? Well, Python can be as quick without being as dirty as PHP". And now, this rebuttal is... "But look, I can be as quick as your Python example with PHP!"? Huh? Didn't we lose something along the way here?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.